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 constants
55 from ganeti import errors
56 from ganeti import objects
57 from ganeti import utils
72
87
88
89
90
91
92
93 _FILE_CHECK = (utils.IsNormAbsPath, "must be an absolute normalized path",
94 os.path.isfile, "not found or not a file")
95
96
97 _FILE_OR_URL_CHECK = (lambda x: utils.IsNormAbsPath(x) or utils.IsUrl(x),
98 "must be an absolute normalized path or a URL",
99 lambda x: os.path.isfile(x) or utils.IsUrl(x),
100 "not found or not a file or URL")
101
102
103 _DIR_CHECK = (utils.IsNormAbsPath, "must be an absolute normalized path",
104 os.path.isdir, "not found or not a directory")
105
106
107
108 _CPU_MASK_CHECK = (_IsCpuMaskWellFormed,
109 "CPU mask definition is not well-formed",
110 None, None)
111
112
113 _MULTI_CPU_MASK_CHECK = (_IsMultiCpuMaskWellFormed,
114 "Multiple CPU mask definition is not well-formed",
115 None, None)
116
117
118 _NET_PORT_CHECK = (lambda x: 0 < x < 65535, "invalid port number",
119 None, None)
120
121
122 _VIRTIO_NET_QUEUES_CHECK = (lambda x: 0 < x < 9, "invalid number of queues",
123 None, None)
124
125
126 _NONNEGATIVE_INT_CHECK = (lambda x: x >= 0, "cannot be negative", None, None)
127
128
129 REQ_FILE_CHECK = (True, ) + _FILE_CHECK
130 OPT_FILE_CHECK = (False, ) + _FILE_CHECK
131 REQ_FILE_OR_URL_CHECK = (True, ) + _FILE_OR_URL_CHECK
132 OPT_FILE_OR_URL_CHECK = (False, ) + _FILE_OR_URL_CHECK
133 REQ_DIR_CHECK = (True, ) + _DIR_CHECK
134 OPT_DIR_CHECK = (False, ) + _DIR_CHECK
135 REQ_NET_PORT_CHECK = (True, ) + _NET_PORT_CHECK
136 OPT_NET_PORT_CHECK = (False, ) + _NET_PORT_CHECK
137 REQ_VIRTIO_NET_QUEUES_CHECK = (True, ) + _VIRTIO_NET_QUEUES_CHECK
138 OPT_VIRTIO_NET_QUEUES_CHECK = (False, ) + _VIRTIO_NET_QUEUES_CHECK
139 REQ_CPU_MASK_CHECK = (True, ) + _CPU_MASK_CHECK
140 OPT_CPU_MASK_CHECK = (False, ) + _CPU_MASK_CHECK
141 REQ_MULTI_CPU_MASK_CHECK = (True, ) + _MULTI_CPU_MASK_CHECK
142 OPT_MULTI_CPU_MASK_CHECK = (False, ) + _MULTI_CPU_MASK_CHECK
143 REQ_NONNEGATIVE_INT_CHECK = (True, ) + _NONNEGATIVE_INT_CHECK
144 OPT_NONNEGATIVE_INT_CHECK = (False, ) + _NONNEGATIVE_INT_CHECK
145
146
147 NO_CHECK = (False, None, None, None, None)
148
149
150 REQUIRED_CHECK = (True, None, None, None, None)
151
152
153 MIGRATION_MODE_CHECK = (True, lambda x: x in constants.HT_MIGRATION_MODES,
154 "invalid migration mode", None, None)
158 """Builds parameter checker for set membership.
159
160 @type required: boolean
161 @param required: whether this is a required parameter
162 @type my_set: tuple, list or set
163 @param my_set: allowed values set
164
165 """
166 fn = lambda x: x in my_set
167 err = ("The value must be one of: %s" % utils.CommaJoin(my_set))
168 return (required, fn, err, None, None)
169
172 """Generate a TAP network interface name for a NIC.
173
174 This helper function generates a special TAP network interface
175 name for NICs that are meant to be used in instance communication.
176 This function checks the existing TAP interfaces in order to find
177 a unique name for the new TAP network interface. The TAP network
178 interface names are of the form 'gnt.com.%d', where '%d' is a
179 unique number within the node.
180
181 @rtype: string
182 @return: TAP network interface name, or the empty string if the
183 NIC is not used in instance communication
184
185 """
186 result = utils.RunCmd(["ip", "link", "show"])
187
188 if result.failed:
189 raise errors.HypervisorError("Failed to list TUN/TAP interfaces")
190
191 idxs = set()
192
193 for line in result.output.splitlines()[0::2]:
194 parts = line.split(": ")
195
196 if len(parts) < 2:
197 raise errors.HypervisorError("Failed to parse TUN/TAP interfaces")
198
199 r = re.match(r"gnt\.com\.([0-9]+)", parts[1])
200
201 if r is not None:
202 idxs.add(int(r.group(1)))
203
204 if idxs:
205 idx = max(idxs) + 1
206 else:
207 idx = 0
208
209 return "gnt.com.%d" % idx
210
262
265 RUNNING = 0
266 SHUTDOWN = 1
267
268 @staticmethod
271
272 @staticmethod
275
278 """Abstract virtualisation technology interface
279
280 The goal is that all aspects of the virtualisation technology are
281 abstracted away from the rest of code.
282
283 @cvar PARAMETERS: a dict of parameter name: check type; the check type is
284 a five-tuple containing:
285 - the required flag (boolean)
286 - a function to check for syntax, that will be used in
287 L{CheckParameterSyntax}, in the master daemon process
288 - an error message for the above function
289 - a function to check for parameter validity on the remote node,
290 in the L{ValidateParameters} function
291 - an error message for the above function
292 @type CAN_MIGRATE: boolean
293 @cvar CAN_MIGRATE: whether this hypervisor can do migration (either
294 live or non-live)
295
296 """
297 PARAMETERS = {}
298 ANCILLARY_FILES = []
299 ANCILLARY_FILES_OPT = []
300 CAN_MIGRATE = False
301
302 - def StartInstance(self, instance, block_devices, startup_paused):
303 """Start an instance."""
304 raise NotImplementedError
305
306 - def StopInstance(self, instance, force=False, retry=False, name=None,
307 timeout=None):
308 """Stop an instance
309
310 @type instance: L{objects.Instance}
311 @param instance: instance to stop
312 @type force: boolean
313 @param force: whether to do a "hard" stop (destroy)
314 @type retry: boolean
315 @param retry: whether this is just a retry call
316 @type name: string or None
317 @param name: if this parameter is passed, the the instance object
318 should not be used (will be passed as None), and the shutdown
319 must be done by name only
320 @type timeout: int or None
321 @param timeout: if the parameter is not None, a soft shutdown operation will
322 be killed after the specified number of seconds. A hard (forced)
323 shutdown cannot have a timeout
324 @raise errors.HypervisorError: when a parameter is not valid or
325 the instance failed to be stopped
326
327 """
328 raise NotImplementedError
329
331 """Cleanup after a stopped instance
332
333 This is an optional method, used by hypervisors that need to cleanup after
334 an instance has been stopped.
335
336 @type instance_name: string
337 @param instance_name: instance name to cleanup after
338
339 """
340 pass
341
343 """Reboot an instance."""
344 raise NotImplementedError
345
347 """Get the list of running instances."""
348 raise NotImplementedError
349
351 """Get instance properties.
352
353 @type instance_name: string
354 @param instance_name: the instance name
355 @type hvparams: dict of strings
356 @param hvparams: hvparams to be used with this instance
357
358 @rtype: (string, string, int, int, HvInstanceState, int)
359 @return: tuple (name, id, memory, vcpus, state, times)
360
361 """
362 raise NotImplementedError
363
365 """Get properties of all instances.
366
367 @type hvparams: dict of strings
368 @param hvparams: hypervisor parameter
369
370 @rtype: (string, string, int, int, HvInstanceState, int)
371 @return: list of tuples (name, id, memory, vcpus, state, times)
372
373 """
374 raise NotImplementedError
375
377 """Return information about the node.
378
379 @type hvparams: dict of strings
380 @param hvparams: hypervisor parameters
381
382 @return: a dict with at least the following keys (memory values in MiB):
383 - memory_total: the total memory size on the node
384 - memory_free: the available memory on the node for instances
385 - memory_dom0: the memory used by the node itself, if available
386 - cpu_total: total number of CPUs
387 - cpu_dom0: number of CPUs used by the node OS
388 - cpu_nodes: number of NUMA domains
389 - cpu_sockets: number of physical CPU sockets
390
391 """
392 raise NotImplementedError
393
394 @classmethod
397 """Return information for connecting to the console of an instance.
398
399 """
400 raise NotImplementedError
401
402 @classmethod
404 """Return a list of ancillary files to be copied to all nodes as ancillary
405 configuration files.
406
407 @rtype: (list of absolute paths, list of absolute paths)
408 @return: (all files, optional files)
409
410 """
411
412
413 assert set(cls.ANCILLARY_FILES).issuperset(cls.ANCILLARY_FILES_OPT), \
414 "Optional ancillary files must be a subset of ancillary files"
415
416 return (cls.ANCILLARY_FILES, cls.ANCILLARY_FILES_OPT)
417
418 - def Verify(self, hvparams=None):
419 """Verify the hypervisor.
420
421 @type hvparams: dict of strings
422 @param hvparams: hypervisor parameters to be verified against
423
424 @return: Problem description if something is wrong, C{None} otherwise
425
426 """
427 raise NotImplementedError
428
430 """Get instance information to perform a migration.
431
432 By default assume no information is needed.
433
434 @type instance: L{objects.Instance}
435 @param instance: instance to be migrated
436 @rtype: string/data (opaque)
437 @return: instance migration information - serialized form
438
439 """
440 return ""
441
443 """Prepare to accept an instance.
444
445 By default assume no preparation is needed.
446
447 @type instance: L{objects.Instance}
448 @param instance: instance to be accepted
449 @type info: string/data (opaque)
450 @param info: migration information, from the source node
451 @type target: string
452 @param target: target host (usually ip), on this node
453
454 """
455 pass
456
458 """Balloon an instance memory to a certain value.
459
460 @type instance: L{objects.Instance}
461 @param instance: instance to be accepted
462 @type mem: int
463 @param mem: actual memory size to use for instance runtime
464
465 """
466 raise NotImplementedError
467
469 """Finalize the instance migration on the target node.
470
471 Should finalize or revert any preparation done to accept the instance.
472 Since by default we do no preparation, we also don't have anything to do
473
474 @type instance: L{objects.Instance}
475 @param instance: instance whose migration is being finalized
476 @type info: string/data (opaque)
477 @param info: migration information, from the source node
478 @type success: boolean
479 @param success: whether the migration was a success or a failure
480
481 """
482 pass
483
485 """Migrate an instance.
486
487 @type cluster_name: string
488 @param cluster_name: name of the cluster
489 @type instance: L{objects.Instance}
490 @param instance: the instance to be migrated
491 @type target: string
492 @param target: hostname (usually ip) of the target node
493 @type live: boolean
494 @param live: whether to do a live or non-live migration
495
496 """
497 raise NotImplementedError
498
500 """Finalize the instance migration on the source node.
501
502 @type instance: L{objects.Instance}
503 @param instance: the instance that was migrated
504 @type success: bool
505 @param success: whether the migration succeeded or not
506 @type live: bool
507 @param live: whether the user requested a live migration or not
508
509 """
510 pass
511
513 """Get the migration status
514
515 @type instance: L{objects.Instance}
516 @param instance: the instance that is being migrated
517 @rtype: L{objects.MigrationStatus}
518 @return: the status of the current migration (one of
519 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
520 progress info that can be retrieved from the hypervisor
521
522 """
523 raise NotImplementedError
524
526 """Get the correct startup memory for an instance
527
528 This function calculates how much memory an instance should be started
529 with, making sure it's a value between the minimum and the maximum memory,
530 but also trying to use no more than the current free memory on the node.
531
532 @type instance: L{objects.Instance}
533 @param instance: the instance that is being started
534 @rtype: integer
535 @return: memory the instance should be started with
536
537 """
538 free_memory = self.GetNodeInfo(hvparams=instance.hvparams)["memory_free"]
539 max_start_mem = min(instance.beparams[constants.BE_MAXMEM], free_memory)
540 start_mem = max(instance.beparams[constants.BE_MINMEM], max_start_mem)
541 return start_mem
542
543 @classmethod
545 """Check the given parameters for validity.
546
547 This should check the passed set of parameters for
548 validity. Classes should extend, not replace, this function.
549
550 @type hvparams: dict
551 @param hvparams: dictionary with parameter names/value
552 @raise errors.HypervisorError: when a parameter is not valid
553
554 """
555 for key in hvparams:
556 if key not in cls.PARAMETERS:
557 raise errors.HypervisorError("Parameter '%s' is not supported" % key)
558
559
560 for name, (required, check_fn, errstr, _, _) in cls.PARAMETERS.items():
561 if name not in hvparams:
562 raise errors.HypervisorError("Parameter '%s' is missing" % name)
563 value = hvparams[name]
564 if not required and not value:
565 continue
566 if not value:
567 raise errors.HypervisorError("Parameter '%s' is required but"
568 " is currently not defined" % (name, ))
569 if check_fn is not None and not check_fn(value):
570 raise errors.HypervisorError("Parameter '%s' fails syntax"
571 " check: %s (current value: '%s')" %
572 (name, errstr, value))
573
574 @classmethod
576 """Check the given parameters for validity.
577
578 This should check the passed set of parameters for
579 validity. Classes should extend, not replace, this function.
580
581 @type hvparams: dict
582 @param hvparams: dictionary with parameter names/value
583 @raise errors.HypervisorError: when a parameter is not valid
584
585 """
586 for name, (required, _, _, check_fn, errstr) in cls.PARAMETERS.items():
587 value = hvparams[name]
588 if not required and not value:
589 continue
590 if check_fn is not None and not check_fn(value):
591 raise errors.HypervisorError("Parameter '%s' fails"
592 " validation: %s (current value: '%s')" %
593 (name, errstr, value))
594
595 @classmethod
597 """Hard powercycle a node using hypervisor specific methods.
598
599 This method should hard powercycle the node, using whatever
600 methods the hypervisor provides. Note that this means that all
601 instances running on the node must be stopped too.
602
603 @type hvparams: dict of strings
604 @param hvparams: hypervisor params to be used on this node
605
606 """
607 raise NotImplementedError
608
609 @staticmethod
611 """For linux systems, return actual OS information.
612
613 This is an abstraction for all non-hypervisor-based classes, where
614 the node actually sees all the memory and CPUs via the /proc
615 interface and standard commands. The other case if for example
616 xen, where you only see the hardware resources via xen-specific
617 tools.
618
619 @param meminfo: name of the file containing meminfo
620 @type meminfo: string
621 @param cpuinfo: name of the file containing cpuinfo
622 @type cpuinfo: string
623 @return: a dict with the following keys (values in MiB):
624 - memory_total: the total memory size on the node
625 - memory_free: the available memory on the node for instances
626 - memory_dom0: the memory used by the node itself, if available
627 - cpu_total: total number of CPUs
628 - cpu_dom0: number of CPUs used by the node OS
629 - cpu_nodes: number of NUMA domains
630 - cpu_sockets: number of physical CPU sockets
631
632 """
633 try:
634 data = utils.ReadFile(meminfo).splitlines()
635 except EnvironmentError, err:
636 raise errors.HypervisorError("Failed to list node info: %s" % (err,))
637
638 result = {}
639 sum_free = 0
640 try:
641 for line in data:
642 splitfields = line.split(":", 1)
643
644 if len(splitfields) > 1:
645 key = splitfields[0].strip()
646 val = splitfields[1].strip()
647 if key == "MemTotal":
648 result["memory_total"] = int(val.split()[0]) / 1024
649 elif key in ("MemFree", "Buffers", "Cached"):
650 sum_free += int(val.split()[0]) / 1024
651 elif key == "Active":
652 result["memory_dom0"] = int(val.split()[0]) / 1024
653 except (ValueError, TypeError), err:
654 raise errors.HypervisorError("Failed to compute memory usage: %s" %
655 (err,))
656 result["memory_free"] = sum_free
657
658 cpu_total = 0
659 try:
660 fh = open(cpuinfo)
661 try:
662 cpu_total = len(re.findall(r"(?m)^processor\s*:\s*[0-9]+\s*$",
663 fh.read()))
664 finally:
665 fh.close()
666 except EnvironmentError, err:
667 raise errors.HypervisorError("Failed to list node info: %s" % (err,))
668 result["cpu_total"] = cpu_total
669
670 result["cpu_dom0"] = cpu_total
671
672 result["cpu_nodes"] = 1
673 result["cpu_sockets"] = 1
674
675 return result
676
677 @classmethod
679 """Linux-specific powercycle method.
680
681 """
682 try:
683 fd = os.open("/proc/sysrq-trigger", os.O_WRONLY)
684 try:
685 os.write(fd, "b")
686 finally:
687 fd.close()
688 except OSError:
689 logging.exception("Can't open the sysrq-trigger file")
690 result = utils.RunCmd(["reboot", "-n", "-f"])
691 if not result:
692 logging.error("Can't run shutdown: %s", result.output)
693
694 @staticmethod
707
708
709 - def HotAddDevice(self, instance, dev_type, device, extra, seq):
710 """Hot-add a device.
711
712 """
713 raise errors.HotplugError("Hotplug is not supported by this hypervisor")
714
715
716 - def HotDelDevice(self, instance, dev_type, device, extra, seq):
717 """Hot-del a device.
718
719 """
720 raise errors.HotplugError("Hotplug is not supported by this hypervisor")
721
722
723 - def HotModDevice(self, instance, dev_type, device, extra, seq):
724 """Hot-mod a device.
725
726 """
727 raise errors.HotplugError("Hotplug is not supported by this hypervisor")
728
729
731 """Verifies that hotplug is supported.
732
733 Given the target device and hotplug action checks if hotplug is
734 actually supported.
735
736 @type instance: L{objects.Instance}
737 @param instance: the instance object
738 @type action: string
739 @param action: one of the supported hotplug commands
740 @type dev_type: string
741 @param dev_type: one of the supported device types to hotplug
742 @raise errors.HotplugError: if hotplugging is not supported
743
744 """
745 raise errors.HotplugError("Hotplug is not supported.")
746
748 """Checks if hotplug is supported.
749
750 By default is not. Currently only KVM hypervisor supports it.
751
752 """
753 raise errors.HotplugError("Hotplug is not supported by this hypervisor")
754