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 """Base class for all hypervisors
32
33 The syntax for the _CHECK variables and the contents of the PARAMETERS
34 dict is the same, see the docstring for L{BaseHypervisor.PARAMETERS}.
35
36 @var _FILE_CHECK: stub for file checks, without the required flag
37 @var _DIR_CHECK: stub for directory checks, without the required flag
38 @var REQ_FILE_CHECK: mandatory file parameter
39 @var OPT_FILE_CHECK: optional file parameter
40 @var REQ_DIR_CHECK: mandatory directory parametr
41 @var OPT_DIR_CHECK: optional directory parameter
42 @var NO_CHECK: parameter without any checks at all
43 @var REQUIRED_CHECK: parameter required to exist (and non-false), but
44 without other checks; beware that this can't be used for boolean
45 parameters, where you should use NO_CHECK or a custom checker
46
47 """
48
49 import os
50 import re
51 import logging
52
53
54 from ganeti import errors
55 from ganeti import utils
56 from ganeti import constants
71
86
87
88
89
90
91
92 _FILE_CHECK = (utils.IsNormAbsPath, "must be an absolute normalized path",
93 os.path.isfile, "not found or not a file")
94
95
96 _DIR_CHECK = (utils.IsNormAbsPath, "must be an absolute normalized path",
97 os.path.isdir, "not found or not a directory")
98
99
100
101 _CPU_MASK_CHECK = (_IsCpuMaskWellFormed,
102 "CPU mask definition is not well-formed",
103 None, None)
104
105
106 _MULTI_CPU_MASK_CHECK = (_IsMultiCpuMaskWellFormed,
107 "Multiple CPU mask definition is not well-formed",
108 None, None)
109
110
111 _NET_PORT_CHECK = (lambda x: 0 < x < 65535, "invalid port number",
112 None, None)
113
114
115 _NONNEGATIVE_INT_CHECK = (lambda x: x >= 0, "cannot be negative", None, None)
116
117
118 REQ_FILE_CHECK = (True, ) + _FILE_CHECK
119 OPT_FILE_CHECK = (False, ) + _FILE_CHECK
120 REQ_DIR_CHECK = (True, ) + _DIR_CHECK
121 OPT_DIR_CHECK = (False, ) + _DIR_CHECK
122 REQ_NET_PORT_CHECK = (True, ) + _NET_PORT_CHECK
123 OPT_NET_PORT_CHECK = (False, ) + _NET_PORT_CHECK
124 REQ_CPU_MASK_CHECK = (True, ) + _CPU_MASK_CHECK
125 OPT_CPU_MASK_CHECK = (False, ) + _CPU_MASK_CHECK
126 REQ_MULTI_CPU_MASK_CHECK = (True, ) + _MULTI_CPU_MASK_CHECK
127 OPT_MULTI_CPU_MASK_CHECK = (False, ) + _MULTI_CPU_MASK_CHECK
128 REQ_NONNEGATIVE_INT_CHECK = (True, ) + _NONNEGATIVE_INT_CHECK
129 OPT_NONNEGATIVE_INT_CHECK = (False, ) + _NONNEGATIVE_INT_CHECK
130
131
132 NO_CHECK = (False, None, None, None, None)
133
134
135 REQUIRED_CHECK = (True, None, None, None, None)
136
137
138 MIGRATION_MODE_CHECK = (True, lambda x: x in constants.HT_MIGRATION_MODES,
139 "invalid migration mode", None, None)
143 """Builds parameter checker for set membership.
144
145 @type required: boolean
146 @param required: whether this is a required parameter
147 @type my_set: tuple, list or set
148 @param my_set: allowed values set
149
150 """
151 fn = lambda x: x in my_set
152 err = ("The value must be one of: %s" % utils.CommaJoin(my_set))
153 return (required, fn, err, None, None)
154
157 RUNNING = 0
158 SHUTDOWN = 1
159
160 @staticmethod
163
164 @staticmethod
167
170 """Abstract virtualisation technology interface
171
172 The goal is that all aspects of the virtualisation technology are
173 abstracted away from the rest of code.
174
175 @cvar PARAMETERS: a dict of parameter name: check type; the check type is
176 a five-tuple containing:
177 - the required flag (boolean)
178 - a function to check for syntax, that will be used in
179 L{CheckParameterSyntax}, in the master daemon process
180 - an error message for the above function
181 - a function to check for parameter validity on the remote node,
182 in the L{ValidateParameters} function
183 - an error message for the above function
184 @type CAN_MIGRATE: boolean
185 @cvar CAN_MIGRATE: whether this hypervisor can do migration (either
186 live or non-live)
187
188 """
189 PARAMETERS = {}
190 ANCILLARY_FILES = []
191 ANCILLARY_FILES_OPT = []
192 CAN_MIGRATE = False
193
194 - def StartInstance(self, instance, block_devices, startup_paused):
195 """Start an instance."""
196 raise NotImplementedError
197
198 - def StopInstance(self, instance, force=False, retry=False, name=None,
199 timeout=None):
200 """Stop an instance
201
202 @type instance: L{objects.Instance}
203 @param instance: instance to stop
204 @type force: boolean
205 @param force: whether to do a "hard" stop (destroy)
206 @type retry: boolean
207 @param retry: whether this is just a retry call
208 @type name: string or None
209 @param name: if this parameter is passed, the the instance object
210 should not be used (will be passed as None), and the shutdown
211 must be done by name only
212 @type timeout: int or None
213 @param timeout: if the parameter is not None, a soft shutdown operation will
214 be killed after the specified number of seconds. A hard (forced)
215 shutdown cannot have a timeout
216 @raise errors.HypervisorError: when a parameter is not valid or
217 the instance failed to be stopped
218
219 """
220 raise NotImplementedError
221
223 """Cleanup after a stopped instance
224
225 This is an optional method, used by hypervisors that need to cleanup after
226 an instance has been stopped.
227
228 @type instance_name: string
229 @param instance_name: instance name to cleanup after
230
231 """
232 pass
233
235 """Reboot an instance."""
236 raise NotImplementedError
237
239 """Get the list of running instances."""
240 raise NotImplementedError
241
243 """Get instance properties.
244
245 @type instance_name: string
246 @param instance_name: the instance name
247 @type hvparams: dict of strings
248 @param hvparams: hvparams to be used with this instance
249
250 @rtype: (string, string, int, int, HvInstanceState, int)
251 @return: tuple (name, id, memory, vcpus, state, times)
252
253 """
254 raise NotImplementedError
255
257 """Get properties of all instances.
258
259 @type hvparams: dict of strings
260 @param hvparams: hypervisor parameter
261
262 @rtype: (string, string, int, int, HvInstanceState, int)
263 @return: list of tuples (name, id, memory, vcpus, state, times)
264
265 """
266 raise NotImplementedError
267
269 """Return information about the node.
270
271 @type hvparams: dict of strings
272 @param hvparams: hypervisor parameters
273
274 @return: a dict with at least the following keys (memory values in MiB):
275 - memory_total: the total memory size on the node
276 - memory_free: the available memory on the node for instances
277 - memory_dom0: the memory used by the node itself, if available
278 - cpu_total: total number of CPUs
279 - cpu_dom0: number of CPUs used by the node OS
280 - cpu_nodes: number of NUMA domains
281 - cpu_sockets: number of physical CPU sockets
282
283 """
284 raise NotImplementedError
285
286 @classmethod
289 """Return information for connecting to the console of an instance.
290
291 """
292 raise NotImplementedError
293
294 @classmethod
296 """Return a list of ancillary files to be copied to all nodes as ancillary
297 configuration files.
298
299 @rtype: (list of absolute paths, list of absolute paths)
300 @return: (all files, optional files)
301
302 """
303
304
305 assert set(cls.ANCILLARY_FILES).issuperset(cls.ANCILLARY_FILES_OPT), \
306 "Optional ancillary files must be a subset of ancillary files"
307
308 return (cls.ANCILLARY_FILES, cls.ANCILLARY_FILES_OPT)
309
310 - def Verify(self, hvparams=None):
311 """Verify the hypervisor.
312
313 @type hvparams: dict of strings
314 @param hvparams: hypervisor parameters to be verified against
315
316 @return: Problem description if something is wrong, C{None} otherwise
317
318 """
319 raise NotImplementedError
320
322 """Get instance information to perform a migration.
323
324 By default assume no information is needed.
325
326 @type instance: L{objects.Instance}
327 @param instance: instance to be migrated
328 @rtype: string/data (opaque)
329 @return: instance migration information - serialized form
330
331 """
332 return ""
333
335 """Prepare to accept an instance.
336
337 By default assume no preparation is needed.
338
339 @type instance: L{objects.Instance}
340 @param instance: instance to be accepted
341 @type info: string/data (opaque)
342 @param info: migration information, from the source node
343 @type target: string
344 @param target: target host (usually ip), on this node
345
346 """
347 pass
348
350 """Balloon an instance memory to a certain value.
351
352 @type instance: L{objects.Instance}
353 @param instance: instance to be accepted
354 @type mem: int
355 @param mem: actual memory size to use for instance runtime
356
357 """
358 raise NotImplementedError
359
361 """Finalize the instance migration on the target node.
362
363 Should finalize or revert any preparation done to accept the instance.
364 Since by default we do no preparation, we also don't have anything to do
365
366 @type instance: L{objects.Instance}
367 @param instance: instance whose migration is being finalized
368 @type info: string/data (opaque)
369 @param info: migration information, from the source node
370 @type success: boolean
371 @param success: whether the migration was a success or a failure
372
373 """
374 pass
375
377 """Migrate an instance.
378
379 @type cluster_name: string
380 @param cluster_name: name of the cluster
381 @type instance: L{objects.Instance}
382 @param instance: the instance to be migrated
383 @type target: string
384 @param target: hostname (usually ip) of the target node
385 @type live: boolean
386 @param live: whether to do a live or non-live migration
387
388 """
389 raise NotImplementedError
390
392 """Finalize the instance migration on the source node.
393
394 @type instance: L{objects.Instance}
395 @param instance: the instance that was migrated
396 @type success: bool
397 @param success: whether the migration succeeded or not
398 @type live: bool
399 @param live: whether the user requested a live migration or not
400
401 """
402 pass
403
405 """Get the migration status
406
407 @type instance: L{objects.Instance}
408 @param instance: the instance that is being migrated
409 @rtype: L{objects.MigrationStatus}
410 @return: the status of the current migration (one of
411 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
412 progress info that can be retrieved from the hypervisor
413
414 """
415 raise NotImplementedError
416
418 """Get the correct startup memory for an instance
419
420 This function calculates how much memory an instance should be started
421 with, making sure it's a value between the minimum and the maximum memory,
422 but also trying to use no more than the current free memory on the node.
423
424 @type instance: L{objects.Instance}
425 @param instance: the instance that is being started
426 @rtype: integer
427 @return: memory the instance should be started with
428
429 """
430 free_memory = self.GetNodeInfo(hvparams=instance.hvparams)["memory_free"]
431 max_start_mem = min(instance.beparams[constants.BE_MAXMEM], free_memory)
432 start_mem = max(instance.beparams[constants.BE_MINMEM], max_start_mem)
433 return start_mem
434
435 @classmethod
437 """Check the given parameters for validity.
438
439 This should check the passed set of parameters for
440 validity. Classes should extend, not replace, this function.
441
442 @type hvparams: dict
443 @param hvparams: dictionary with parameter names/value
444 @raise errors.HypervisorError: when a parameter is not valid
445
446 """
447 for key in hvparams:
448 if key not in cls.PARAMETERS:
449 raise errors.HypervisorError("Parameter '%s' is not supported" % key)
450
451
452 for name, (required, check_fn, errstr, _, _) in cls.PARAMETERS.items():
453 if name not in hvparams:
454 raise errors.HypervisorError("Parameter '%s' is missing" % name)
455 value = hvparams[name]
456 if not required and not value:
457 continue
458 if not value:
459 raise errors.HypervisorError("Parameter '%s' is required but"
460 " is currently not defined" % (name, ))
461 if check_fn is not None and not check_fn(value):
462 raise errors.HypervisorError("Parameter '%s' fails syntax"
463 " check: %s (current value: '%s')" %
464 (name, errstr, value))
465
466 @classmethod
468 """Check the given parameters for validity.
469
470 This should check the passed set of parameters for
471 validity. Classes should extend, not replace, this function.
472
473 @type hvparams: dict
474 @param hvparams: dictionary with parameter names/value
475 @raise errors.HypervisorError: when a parameter is not valid
476
477 """
478 for name, (required, _, _, check_fn, errstr) in cls.PARAMETERS.items():
479 value = hvparams[name]
480 if not required and not value:
481 continue
482 if check_fn is not None and not check_fn(value):
483 raise errors.HypervisorError("Parameter '%s' fails"
484 " validation: %s (current value: '%s')" %
485 (name, errstr, value))
486
487 @classmethod
489 """Hard powercycle a node using hypervisor specific methods.
490
491 This method should hard powercycle the node, using whatever
492 methods the hypervisor provides. Note that this means that all
493 instances running on the node must be stopped too.
494
495 @type hvparams: dict of strings
496 @param hvparams: hypervisor params to be used on this node
497
498 """
499 raise NotImplementedError
500
501 @staticmethod
503 """For linux systems, return actual OS information.
504
505 This is an abstraction for all non-hypervisor-based classes, where
506 the node actually sees all the memory and CPUs via the /proc
507 interface and standard commands. The other case if for example
508 xen, where you only see the hardware resources via xen-specific
509 tools.
510
511 @param meminfo: name of the file containing meminfo
512 @type meminfo: string
513 @param cpuinfo: name of the file containing cpuinfo
514 @type cpuinfo: string
515 @return: a dict with the following keys (values in MiB):
516 - memory_total: the total memory size on the node
517 - memory_free: the available memory on the node for instances
518 - memory_dom0: the memory used by the node itself, if available
519 - cpu_total: total number of CPUs
520 - cpu_dom0: number of CPUs used by the node OS
521 - cpu_nodes: number of NUMA domains
522 - cpu_sockets: number of physical CPU sockets
523
524 """
525 try:
526 data = utils.ReadFile(meminfo).splitlines()
527 except EnvironmentError, err:
528 raise errors.HypervisorError("Failed to list node info: %s" % (err,))
529
530 result = {}
531 sum_free = 0
532 try:
533 for line in data:
534 splitfields = line.split(":", 1)
535
536 if len(splitfields) > 1:
537 key = splitfields[0].strip()
538 val = splitfields[1].strip()
539 if key == "MemTotal":
540 result["memory_total"] = int(val.split()[0]) / 1024
541 elif key in ("MemFree", "Buffers", "Cached"):
542 sum_free += int(val.split()[0]) / 1024
543 elif key == "Active":
544 result["memory_dom0"] = int(val.split()[0]) / 1024
545 except (ValueError, TypeError), err:
546 raise errors.HypervisorError("Failed to compute memory usage: %s" %
547 (err,))
548 result["memory_free"] = sum_free
549
550 cpu_total = 0
551 try:
552 fh = open(cpuinfo)
553 try:
554 cpu_total = len(re.findall(r"(?m)^processor\s*:\s*[0-9]+\s*$",
555 fh.read()))
556 finally:
557 fh.close()
558 except EnvironmentError, err:
559 raise errors.HypervisorError("Failed to list node info: %s" % (err,))
560 result["cpu_total"] = cpu_total
561
562 result["cpu_dom0"] = cpu_total
563
564 result["cpu_nodes"] = 1
565 result["cpu_sockets"] = 1
566
567 return result
568
569 @classmethod
571 """Linux-specific powercycle method.
572
573 """
574 try:
575 fd = os.open("/proc/sysrq-trigger", os.O_WRONLY)
576 try:
577 os.write(fd, "b")
578 finally:
579 fd.close()
580 except OSError:
581 logging.exception("Can't open the sysrq-trigger file")
582 result = utils.RunCmd(["reboot", "-n", "-f"])
583 if not result:
584 logging.error("Can't run shutdown: %s", result.output)
585
586 @staticmethod
599
600
601 - def HotAddDevice(self, instance, dev_type, device, extra, seq):
602 """Hot-add a device.
603
604 """
605 raise errors.HotplugError("Hotplug is not supported by this hypervisor")
606
607
608 - def HotDelDevice(self, instance, dev_type, device, extra, seq):
609 """Hot-del a device.
610
611 """
612 raise errors.HotplugError("Hotplug is not supported by this hypervisor")
613
614
615 - def HotModDevice(self, instance, dev_type, device, extra, seq):
616 """Hot-mod a device.
617
618 """
619 raise errors.HotplugError("Hotplug is not supported by this hypervisor")
620
621
623 """Verifies that hotplug is supported.
624
625 Given the target device and hotplug action checks if hotplug is
626 actually supported.
627
628 @type instance: L{objects.Instance}
629 @param instance: the instance object
630 @type action: string
631 @param action: one of the supported hotplug commands
632 @type dev_type: string
633 @param dev_type: one of the supported device types to hotplug
634 @raise errors.HotplugError: if hotplugging is not supported
635
636 """
637 raise errors.HotplugError("Hotplug is not supported.")
638
640 """Checks if hotplug is supported.
641
642 By default is not. Currently only KVM hypervisor supports it.
643
644 """
645 raise errors.HotplugError("Hotplug is not supported by this hypervisor")
646