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 """Stop an instance.
170
171 For the fake hypervisor, this just removes the file in the base
172 dir, if it exist, otherwise we raise an exception.
173
174 """
175 if name is None:
176 name = instance.name
177 if not self._IsAlive(name):
178 raise errors.HypervisorError("Failed to stop instance %s: %s" %
179 (name, "not running"))
180 self._MarkDown(name)
181
183 """Reboot an instance.
184
185 For the fake hypervisor, this does nothing.
186
187 """
188 return
189
191 """Balloon an instance memory to a certain value.
192
193 @type instance: L{objects.Instance}
194 @param instance: instance to be accepted
195 @type mem: int
196 @param mem: actual memory size to use for instance runtime
197
198 """
199 if not self._IsAlive(instance.name):
200 raise errors.HypervisorError("Failed to balloon memory for %s: %s" %
201 (instance.name, "not running"))
202 try:
203 self._MarkUp(instance, mem)
204 except EnvironmentError, err:
205 raise errors.HypervisorError("Failed to balloon memory for %s: %s" %
206 (instance.name, utils.ErrnoOrStr(err)))
207
209 """Return information about the node.
210
211 This is just a wrapper over the base GetLinuxNodeInfo method.
212
213 @return: a dict with the following keys (values in MiB):
214 - memory_total: the total memory size on the node
215 - memory_free: the available memory on the node for instances
216 - memory_dom0: the memory used by the node itself, if available
217
218 """
219 result = self.GetLinuxNodeInfo()
220
221 all_instances = self.GetAllInstancesInfo()
222 result["memory_free"] -= min(result["memory_free"],
223 sum([row[2] for row in all_instances]))
224 return result
225
226 @classmethod
235
237 """Verify the hypervisor.
238
239 For the fake hypervisor, it just checks the existence of the base
240 dir.
241
242 @return: Problem description if something is wrong, C{None} otherwise
243
244 """
245 if os.path.exists(self._ROOT_DIR):
246 return None
247 else:
248 return "The required directory '%s' does not exist" % self._ROOT_DIR
249
250 @classmethod
252 """Fake hypervisor powercycle, just a wrapper over Linux powercycle.
253
254 """
255 cls.LinuxPowercycle()
256
258 """Prepare to accept an instance.
259
260 @type instance: L{objects.Instance}
261 @param instance: instance to be accepted
262 @type info: string
263 @param info: instance info, not used
264 @type target: string
265 @param target: target host (usually ip), on this node
266
267 """
268 if self._IsAlive(instance.name):
269 raise errors.HypervisorError("Can't accept instance, already running")
270
272 """Migrate an instance.
273
274 @type instance: L{objects.Instance}
275 @param instance: the instance to be migrated
276 @type target: string
277 @param target: hostname (usually ip) of the target node
278 @type live: boolean
279 @param live: whether to do a live or non-live migration
280
281 """
282 logging.debug("Fake hypervisor migrating %s to %s (live=%s)",
283 instance, target, live)
284
286 """Finalize the instance migration on the target node.
287
288 For the fake hv, this just marks the instance up.
289
290 @type instance: L{objects.Instance}
291 @param instance: instance whose migration is being finalized
292 @type info: string/data (opaque)
293 @param info: migration information, from the source node
294 @type success: boolean
295 @param success: whether the migration was a success or a failure
296
297 """
298 if success:
299 self._MarkUp(instance, self._InstanceStartupMemory(instance))
300 else:
301
302 self._MarkDown(instance.name)
303
304 - def PostMigrationCleanup(self, instance):
305 """Clean-up after a migration.
306
307 To be executed on the source node.
308
309 @type instance: L{objects.Instance}
310 @param instance: the instance that was migrated
311
312 """
313 pass
314
316 """Finalize the instance migration on the source node.
317
318 @type instance: L{objects.Instance}
319 @param instance: the instance that was migrated
320 @type success: bool
321 @param success: whether the migration succeeded or not
322 @type live: bool
323 @param live: whether the user requested a live migration or not
324
325 """
326
327 if success:
328 self._MarkDown(instance.name)
329
331 """Get the migration status
332
333 The fake hypervisor migration always succeeds.
334
335 @type instance: L{objects.Instance}
336 @param instance: the instance that is being migrated
337 @rtype: L{objects.MigrationStatus}
338 @return: the status of the current migration (one of
339 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
340 progress info that can be retrieved from the hypervisor
341
342 """
343 return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
344