1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """Fake hypervisor
23
24 """
25
26 import os
27 import os.path
28 import logging
29
30 from ganeti import utils
31 from ganeti import constants
32 from ganeti import errors
33 from ganeti import objects
34 from ganeti import pathutils
35 from ganeti.hypervisor import hv_base
39 """Fake hypervisor interface.
40
41 This can be used for testing the ganeti code without having to have
42 a real virtualisation software installed.
43
44 """
45 CAN_MIGRATE = True
46
47 _ROOT_DIR = pathutils.RUN_DIR + "/fake-hypervisor"
48
52
54 """Get the list of running instances.
55
56 """
57 return os.listdir(self._ROOT_DIR)
58
60 """Get instance properties.
61
62 @param instance_name: the instance name
63
64 @return: tuple of (name, id, memory, vcpus, stat, times)
65
66 """
67 file_name = self._InstanceFile(instance_name)
68 if not os.path.exists(file_name):
69 return None
70 try:
71 fh = open(file_name, "r")
72 try:
73 inst_id = fh.readline().strip()
74 memory = utils.TryConvert(int, fh.readline().strip())
75 vcpus = utils.TryConvert(int, fh.readline().strip())
76 stat = "---b-"
77 times = "0"
78 return (instance_name, inst_id, memory, vcpus, stat, times)
79 finally:
80 fh.close()
81 except IOError, err:
82 raise errors.HypervisorError("Failed to list instance %s: %s" %
83 (instance_name, err))
84
86 """Get properties of all instances.
87
88 @return: list of tuples (name, id, memory, vcpus, stat, times)
89
90 """
91 data = []
92 for file_name in os.listdir(self._ROOT_DIR):
93 try:
94 fh = open(utils.PathJoin(self._ROOT_DIR, file_name), "r")
95 inst_id = "-1"
96 memory = 0
97 vcpus = 1
98 stat = "-----"
99 times = "-1"
100 try:
101 inst_id = fh.readline().strip()
102 memory = utils.TryConvert(int, fh.readline().strip())
103 vcpus = utils.TryConvert(int, fh.readline().strip())
104 stat = "---b-"
105 times = "0"
106 finally:
107 fh.close()
108 data.append((file_name, inst_id, memory, vcpus, stat, times))
109 except IOError, err:
110 raise errors.HypervisorError("Failed to list instances: %s" % err)
111 return data
112
113 @classmethod
115 """Compute the instance file for an instance name.
116
117 """
118 return utils.PathJoin(cls._ROOT_DIR, instance_name)
119
121 """Checks if an instance is alive.
122
123 """
124 file_name = self._InstanceFile(instance_name)
125 return os.path.exists(file_name)
126
127 - def _MarkUp(self, instance, memory):
128 """Mark the instance as running.
129
130 This does no checks, which should be done by its callers.
131
132 """
133 file_name = self._InstanceFile(instance.name)
134 fh = file(file_name, "w")
135 try:
136 fh.write("0\n%d\n%d\n" %
137 (memory,
138 instance.beparams[constants.BE_VCPUS]))
139 finally:
140 fh.close()
141
143 """Mark the instance as running.
144
145 This does no checks, which should be done by its callers.
146
147 """
148 file_name = self._InstanceFile(instance_name)
149 utils.RemoveFile(file_name)
150
151 - def StartInstance(self, instance, block_devices, startup_paused):
167
168 - def StopInstance(self, instance, force=False, retry=False, name=None,
169 timeout=None):
170 """Stop an instance.
171
172 For the fake hypervisor, this just removes the file in the base
173 dir, if it exist, otherwise we raise an exception.
174
175 """
176 assert(timeout is None or force is not None)
177
178 if name is None:
179 name = instance.name
180 if not self._IsAlive(name):
181 raise errors.HypervisorError("Failed to stop instance %s: %s" %
182 (name, "not running"))
183 self._MarkDown(name)
184
186 """Reboot an instance.
187
188 For the fake hypervisor, this does nothing.
189
190 """
191 return
192
194 """Balloon an instance memory to a certain value.
195
196 @type instance: L{objects.Instance}
197 @param instance: instance to be accepted
198 @type mem: int
199 @param mem: actual memory size to use for instance runtime
200
201 """
202 if not self._IsAlive(instance.name):
203 raise errors.HypervisorError("Failed to balloon memory for %s: %s" %
204 (instance.name, "not running"))
205 try:
206 self._MarkUp(instance, mem)
207 except EnvironmentError, err:
208 raise errors.HypervisorError("Failed to balloon memory for %s: %s" %
209 (instance.name, utils.ErrnoOrStr(err)))
210
212 """Return information about the node.
213
214 This is just a wrapper over the base GetLinuxNodeInfo method.
215
216 @return: a dict with the following keys (values in MiB):
217 - memory_total: the total memory size on the node
218 - memory_free: the available memory on the node for instances
219 - memory_dom0: the memory used by the node itself, if available
220
221 """
222 result = self.GetLinuxNodeInfo()
223
224 all_instances = self.GetAllInstancesInfo()
225 result["memory_free"] -= min(result["memory_free"],
226 sum([row[2] for row in all_instances]))
227 return result
228
229 @classmethod
238
240 """Verify the hypervisor.
241
242 For the fake hypervisor, it just checks the existence of the base
243 dir.
244
245 @return: Problem description if something is wrong, C{None} otherwise
246
247 """
248 if os.path.exists(self._ROOT_DIR):
249 return None
250 else:
251 return "The required directory '%s' does not exist" % self._ROOT_DIR
252
253 @classmethod
255 """Fake hypervisor powercycle, just a wrapper over Linux powercycle.
256
257 """
258 cls.LinuxPowercycle()
259
261 """Prepare to accept an instance.
262
263 @type instance: L{objects.Instance}
264 @param instance: instance to be accepted
265 @type info: string
266 @param info: instance info, not used
267 @type target: string
268 @param target: target host (usually ip), on this node
269
270 """
271 if self._IsAlive(instance.name):
272 raise errors.HypervisorError("Can't accept instance, already running")
273
275 """Migrate an instance.
276
277 @type instance: L{objects.Instance}
278 @param instance: the instance to be migrated
279 @type target: string
280 @param target: hostname (usually ip) of the target node
281 @type live: boolean
282 @param live: whether to do a live or non-live migration
283
284 """
285 logging.debug("Fake hypervisor migrating %s to %s (live=%s)",
286 instance, target, live)
287
289 """Finalize the instance migration on the target node.
290
291 For the fake hv, this just marks the instance up.
292
293 @type instance: L{objects.Instance}
294 @param instance: instance whose migration is being finalized
295 @type info: string/data (opaque)
296 @param info: migration information, from the source node
297 @type success: boolean
298 @param success: whether the migration was a success or a failure
299
300 """
301 if success:
302 self._MarkUp(instance, self._InstanceStartupMemory(instance))
303 else:
304
305 self._MarkDown(instance.name)
306
307 - def PostMigrationCleanup(self, instance):
308 """Clean-up after a migration.
309
310 To be executed on the source node.
311
312 @type instance: L{objects.Instance}
313 @param instance: the instance that was migrated
314
315 """
316 pass
317
319 """Finalize the instance migration on the source node.
320
321 @type instance: L{objects.Instance}
322 @param instance: the instance that was migrated
323 @type success: bool
324 @param success: whether the migration succeeded or not
325 @type live: bool
326 @param live: whether the user requested a live migration or not
327
328 """
329
330 if success:
331 self._MarkDown(instance.name)
332
334 """Get the migration status
335
336 The fake hypervisor migration always succeeds.
337
338 @type instance: L{objects.Instance}
339 @param instance: the instance that is being migrated
340 @rtype: L{objects.MigrationStatus}
341 @return: the status of the current migration (one of
342 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
343 progress info that can be retrieved from the hypervisor
344
345 """
346 return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
347