Package ganeti :: Package hypervisor :: Module hv_base
[hide private]
[frames] | no frames]

Source Code for Module ganeti.hypervisor.hv_base

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2012, 2013 Google Inc. 
  5  # All rights reserved. 
  6  # 
  7  # Redistribution and use in source and binary forms, with or without 
  8  # modification, are permitted provided that the following conditions are 
  9  # met: 
 10  # 
 11  # 1. Redistributions of source code must retain the above copyright notice, 
 12  # this list of conditions and the following disclaimer. 
 13  # 
 14  # 2. Redistributions in binary form must reproduce the above copyright 
 15  # notice, this list of conditions and the following disclaimer in the 
 16  # documentation and/or other materials provided with the distribution. 
 17  # 
 18  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
 19  # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
 20  # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 21  # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
 22  # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 23  # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 24  # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
 25  # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
 26  # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
 27  # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 28  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 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 
57 58 59 -def _IsCpuMaskWellFormed(cpu_mask):
60 """Verifies if the given single CPU mask is valid 61 62 The single CPU mask should be in the form "a,b,c,d", where each 63 letter is a positive number or range. 64 65 """ 66 try: 67 cpu_list = utils.ParseCpuMask(cpu_mask) 68 except errors.ParseError, _: 69 return False 70 return isinstance(cpu_list, list) and len(cpu_list) > 0
71
72 73 -def _IsMultiCpuMaskWellFormed(cpu_mask):
74 """Verifies if the given multiple CPU mask is valid 75 76 A valid multiple CPU mask is in the form "a:b:c:d", where each 77 letter is a single CPU mask. 78 79 """ 80 try: 81 utils.ParseMultiCpuMask(cpu_mask) 82 except errors.ParseError, _: 83 return False 84 85 return True
86 87 88 # Read the BaseHypervisor.PARAMETERS docstring for the syntax of the 89 # _CHECK values 90 91 # must be afile 92 _FILE_CHECK = (utils.IsNormAbsPath, "must be an absolute normalized path", 93 os.path.isfile, "not found or not a file") 94 95 # must be a directory 96 _DIR_CHECK = (utils.IsNormAbsPath, "must be an absolute normalized path", 97 os.path.isdir, "not found or not a directory") 98 99 # CPU mask must be well-formed 100 # TODO: implement node level check for the CPU mask 101 _CPU_MASK_CHECK = (_IsCpuMaskWellFormed, 102 "CPU mask definition is not well-formed", 103 None, None) 104 105 # Multiple CPU mask must be well-formed 106 _MULTI_CPU_MASK_CHECK = (_IsMultiCpuMaskWellFormed, 107 "Multiple CPU mask definition is not well-formed", 108 None, None) 109 110 # Check for validity of port number 111 _NET_PORT_CHECK = (lambda x: 0 < x < 65535, "invalid port number", 112 None, None) 113 114 # Check that an integer is non negative 115 _NONNEGATIVE_INT_CHECK = (lambda x: x >= 0, "cannot be negative", None, None) 116 117 # nice wrappers for users 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 # no checks at all 132 NO_CHECK = (False, None, None, None, None) 133 134 # required, but no other checks 135 REQUIRED_CHECK = (True, None, None, None, None) 136 137 # migration type 138 MIGRATION_MODE_CHECK = (True, lambda x: x in constants.HT_MIGRATION_MODES, 139 "invalid migration mode", None, None)
140 141 142 -def ParamInSet(required, my_set):
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
155 156 -class HvInstanceState(object):
157 RUNNING = 0 158 SHUTDOWN = 1 159 160 @staticmethod
161 - def IsRunning(s):
162 return s == HvInstanceState.RUNNING
163 164 @staticmethod
165 - def IsShutdown(s):
166 return s == HvInstanceState.SHUTDOWN
167
168 169 -class BaseHypervisor(object):
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
222 - def CleanupInstance(self, instance_name):
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
234 - def RebootInstance(self, instance):
235 """Reboot an instance.""" 236 raise NotImplementedError
237
238 - def ListInstances(self, hvparams=None):
239 """Get the list of running instances.""" 240 raise NotImplementedError
241
242 - def GetInstanceInfo(self, instance_name, hvparams=None):
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
256 - def GetAllInstancesInfo(self, hvparams=None):
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
268 - def GetNodeInfo(self, hvparams=None):
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
287 - def GetInstanceConsole(cls, instance, primary_node, node_group, 288 hvparams, beparams):
289 """Return information for connecting to the console of an instance. 290 291 """ 292 raise NotImplementedError
293 294 @classmethod
295 - def GetAncillaryFiles(cls):
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 # By default we return a member variable, so that if an hypervisor has just 304 # a static list of files it doesn't have to override this function. 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
321 - def MigrationInfo(self, instance): # pylint: disable=R0201,W0613
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
334 - def AcceptInstance(self, instance, info, target):
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
349 - def BalloonInstanceMemory(self, instance, mem):
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
360 - def FinalizeMigrationDst(self, instance, info, success):
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
376 - def MigrateInstance(self, cluster_name, instance, target, live):
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
391 - def FinalizeMigrationSource(self, instance, success, live):
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
404 - def GetMigrationStatus(self, instance):
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
417 - def _InstanceStartupMemory(self, instance):
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
436 - def CheckParameterSyntax(cls, hvparams):
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 # cheap tests that run on the master, should not access the world 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
467 - def ValidateParameters(cls, hvparams):
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
488 - def PowercycleNode(cls, hvparams=None):
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
502 - def GetLinuxNodeInfo(meminfo="/proc/meminfo", cpuinfo="/proc/cpuinfo"):
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 # We assume that the node OS can access all the CPUs 562 result["cpu_dom0"] = cpu_total 563 # FIXME: export correct data here 564 result["cpu_nodes"] = 1 565 result["cpu_sockets"] = 1 566 567 return result
568 569 @classmethod
570 - def LinuxPowercycle(cls):
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
587 - def _FormatVerifyResults(msgs):
588 """Formats the verification results, given a list of errors. 589 590 @param msgs: list of errors, possibly empty 591 @return: overall problem description if something is wrong, 592 C{None} otherwise 593 594 """ 595 if msgs: 596 return "; ".join(msgs) 597 else: 598 return None
599 600 # pylint: disable=R0201,W0613
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 # pylint: disable=R0201,W0613
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 # pylint: disable=R0201,W0613
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 # pylint: disable=R0201,W0613
622 - def VerifyHotplugSupport(self, instance, action, dev_type):
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
639 - def HotplugSupported(self, instance):
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