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
429 @staticmethod
441
443 """Get instance information to perform a migration.
444
445 By default assume no information is needed.
446
447 @type instance: L{objects.Instance}
448 @param instance: instance to be migrated
449 @rtype: string/data (opaque)
450 @return: instance migration information - serialized form
451
452 """
453 return ""
454
456 """Prepare to accept an instance.
457
458 By default assume no preparation is needed.
459
460 @type instance: L{objects.Instance}
461 @param instance: instance to be accepted
462 @type info: string/data (opaque)
463 @param info: migration information, from the source node
464 @type target: string
465 @param target: target host (usually ip), on this node
466
467 """
468 pass
469
471 """Balloon an instance memory to a certain value.
472
473 @type instance: L{objects.Instance}
474 @param instance: instance to be accepted
475 @type mem: int
476 @param mem: actual memory size to use for instance runtime
477
478 """
479 raise NotImplementedError
480
482 """Finalize the instance migration on the target node.
483
484 Should finalize or revert any preparation done to accept the instance.
485 Since by default we do no preparation, we also don't have anything to do
486
487 @type instance: L{objects.Instance}
488 @param instance: instance whose migration is being finalized
489 @type info: string/data (opaque)
490 @param info: migration information, from the source node
491 @type success: boolean
492 @param success: whether the migration was a success or a failure
493
494 """
495 pass
496
498 """Migrate an instance.
499
500 @type cluster_name: string
501 @param cluster_name: name of the cluster
502 @type instance: L{objects.Instance}
503 @param instance: the instance to be migrated
504 @type target: string
505 @param target: hostname (usually ip) of the target node
506 @type live: boolean
507 @param live: whether to do a live or non-live migration
508
509 """
510 raise NotImplementedError
511
513 """Finalize the instance migration on the source node.
514
515 @type instance: L{objects.Instance}
516 @param instance: the instance that was migrated
517 @type success: bool
518 @param success: whether the migration succeeded or not
519 @type live: bool
520 @param live: whether the user requested a live migration or not
521
522 """
523 pass
524
526 """Get the migration status
527
528 @type instance: L{objects.Instance}
529 @param instance: the instance that is being migrated
530 @rtype: L{objects.MigrationStatus}
531 @return: the status of the current migration (one of
532 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
533 progress info that can be retrieved from the hypervisor
534
535 """
536 raise NotImplementedError
537
539 """Get the correct startup memory for an instance
540
541 This function calculates how much memory an instance should be started
542 with, making sure it's a value between the minimum and the maximum memory,
543 but also trying to use no more than the current free memory on the node.
544
545 @type instance: L{objects.Instance}
546 @param instance: the instance that is being started
547 @rtype: integer
548 @return: memory the instance should be started with
549
550 """
551 free_memory = self.GetNodeInfo(hvparams=instance.hvparams)["memory_free"]
552 max_start_mem = min(instance.beparams[constants.BE_MAXMEM], free_memory)
553 start_mem = max(instance.beparams[constants.BE_MINMEM], max_start_mem)
554 return start_mem
555
556 @classmethod
558 """Check if the parameter value is a kind of value meaning unspecified.
559
560 This function checks if the parameter value is a kind of value meaning
561 unspecified.
562
563 @type param_value: any
564 @param param_value: the parameter value that needs to be checked
565 @rtype: bool
566 @return: True if the parameter value is a kind of value meaning unspecified,
567 False otherwise
568
569 """
570 return param_value is None \
571 or isinstance(param_value, basestring) and param_value == ""
572
573 @classmethod
575 """Check the given parameters for validity.
576
577 This should check the passed set of parameters for
578 validity. Classes should extend, not replace, this function.
579
580 @type hvparams: dict
581 @param hvparams: dictionary with parameter names/value
582 @raise errors.HypervisorError: when a parameter is not valid
583
584 """
585 for key in hvparams:
586 if key not in cls.PARAMETERS:
587 raise errors.HypervisorError("Parameter '%s' is not supported" % key)
588
589
590 for name, (required, check_fn, errstr, _, _) in cls.PARAMETERS.items():
591 if name not in hvparams:
592 raise errors.HypervisorError("Parameter '%s' is missing" % name)
593 value = hvparams[name]
594 if not required and cls._IsParamValueUnspecified(value):
595 continue
596 if cls._IsParamValueUnspecified(value):
597 raise errors.HypervisorError("Parameter '%s' is required but"
598 " is currently not defined" % (name, ))
599 if check_fn is not None and not check_fn(value):
600 raise errors.HypervisorError("Parameter '%s' fails syntax"
601 " check: %s (current value: '%s')" %
602 (name, errstr, value))
603
604 @classmethod
606 """Check the given parameters for validity.
607
608 This should check the passed set of parameters for
609 validity. Classes should extend, not replace, this function.
610
611 @type hvparams: dict
612 @param hvparams: dictionary with parameter names/value
613 @raise errors.HypervisorError: when a parameter is not valid
614
615 """
616 for name, (required, _, _, check_fn, errstr) in cls.PARAMETERS.items():
617 value = hvparams[name]
618 if not required and cls._IsParamValueUnspecified(value):
619 continue
620 if check_fn is not None and not check_fn(value):
621 raise errors.HypervisorError("Parameter '%s' fails"
622 " validation: %s (current value: '%s')" %
623 (name, errstr, value))
624
625 @classmethod
627 """Hard powercycle a node using hypervisor specific methods.
628
629 This method should hard powercycle the node, using whatever
630 methods the hypervisor provides. Note that this means that all
631 instances running on the node must be stopped too.
632
633 @type hvparams: dict of strings
634 @param hvparams: hypervisor params to be used on this node
635
636 """
637 raise NotImplementedError
638
639 @staticmethod
641 """For linux systems, return actual OS information.
642
643 This is an abstraction for all non-hypervisor-based classes, where
644 the node actually sees all the memory and CPUs via the /proc
645 interface and standard commands. The other case if for example
646 xen, where you only see the hardware resources via xen-specific
647 tools.
648
649 @param meminfo: name of the file containing meminfo
650 @type meminfo: string
651 @param cpuinfo: name of the file containing cpuinfo
652 @type cpuinfo: string
653 @return: a dict with the following keys (values in MiB):
654 - memory_total: the total memory size on the node
655 - memory_free: the available memory on the node for instances
656 - memory_dom0: the memory used by the node itself, if available
657 - cpu_total: total number of CPUs
658 - cpu_dom0: number of CPUs used by the node OS
659 - cpu_nodes: number of NUMA domains
660 - cpu_sockets: number of physical CPU sockets
661
662 """
663 try:
664 data = utils.ReadFile(meminfo).splitlines()
665 except EnvironmentError, err:
666 raise errors.HypervisorError("Failed to list node info: %s" % (err,))
667
668 result = {}
669 sum_free = 0
670 try:
671 for line in data:
672 splitfields = line.split(":", 1)
673
674 if len(splitfields) > 1:
675 key = splitfields[0].strip()
676 val = splitfields[1].strip()
677 if key == "MemTotal":
678 result["memory_total"] = int(val.split()[0]) / 1024
679 elif key in ("MemFree", "Buffers", "Cached"):
680 sum_free += int(val.split()[0]) / 1024
681 elif key == "Active":
682 result["memory_dom0"] = int(val.split()[0]) / 1024
683 except (ValueError, TypeError), err:
684 raise errors.HypervisorError("Failed to compute memory usage: %s" %
685 (err,))
686 result["memory_free"] = sum_free
687
688 cpu_total = 0
689 try:
690 fh = open(cpuinfo)
691 try:
692 cpu_total = len(re.findall(r"(?m)^processor\s*:\s*[0-9]+\s*$",
693 fh.read()))
694 finally:
695 fh.close()
696 except EnvironmentError, err:
697 raise errors.HypervisorError("Failed to list node info: %s" % (err,))
698 result["cpu_total"] = cpu_total
699
700 result["cpu_dom0"] = cpu_total
701
702 result["cpu_nodes"] = 1
703 result["cpu_sockets"] = 1
704
705 return result
706
707 @classmethod
709 """Linux-specific powercycle method.
710
711 """
712 try:
713 fd = os.open("/proc/sysrq-trigger", os.O_WRONLY)
714 try:
715 os.write(fd, "b")
716 finally:
717 fd.close()
718 except OSError:
719 logging.exception("Can't open the sysrq-trigger file")
720 result = utils.RunCmd(["reboot", "-n", "-f"])
721 if not result:
722 logging.error("Can't run shutdown: %s", result.output)
723
724 @staticmethod
737
738
739 - def HotAddDevice(self, instance, dev_type, device, extra, seq):
740 """Hot-add a device.
741
742 """
743 raise errors.HotplugError("Hotplug is not supported by this hypervisor")
744
745
746 - def HotDelDevice(self, instance, dev_type, device, extra, seq):
747 """Hot-del a device.
748
749 """
750 raise errors.HotplugError("Hotplug is not supported by this hypervisor")
751
752
753 - def HotModDevice(self, instance, dev_type, device, extra, seq):
754 """Hot-mod a device.
755
756 """
757 raise errors.HotplugError("Hotplug is not supported by this hypervisor")
758
759
761 """Verifies that hotplug is supported.
762
763 Given the target device and hotplug action checks if hotplug is
764 actually supported.
765
766 @type instance: L{objects.Instance}
767 @param instance: the instance object
768 @type action: string
769 @param action: one of the supported hotplug commands
770 @type dev_type: string
771 @param dev_type: one of the supported device types to hotplug
772 @raise errors.HotplugError: if hotplugging is not supported
773
774 """
775 raise errors.HotplugError("Hotplug is not supported.")
776
778 """Checks if hotplug is supported.
779
780 By default is not. Currently only KVM hypervisor supports it.
781
782 """
783 raise errors.HotplugError("Hotplug is not supported by this hypervisor")
784