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 BaseHypervisor(object):
157 """Abstract virtualisation technology interface 158 159 The goal is that all aspects of the virtualisation technology are 160 abstracted away from the rest of code. 161 162 @cvar PARAMETERS: a dict of parameter name: check type; the check type is 163 a five-tuple containing: 164 - the required flag (boolean) 165 - a function to check for syntax, that will be used in 166 L{CheckParameterSyntax}, in the master daemon process 167 - an error message for the above function 168 - a function to check for parameter validity on the remote node, 169 in the L{ValidateParameters} function 170 - an error message for the above function 171 @type CAN_MIGRATE: boolean 172 @cvar CAN_MIGRATE: whether this hypervisor can do migration (either 173 live or non-live) 174 175 """ 176 PARAMETERS = {} 177 ANCILLARY_FILES = [] 178 ANCILLARY_FILES_OPT = [] 179 CAN_MIGRATE = False 180
181 - def StartInstance(self, instance, block_devices, startup_paused):
182 """Start an instance.""" 183 raise NotImplementedError
184
185 - def StopInstance(self, instance, force=False, retry=False, name=None, 186 timeout=None):
187 """Stop an instance 188 189 @type instance: L{objects.Instance} 190 @param instance: instance to stop 191 @type force: boolean 192 @param force: whether to do a "hard" stop (destroy) 193 @type retry: boolean 194 @param retry: whether this is just a retry call 195 @type name: string or None 196 @param name: if this parameter is passed, the the instance object 197 should not be used (will be passed as None), and the shutdown 198 must be done by name only 199 @type timeout: int or None 200 @param timeout: if the parameter is not None, a soft shutdown operation will 201 be killed after the specified number of seconds. A hard (forced) 202 shutdown cannot have a timeout 203 204 """ 205 raise NotImplementedError
206
207 - def CleanupInstance(self, instance_name):
208 """Cleanup after a stopped instance 209 210 This is an optional method, used by hypervisors that need to cleanup after 211 an instance has been stopped. 212 213 @type instance_name: string 214 @param instance_name: instance name to cleanup after 215 216 """ 217 pass
218
219 - def RebootInstance(self, instance):
220 """Reboot an instance.""" 221 raise NotImplementedError
222
223 - def ListInstances(self, hvparams=None):
224 """Get the list of running instances.""" 225 raise NotImplementedError
226
227 - def GetInstanceInfo(self, instance_name, hvparams=None):
228 """Get instance properties. 229 230 @type instance_name: string 231 @param instance_name: the instance name 232 @type hvparams: dict of strings 233 @param hvparams: hvparams to be used with this instance 234 235 @return: tuple (name, id, memory, vcpus, state, times) 236 237 """ 238 raise NotImplementedError
239
240 - def GetAllInstancesInfo(self, hvparams=None):
241 """Get properties of all instances. 242 243 @type hvparams: dict of strings 244 @param hvparams: hypervisor parameter 245 @return: list of tuples (name, id, memory, vcpus, stat, times) 246 247 """ 248 raise NotImplementedError
249
250 - def GetNodeInfo(self, hvparams=None):
251 """Return information about the node. 252 253 @type hvparams: dict of strings 254 @param hvparams: hypervisor parameters 255 256 @return: a dict with at least the following keys (memory values in MiB): 257 - memory_total: the total memory size on the node 258 - memory_free: the available memory on the node for instances 259 - memory_dom0: the memory used by the node itself, if available 260 - cpu_total: total number of CPUs 261 - cpu_dom0: number of CPUs used by the node OS 262 - cpu_nodes: number of NUMA domains 263 - cpu_sockets: number of physical CPU sockets 264 265 """ 266 raise NotImplementedError
267 268 @classmethod
269 - def GetInstanceConsole(cls, instance, primary_node, hvparams, beparams):
270 """Return information for connecting to the console of an instance. 271 272 """ 273 raise NotImplementedError
274 275 @classmethod
276 - def GetAncillaryFiles(cls):
277 """Return a list of ancillary files to be copied to all nodes as ancillary 278 configuration files. 279 280 @rtype: (list of absolute paths, list of absolute paths) 281 @return: (all files, optional files) 282 283 """ 284 # By default we return a member variable, so that if an hypervisor has just 285 # a static list of files it doesn't have to override this function. 286 assert set(cls.ANCILLARY_FILES).issuperset(cls.ANCILLARY_FILES_OPT), \ 287 "Optional ancillary files must be a subset of ancillary files" 288 289 return (cls.ANCILLARY_FILES, cls.ANCILLARY_FILES_OPT)
290
291 - def Verify(self, hvparams=None):
292 """Verify the hypervisor. 293 294 @type hvparams: dict of strings 295 @param hvparams: hypervisor parameters to be verified against 296 297 @return: Problem description if something is wrong, C{None} otherwise 298 299 """ 300 raise NotImplementedError
301
302 - def MigrationInfo(self, instance): # pylint: disable=R0201,W0613
303 """Get instance information to perform a migration. 304 305 By default assume no information is needed. 306 307 @type instance: L{objects.Instance} 308 @param instance: instance to be migrated 309 @rtype: string/data (opaque) 310 @return: instance migration information - serialized form 311 312 """ 313 return ""
314
315 - def AcceptInstance(self, instance, info, target):
316 """Prepare to accept an instance. 317 318 By default assume no preparation is needed. 319 320 @type instance: L{objects.Instance} 321 @param instance: instance to be accepted 322 @type info: string/data (opaque) 323 @param info: migration information, from the source node 324 @type target: string 325 @param target: target host (usually ip), on this node 326 327 """ 328 pass
329
330 - def BalloonInstanceMemory(self, instance, mem):
331 """Balloon an instance memory to a certain value. 332 333 @type instance: L{objects.Instance} 334 @param instance: instance to be accepted 335 @type mem: int 336 @param mem: actual memory size to use for instance runtime 337 338 """ 339 raise NotImplementedError
340
341 - def FinalizeMigrationDst(self, instance, info, success):
342 """Finalize the instance migration on the target node. 343 344 Should finalize or revert any preparation done to accept the instance. 345 Since by default we do no preparation, we also don't have anything to do 346 347 @type instance: L{objects.Instance} 348 @param instance: instance whose migration is being finalized 349 @type info: string/data (opaque) 350 @param info: migration information, from the source node 351 @type success: boolean 352 @param success: whether the migration was a success or a failure 353 354 """ 355 pass
356
357 - def MigrateInstance(self, cluster_name, instance, target, live):
358 """Migrate an instance. 359 360 @type cluster_name: string 361 @param cluster_name: name of the cluster 362 @type instance: L{objects.Instance} 363 @param instance: the instance to be migrated 364 @type target: string 365 @param target: hostname (usually ip) of the target node 366 @type live: boolean 367 @param live: whether to do a live or non-live migration 368 369 """ 370 raise NotImplementedError
371
372 - def FinalizeMigrationSource(self, instance, success, live):
373 """Finalize the instance migration on the source node. 374 375 @type instance: L{objects.Instance} 376 @param instance: the instance that was migrated 377 @type success: bool 378 @param success: whether the migration succeeded or not 379 @type live: bool 380 @param live: whether the user requested a live migration or not 381 382 """ 383 pass
384
385 - def GetMigrationStatus(self, instance):
386 """Get the migration status 387 388 @type instance: L{objects.Instance} 389 @param instance: the instance that is being migrated 390 @rtype: L{objects.MigrationStatus} 391 @return: the status of the current migration (one of 392 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional 393 progress info that can be retrieved from the hypervisor 394 395 """ 396 raise NotImplementedError
397
398 - def _InstanceStartupMemory(self, instance):
399 """Get the correct startup memory for an instance 400 401 This function calculates how much memory an instance should be started 402 with, making sure it's a value between the minimum and the maximum memory, 403 but also trying to use no more than the current free memory on the node. 404 405 @type instance: L{objects.Instance} 406 @param instance: the instance that is being started 407 @rtype: integer 408 @return: memory the instance should be started with 409 410 """ 411 free_memory = self.GetNodeInfo(hvparams=instance.hvparams)["memory_free"] 412 max_start_mem = min(instance.beparams[constants.BE_MAXMEM], free_memory) 413 start_mem = max(instance.beparams[constants.BE_MINMEM], max_start_mem) 414 return start_mem
415 416 @classmethod
417 - def CheckParameterSyntax(cls, hvparams):
418 """Check the given parameters for validity. 419 420 This should check the passed set of parameters for 421 validity. Classes should extend, not replace, this function. 422 423 @type hvparams: dict 424 @param hvparams: dictionary with parameter names/value 425 @raise errors.HypervisorError: when a parameter is not valid 426 427 """ 428 for key in hvparams: 429 if key not in cls.PARAMETERS: 430 raise errors.HypervisorError("Parameter '%s' is not supported" % key) 431 432 # cheap tests that run on the master, should not access the world 433 for name, (required, check_fn, errstr, _, _) in cls.PARAMETERS.items(): 434 if name not in hvparams: 435 raise errors.HypervisorError("Parameter '%s' is missing" % name) 436 value = hvparams[name] 437 if not required and not value: 438 continue 439 if not value: 440 raise errors.HypervisorError("Parameter '%s' is required but" 441 " is currently not defined" % (name, )) 442 if check_fn is not None and not check_fn(value): 443 raise errors.HypervisorError("Parameter '%s' fails syntax" 444 " check: %s (current value: '%s')" % 445 (name, errstr, value))
446 447 @classmethod
448 - def ValidateParameters(cls, hvparams):
449 """Check the given parameters for validity. 450 451 This should check the passed set of parameters for 452 validity. Classes should extend, not replace, this function. 453 454 @type hvparams: dict 455 @param hvparams: dictionary with parameter names/value 456 @raise errors.HypervisorError: when a parameter is not valid 457 458 """ 459 for name, (required, _, _, check_fn, errstr) in cls.PARAMETERS.items(): 460 value = hvparams[name] 461 if not required and not value: 462 continue 463 if check_fn is not None and not check_fn(value): 464 raise errors.HypervisorError("Parameter '%s' fails" 465 " validation: %s (current value: '%s')" % 466 (name, errstr, value))
467 468 @classmethod
469 - def PowercycleNode(cls, hvparams=None):
470 """Hard powercycle a node using hypervisor specific methods. 471 472 This method should hard powercycle the node, using whatever 473 methods the hypervisor provides. Note that this means that all 474 instances running on the node must be stopped too. 475 476 @type hvparams: dict of strings 477 @param hvparams: hypervisor params to be used on this node 478 479 """ 480 raise NotImplementedError
481 482 @staticmethod
483 - def GetLinuxNodeInfo(meminfo="/proc/meminfo", cpuinfo="/proc/cpuinfo"):
484 """For linux systems, return actual OS information. 485 486 This is an abstraction for all non-hypervisor-based classes, where 487 the node actually sees all the memory and CPUs via the /proc 488 interface and standard commands. The other case if for example 489 xen, where you only see the hardware resources via xen-specific 490 tools. 491 492 @param meminfo: name of the file containing meminfo 493 @type meminfo: string 494 @param cpuinfo: name of the file containing cpuinfo 495 @type cpuinfo: string 496 @return: a dict with the following keys (values in MiB): 497 - memory_total: the total memory size on the node 498 - memory_free: the available memory on the node for instances 499 - memory_dom0: the memory used by the node itself, if available 500 - cpu_total: total number of CPUs 501 - cpu_dom0: number of CPUs used by the node OS 502 - cpu_nodes: number of NUMA domains 503 - cpu_sockets: number of physical CPU sockets 504 505 """ 506 try: 507 data = utils.ReadFile(meminfo).splitlines() 508 except EnvironmentError, err: 509 raise errors.HypervisorError("Failed to list node info: %s" % (err,)) 510 511 result = {} 512 sum_free = 0 513 try: 514 for line in data: 515 splitfields = line.split(":", 1) 516 517 if len(splitfields) > 1: 518 key = splitfields[0].strip() 519 val = splitfields[1].strip() 520 if key == "MemTotal": 521 result["memory_total"] = int(val.split()[0]) / 1024 522 elif key in ("MemFree", "Buffers", "Cached"): 523 sum_free += int(val.split()[0]) / 1024 524 elif key == "Active": 525 result["memory_dom0"] = int(val.split()[0]) / 1024 526 except (ValueError, TypeError), err: 527 raise errors.HypervisorError("Failed to compute memory usage: %s" % 528 (err,)) 529 result["memory_free"] = sum_free 530 531 cpu_total = 0 532 try: 533 fh = open(cpuinfo) 534 try: 535 cpu_total = len(re.findall(r"(?m)^processor\s*:\s*[0-9]+\s*$", 536 fh.read())) 537 finally: 538 fh.close() 539 except EnvironmentError, err: 540 raise errors.HypervisorError("Failed to list node info: %s" % (err,)) 541 result["cpu_total"] = cpu_total 542 # We assume that the node OS can access all the CPUs 543 result["cpu_dom0"] = cpu_total 544 # FIXME: export correct data here 545 result["cpu_nodes"] = 1 546 result["cpu_sockets"] = 1 547 548 return result
549 550 @classmethod
551 - def LinuxPowercycle(cls):
552 """Linux-specific powercycle method. 553 554 """ 555 try: 556 fd = os.open("/proc/sysrq-trigger", os.O_WRONLY) 557 try: 558 os.write(fd, "b") 559 finally: 560 fd.close() 561 except OSError: 562 logging.exception("Can't open the sysrq-trigger file") 563 result = utils.RunCmd(["reboot", "-n", "-f"]) 564 if not result: 565 logging.error("Can't run shutdown: %s", result.output)
566 567 @staticmethod
568 - def _FormatVerifyResults(msgs):
569 """Formats the verification results, given a list of errors. 570 571 @param msgs: list of errors, possibly empty 572 @return: overall problem description if something is wrong, 573 C{None} otherwise 574 575 """ 576 if msgs: 577 return "; ".join(msgs) 578 else: 579 return None
580 581 # pylint: disable=R0201,W0613
582 - def HotAddDevice(self, instance, dev_type, device, extra, seq):
583 """Hot-add a device. 584 585 """ 586 raise errors.HotplugError("Hotplug is not supported by this hypervisor")
587 588 # pylint: disable=R0201,W0613
589 - def HotDelDevice(self, instance, dev_type, device, extra, seq):
590 """Hot-del a device. 591 592 """ 593 raise errors.HotplugError("Hotplug is not supported by this hypervisor")
594 595 # pylint: disable=R0201,W0613
596 - def HotModDevice(self, instance, dev_type, device, extra, seq):
597 """Hot-mod a device. 598 599 """ 600 raise errors.HotplugError("Hotplug is not supported by this hypervisor")
601 602 # pylint: disable=R0201,W0613
603 - def VerifyHotplugSupport(self, instance, action, dev_type):
604 """Verifies that hotplug is supported. 605 606 Given the target device and hotplug action checks if hotplug is 607 actually supported. 608 609 @type instance: L{objects.Instance} 610 @param instance: the instance object 611 @type action: string 612 @param action: one of the supported hotplug commands 613 @type dev_type: string 614 @param dev_type: one of the supported device types to hotplug 615 @raise errors.HotplugError: if hotplugging is not supported 616 617 """ 618 raise errors.HotplugError("Hotplug is not supported.")
619
620 - def HotplugSupported(self, instance):
621 """Checks if hotplug is supported. 622 623 By default is not. Currently only KVM hypervisor supports it. 624 625 """ 626 raise errors.HotplugError("Hotplug is not supported by this hypervisor")
627