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