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

Source Code for Module ganeti.hypervisor.hv_xen

   1  # 
   2  # 
   3   
   4  # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 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  """Xen hypervisors 
  32   
  33  """ 
  34   
  35  import logging 
  36  import errno 
  37  import string # pylint: disable=W0402 
  38  import shutil 
  39  import time 
  40  from cStringIO import StringIO 
  41   
  42  from ganeti import constants 
  43  from ganeti import errors 
  44  from ganeti import utils 
  45  from ganeti.hypervisor import hv_base 
  46  from ganeti import netutils 
  47  from ganeti import objects 
  48  from ganeti import pathutils 
  49   
  50   
  51  XEND_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xend-config.sxp") 
  52  XL_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xen/xl.conf") 
  53  VIF_BRIDGE_SCRIPT = utils.PathJoin(pathutils.XEN_CONFIG_DIR, 
  54                                     "scripts/vif-bridge") 
  55  _DOM0_NAME = "Domain-0" 
  56  _DISK_LETTERS = string.ascii_lowercase 
  57   
  58  _FILE_DRIVER_MAP = { 
  59    constants.FD_LOOP: "file", 
  60    constants.FD_BLKTAP: "tap:aio", 
  61    constants.FD_BLKTAP2: "tap2:tapdisk:aio", 
  62    } 
63 64 65 -def _CreateConfigCpus(cpu_mask):
66 """Create a CPU config string for Xen's config file. 67 68 """ 69 # Convert the string CPU mask to a list of list of int's 70 cpu_list = utils.ParseMultiCpuMask(cpu_mask) 71 72 if len(cpu_list) == 1: 73 all_cpu_mapping = cpu_list[0] 74 if all_cpu_mapping == constants.CPU_PINNING_OFF: 75 # If CPU pinning has 1 entry that's "all", then remove the 76 # parameter from the config file 77 return None 78 else: 79 # If CPU pinning has one non-all entry, mapping all vCPUS (the entire 80 # VM) to one physical CPU, using format 'cpu = "C"' 81 return "cpu = \"%s\"" % ",".join(map(str, all_cpu_mapping)) 82 else: 83 84 def _GetCPUMap(vcpu): 85 if vcpu[0] == constants.CPU_PINNING_ALL_VAL: 86 cpu_map = constants.CPU_PINNING_ALL_XEN 87 else: 88 cpu_map = ",".join(map(str, vcpu)) 89 return "\"%s\"" % cpu_map
90 91 # build the result string in format 'cpus = [ "c", "c", "c" ]', 92 # where each c is a physical CPU number, a range, a list, or any 93 # combination 94 return "cpus = [ %s ]" % ", ".join(map(_GetCPUMap, cpu_list)) 95
96 97 -def _RunInstanceList(fn, instance_list_errors):
98 """Helper function for L{_GetAllInstanceList} to retrieve the list 99 of instances from xen. 100 101 @type fn: callable 102 @param fn: Function to query xen for the list of instances 103 @type instance_list_errors: list 104 @param instance_list_errors: Error list 105 @rtype: list 106 107 """ 108 result = fn() 109 if result.failed: 110 logging.error("Retrieving the instance list from xen failed (%s): %s", 111 result.fail_reason, result.output) 112 instance_list_errors.append(result) 113 raise utils.RetryAgain() 114 115 # skip over the heading 116 return result.stdout.splitlines()
117
118 119 -class _InstanceCrashed(errors.GenericError):
120 """Instance has reached a violent ending. 121 122 This is raised within the Xen hypervisor only, and should not be seen or used 123 outside. 124 125 """
126
127 128 -def _ParseInstanceList(lines, include_node):
129 """Parses the output of listing instances by xen. 130 131 @type lines: list 132 @param lines: Result of retrieving the instance list from xen 133 @type include_node: boolean 134 @param include_node: If True, return information for Dom0 135 @return: list of tuple containing (name, id, memory, vcpus, state, time 136 spent) 137 138 """ 139 result = [] 140 141 # Iterate through all lines while ignoring header 142 for line in lines[1:]: 143 # The format of lines is: 144 # Name ID Mem(MiB) VCPUs State Time(s) 145 # Domain-0 0 3418 4 r----- 266.2 146 data = line.split() 147 if len(data) != 6: 148 raise errors.HypervisorError("Can't parse instance list," 149 " line: %s" % line) 150 try: 151 data[1] = int(data[1]) 152 data[2] = int(data[2]) 153 data[3] = int(data[3]) 154 data[4] = _XenToHypervisorInstanceState(data[4]) 155 data[5] = float(data[5]) 156 except (TypeError, ValueError), err: 157 raise errors.HypervisorError("Can't parse instance list," 158 " line: %s, error: %s" % (line, err)) 159 except _InstanceCrashed: 160 # The crashed instance can be interpreted as being down, so we omit it 161 # from the instance list. 162 continue 163 164 # skip the Domain-0 (optional) 165 if include_node or data[0] != _DOM0_NAME: 166 result.append(data) 167 168 return result
169
170 171 -def _GetAllInstanceList(fn, include_node, delays, timeout):
172 """Return the list of instances including running and shutdown. 173 174 See L{_RunInstanceList} and L{_ParseInstanceList} for parameter details. 175 176 """ 177 instance_list_errors = [] 178 try: 179 lines = utils.Retry(_RunInstanceList, delays, timeout, 180 args=(fn, instance_list_errors)) 181 except utils.RetryTimeout: 182 if instance_list_errors: 183 instance_list_result = instance_list_errors.pop() 184 185 errmsg = ("listing instances failed, timeout exceeded (%s): %s" % 186 (instance_list_result.fail_reason, instance_list_result.output)) 187 else: 188 errmsg = "listing instances failed" 189 190 raise errors.HypervisorError(errmsg) 191 192 return _ParseInstanceList(lines, include_node)
193
194 195 -def _IsInstanceRunning(instance_info):
196 """Determine whether an instance is running. 197 198 An instance is running if it is in the following Xen states: 199 running, blocked, paused, or dying (about to be destroyed / shutdown). 200 201 For some strange reason, Xen once printed 'rb----' which does not make any 202 sense because an instance cannot be both running and blocked. Fortunately, 203 for Ganeti 'running' or 'blocked' is the same as 'running'. 204 205 A state of nothing '------' means that the domain is runnable but it is not 206 currently running. That means it is in the queue behind other domains waiting 207 to be scheduled to run. 208 http://old-list-archives.xenproject.org/xen-users/2007-06/msg00849.html 209 210 A dying instance is about to be removed, but it is still consuming resources, 211 and counts as running. 212 213 @type instance_info: string 214 @param instance_info: Information about instance, as supplied by Xen. 215 @rtype: bool 216 @return: Whether an instance is running. 217 218 """ 219 allowable_running_prefixes = [ 220 "r--", 221 "rb-", 222 "-b-", 223 "---", 224 ] 225 226 def _RunningWithSuffix(suffix): 227 return map(lambda x: x + suffix, allowable_running_prefixes)
228 229 # The shutdown suspend ("ss") state is encountered during migration, where 230 # the instance is still considered to be running. 231 # The shutdown restart ("sr") is probably encountered during restarts - still 232 # running. 233 # See Xen commit e1475a6693aac8cddc4bdd456548aa05a625556b 234 return instance_info in _RunningWithSuffix("---") \ 235 or instance_info in _RunningWithSuffix("ss-") \ 236 or instance_info in _RunningWithSuffix("sr-") \ 237 or instance_info == "-----d" 238
239 240 -def _IsInstanceShutdown(instance_info):
241 """Determine whether the instance is shutdown. 242 243 An instance is shutdown when a user shuts it down from within, and we do not 244 remove domains to be able to detect that. 245 246 The dying state has been added as a precaution, as Xen's status reporting is 247 weird. 248 249 """ 250 return instance_info == "---s--" \ 251 or instance_info == "---s-d"
252
253 254 -def _IgnorePaused(instance_info):
255 """Removes information about whether a Xen state is paused from the state. 256 257 As it turns out, an instance can be reported as paused in almost any 258 condition. Paused instances can be paused, running instances can be paused for 259 scheduling, and any other condition can appear to be paused as a result of 260 races or improbable conditions in Xen's status reporting. 261 As we do not use Xen's pause commands in any way at the time, we can simply 262 ignore the paused field and save ourselves a lot of trouble. 263 264 Should we ever use the pause commands, several samples would be needed before 265 we could confirm the domain as paused. 266 267 """ 268 return instance_info.replace('p', '-')
269
270 271 -def _IsCrashed(instance_info):
272 """Returns whether an instance is in the crashed Xen state. 273 274 When a horrible misconfiguration happens to a Xen domain, it can crash, 275 meaning that it encounters a violent ending. While this state usually flashes 276 only temporarily before the domain is restarted, being able to check for it 277 allows Ganeti not to act confused and do something about it. 278 279 """ 280 return instance_info.count('c') > 0
281
282 283 -def _XenToHypervisorInstanceState(instance_info):
284 """Maps Xen states to hypervisor states. 285 286 @type instance_info: string 287 @param instance_info: Information about instance, as supplied by Xen. 288 @rtype: L{hv_base.HvInstanceState} 289 290 """ 291 instance_info = _IgnorePaused(instance_info) 292 293 if _IsCrashed(instance_info): 294 raise _InstanceCrashed("Instance detected as crashed, should be omitted") 295 296 if _IsInstanceRunning(instance_info): 297 return hv_base.HvInstanceState.RUNNING 298 elif _IsInstanceShutdown(instance_info): 299 return hv_base.HvInstanceState.SHUTDOWN 300 else: 301 raise errors.HypervisorError("hv_xen._XenToHypervisorInstanceState:" 302 " unhandled Xen instance state '%s'" % 303 instance_info)
304
305 306 -def _GetRunningInstanceList(fn, include_node, delays, timeout):
307 """Return the list of running instances. 308 309 See L{_GetAllInstanceList} for parameter details. 310 311 """ 312 instances = _GetAllInstanceList(fn, include_node, delays, timeout) 313 return [i for i in instances if hv_base.HvInstanceState.IsRunning(i[4])]
314
315 316 -def _GetShutdownInstanceList(fn, include_node, delays, timeout):
317 """Return the list of shutdown instances. 318 319 See L{_GetAllInstanceList} for parameter details. 320 321 """ 322 instances = _GetAllInstanceList(fn, include_node, delays, timeout) 323 return [i for i in instances if hv_base.HvInstanceState.IsShutdown(i[4])]
324
325 326 -def _ParseNodeInfo(info):
327 """Return information about the node. 328 329 @return: a dict with the following keys (memory values in MiB): 330 - memory_total: the total memory size on the node 331 - memory_free: the available memory on the node for instances 332 - nr_cpus: total number of CPUs 333 - nr_nodes: in a NUMA system, the number of domains 334 - nr_sockets: the number of physical CPU sockets in the node 335 - hv_version: the hypervisor version in the form (major, minor) 336 337 """ 338 result = {} 339 cores_per_socket = threads_per_core = nr_cpus = None 340 xen_major, xen_minor = None, None 341 memory_total = None 342 memory_free = None 343 344 for line in info.splitlines(): 345 fields = line.split(":", 1) 346 347 if len(fields) < 2: 348 continue 349 350 (key, val) = map(lambda s: s.strip(), fields) 351 352 # Note: in Xen 3, memory has changed to total_memory 353 if key in ("memory", "total_memory"): 354 memory_total = int(val) 355 elif key == "free_memory": 356 memory_free = int(val) 357 elif key == "nr_cpus": 358 nr_cpus = result["cpu_total"] = int(val) 359 elif key == "nr_nodes": 360 result["cpu_nodes"] = int(val) 361 elif key == "cores_per_socket": 362 cores_per_socket = int(val) 363 elif key == "threads_per_core": 364 threads_per_core = int(val) 365 elif key == "xen_major": 366 xen_major = int(val) 367 elif key == "xen_minor": 368 xen_minor = int(val) 369 370 if None not in [cores_per_socket, threads_per_core, nr_cpus]: 371 result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core) 372 373 if memory_free is not None: 374 result["memory_free"] = memory_free 375 376 if memory_total is not None: 377 result["memory_total"] = memory_total 378 379 if not (xen_major is None or xen_minor is None): 380 result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor) 381 382 return result
383
384 385 -def _MergeInstanceInfo(info, instance_list):
386 """Updates node information from L{_ParseNodeInfo} with instance info. 387 388 @type info: dict 389 @param info: Result from L{_ParseNodeInfo} 390 @type instance_list: list of tuples 391 @param instance_list: list of instance information; one tuple per instance 392 @rtype: dict 393 394 """ 395 total_instmem = 0 396 397 for (name, _, mem, vcpus, _, _) in instance_list: 398 if name == _DOM0_NAME: 399 info["memory_dom0"] = mem 400 info["cpu_dom0"] = vcpus 401 402 # Include Dom0 in total memory usage 403 total_instmem += mem 404 405 memory_free = info.get("memory_free") 406 memory_total = info.get("memory_total") 407 408 # Calculate memory used by hypervisor 409 if None not in [memory_total, memory_free, total_instmem]: 410 info["memory_hv"] = memory_total - memory_free - total_instmem 411 412 return info
413
414 415 -def _GetNodeInfo(info, instance_list):
416 """Combines L{_MergeInstanceInfo} and L{_ParseNodeInfo}. 417 418 @type instance_list: list of tuples 419 @param instance_list: list of instance information; one tuple per instance 420 421 """ 422 return _MergeInstanceInfo(_ParseNodeInfo(info), instance_list)
423
424 425 -def _GetConfigFileDiskData(block_devices, blockdev_prefix, 426 _letters=_DISK_LETTERS):
427 """Get disk directives for Xen config file. 428 429 This method builds the xen config disk directive according to the 430 given disk_template and block_devices. 431 432 @param block_devices: list of tuples (cfdev, rldev): 433 - cfdev: dict containing ganeti config disk part 434 - rldev: ganeti.block.bdev.BlockDev object 435 @param blockdev_prefix: a string containing blockdevice prefix, 436 e.g. "sd" for /dev/sda 437 438 @return: string containing disk directive for xen instance config file 439 440 """ 441 if len(block_devices) > len(_letters): 442 raise errors.HypervisorError("Too many disks") 443 444 disk_data = [] 445 446 for sd_suffix, (cfdev, dev_path, _) in zip(_letters, block_devices): 447 sd_name = blockdev_prefix + sd_suffix 448 449 if cfdev.mode == constants.DISK_RDWR: 450 mode = "w" 451 else: 452 mode = "r" 453 454 if cfdev.dev_type in constants.DTS_FILEBASED: 455 driver = _FILE_DRIVER_MAP[cfdev.logical_id[0]] 456 else: 457 driver = "phy" 458 459 disk_data.append("'%s:%s,%s,%s'" % (driver, dev_path, sd_name, mode)) 460 461 return disk_data
462
463 464 -def _QuoteCpuidField(data):
465 """Add quotes around the CPUID field only if necessary. 466 467 Xen CPUID fields come in two shapes: LIBXL strings, which need quotes around 468 them, and lists of XEND strings, which don't. 469 470 @param data: Either type of parameter. 471 @return: The quoted version thereof. 472 473 """ 474 return "'%s'" % data if data.startswith("host") else data
475
476 477 -def _ConfigureNIC(instance, seq, nic, tap):
478 """Run the network configuration script for a specified NIC 479 480 See L{hv_base.ConfigureNIC}. 481 482 @type instance: instance object 483 @param instance: instance we're acting on 484 @type seq: int 485 @param seq: nic sequence number 486 @type nic: nic object 487 @param nic: nic we're acting on 488 @type tap: str 489 @param tap: the host's tap interface this NIC corresponds to 490 491 """ 492 hv_base.ConfigureNIC(pathutils.XEN_IFUP_OS, instance, seq, nic, tap)
493
494 495 -class XenHypervisor(hv_base.BaseHypervisor):
496 """Xen generic hypervisor interface 497 498 This is the Xen base class used for both Xen PVM and HVM. It contains 499 all the functionality that is identical for both. 500 501 """ 502 CAN_MIGRATE = True 503 REBOOT_RETRY_COUNT = 60 504 REBOOT_RETRY_INTERVAL = 10 505 _ROOT_DIR = pathutils.RUN_DIR + "/xen-hypervisor" 506 # contains NICs' info 507 _NICS_DIR = _ROOT_DIR + "/nic" 508 # contains the pidfiles of socat processes used to migrate instaces under xl 509 _MIGRATION_DIR = _ROOT_DIR + "/migration" 510 _DIRS = [_ROOT_DIR, _NICS_DIR, _MIGRATION_DIR] 511 512 _INSTANCE_LIST_DELAYS = (0.3, 1.5, 1.0) 513 _INSTANCE_LIST_TIMEOUT = 5 514 515 ANCILLARY_FILES = [ 516 XEND_CONFIG_FILE, 517 XL_CONFIG_FILE, 518 VIF_BRIDGE_SCRIPT, 519 ] 520 ANCILLARY_FILES_OPT = [ 521 XEND_CONFIG_FILE, 522 XL_CONFIG_FILE, 523 ] 524
525 - def __init__(self, _cfgdir=None, _run_cmd_fn=None, _cmd=None):
526 hv_base.BaseHypervisor.__init__(self) 527 528 if _cfgdir is None: 529 self._cfgdir = pathutils.XEN_CONFIG_DIR 530 else: 531 self._cfgdir = _cfgdir 532 533 if _run_cmd_fn is None: 534 self._run_cmd_fn = utils.RunCmd 535 else: 536 self._run_cmd_fn = _run_cmd_fn 537 538 self._cmd = _cmd
539 540 @staticmethod
541 - def _GetCommandFromHvparams(hvparams):
542 """Returns the Xen command extracted from the given hvparams. 543 544 @type hvparams: dict of strings 545 @param hvparams: hypervisor parameters 546 547 """ 548 if hvparams is None or constants.HV_XEN_CMD not in hvparams: 549 raise errors.HypervisorError("Cannot determine xen command.") 550 else: 551 return hvparams[constants.HV_XEN_CMD]
552
553 - def _GetCommand(self, hvparams):
554 """Returns Xen command to use. 555 556 @type hvparams: dict of strings 557 @param hvparams: hypervisor parameters 558 559 """ 560 if self._cmd is None: 561 cmd = XenHypervisor._GetCommandFromHvparams(hvparams) 562 else: 563 cmd = self._cmd 564 565 if cmd not in constants.KNOWN_XEN_COMMANDS: 566 raise errors.ProgrammerError("Unknown Xen command '%s'" % cmd) 567 568 return cmd
569
570 - def _RunXen(self, args, hvparams, timeout=None):
571 """Wrapper around L{utils.process.RunCmd} to run Xen command. 572 573 @type hvparams: dict of strings 574 @param hvparams: dictionary of hypervisor params 575 @type timeout: int or None 576 @param timeout: if a timeout (in seconds) is specified, the command will be 577 terminated after that number of seconds. 578 @see: L{utils.process.RunCmd} 579 580 """ 581 cmd = [] 582 583 if timeout is not None: 584 cmd.extend(["timeout", str(timeout)]) 585 586 cmd.extend([self._GetCommand(hvparams)]) 587 cmd.extend(args) 588 589 return self._run_cmd_fn(cmd)
590
591 - def _ConfigFileName(self, instance_name):
592 """Get the config file name for an instance. 593 594 @param instance_name: instance name 595 @type instance_name: str 596 @return: fully qualified path to instance config file 597 @rtype: str 598 599 """ 600 return utils.PathJoin(self._cfgdir, instance_name)
601 602 @classmethod
603 - def _EnsureDirs(cls, extra_dirs=None):
604 """Makes sure that the directories needed by the hypervisor exist. 605 606 @type extra_dirs: list of string or None 607 @param extra_dirs: Additional directories which ought to exist. 608 609 """ 610 if extra_dirs is None: 611 extra_dirs = [] 612 dirs = [(dname, constants.RUN_DIRS_MODE) for dname in 613 (cls._DIRS + extra_dirs)] 614 utils.EnsureDirs(dirs)
615 616 @classmethod
617 - def _WriteNICInfoFile(cls, instance, idx, nic):
618 """Write the Xen config file for the instance. 619 620 This version of the function just writes the config file from static data. 621 622 """ 623 instance_name = instance.name 624 cls._EnsureDirs(extra_dirs=[cls._InstanceNICDir(instance_name)]) 625 626 cfg_file = cls._InstanceNICFile(instance_name, idx) 627 data = StringIO() 628 629 data.write("TAGS=\"%s\"\n" % r"\ ".join(instance.GetTags())) 630 if nic.netinfo: 631 netinfo = objects.Network.FromDict(nic.netinfo) 632 for k, v in netinfo.HooksDict().iteritems(): 633 data.write("%s=\"%s\"\n" % (k, v)) 634 635 data.write("MAC=%s\n" % nic.mac) 636 if nic.ip: 637 data.write("IP=%s\n" % nic.ip) 638 data.write("INTERFACE_INDEX=%s\n" % str(idx)) 639 if nic.name: 640 data.write("INTERFACE_NAME=%s\n" % nic.name) 641 data.write("INTERFACE_UUID=%s\n" % nic.uuid) 642 data.write("MODE=%s\n" % nic.nicparams[constants.NIC_MODE]) 643 data.write("LINK=%s\n" % nic.nicparams[constants.NIC_LINK]) 644 data.write("VLAN=%s\n" % nic.nicparams[constants.NIC_VLAN]) 645 646 try: 647 utils.WriteFile(cfg_file, data=data.getvalue()) 648 except EnvironmentError, err: 649 raise errors.HypervisorError("Cannot write Xen instance configuration" 650 " file %s: %s" % (cfg_file, err))
651 652 @staticmethod
653 - def VersionsSafeForMigration(src, target):
654 """Decide if migration is likely to suceed for hypervisor versions. 655 656 Given two versions of a hypervisor, give a guess whether live migration 657 from the one version to the other version is likely to succeed. For Xen, 658 the heuristics is, that an increase by one on the second digit is OK. This 659 fits with the current numbering scheme. 660 661 @type src: list or tuple 662 @type target: list or tuple 663 @rtype: bool 664 """ 665 if src == target: 666 return True 667 668 if len(src) < 2 or len(target) < 2: 669 return False 670 671 return src[0] == target[0] and target[1] in [src[1], src[1] + 1]
672 673 @classmethod
674 - def _InstanceNICDir(cls, instance_name):
675 """Returns the directory holding the tap device files for a given instance. 676 677 """ 678 return utils.PathJoin(cls._NICS_DIR, instance_name)
679 680 @classmethod
681 - def _InstanceNICFile(cls, instance_name, seq):
682 """Returns the name of the file containing the tap device for a given NIC 683 684 """ 685 return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
686 687 @classmethod
688 - def _InstanceMigrationPidfile(cls, _instance_name):
689 """Returns the name of the pid file for a socat process used to migrate. 690 691 """ 692 #TODO(riba): At the moment, we are using a single pidfile because we 693 # use a single port for migrations at the moment. This is because we do not 694 # allow more migrations, so dynamic port selection and the needed port 695 # modifications are not needed. 696 # The _instance_name parameter has been left here for future use. 697 return utils.PathJoin(cls._MIGRATION_DIR, constants.XL_MIGRATION_PIDFILE)
698 699 @classmethod
700 - def _GetConfig(cls, instance, startup_memory, block_devices):
701 """Build Xen configuration for an instance. 702 703 """ 704 raise NotImplementedError
705
706 - def _WriteNicConfig(self, config, instance, hvp):
707 vif_data = [] 708 709 # only XenHvmHypervisor has these hvparams 710 nic_type = hvp.get(constants.HV_NIC_TYPE, None) 711 vif_type = hvp.get(constants.HV_VIF_TYPE, None) 712 nic_type_str = "" 713 if nic_type or vif_type: 714 if nic_type is None: 715 if vif_type: 716 nic_type_str = ", type=%s" % vif_type 717 elif nic_type == constants.HT_NIC_PARAVIRTUAL: 718 nic_type_str = ", type=paravirtualized" 719 else: 720 # parameter 'model' is only valid with type 'ioemu' 721 nic_type_str = ", model=%s, type=%s" % \ 722 (nic_type, constants.HT_HVM_VIF_IOEMU) 723 724 for idx, nic in enumerate(instance.nics): 725 nic_args = {} 726 nic_args["mac"] = "%s%s" % (nic.mac, nic_type_str) 727 728 if nic.name and \ 729 nic.name.startswith(constants.INSTANCE_COMMUNICATION_NIC_PREFIX): 730 tap = hv_base.GenerateTapName() 731 nic_args["vifname"] = tap 732 nic_args["script"] = pathutils.XEN_VIF_METAD_SETUP 733 nic.name = tap 734 else: 735 ip = getattr(nic, "ip", None) 736 if ip is not None: 737 nic_args["ip"] = ip 738 739 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED: 740 nic_args["bridge"] = nic.nicparams[constants.NIC_LINK] 741 elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_OVS: 742 nic_args["bridge"] = nic.nicparams[constants.NIC_LINK] 743 if nic.nicparams[constants.NIC_VLAN]: 744 nic_args["bridge"] += nic.nicparams[constants.NIC_VLAN] 745 746 if hvp[constants.HV_VIF_SCRIPT]: 747 nic_args["script"] = hvp[constants.HV_VIF_SCRIPT] 748 749 nic_str = ", ".join(["%s=%s" % p for p in nic_args.items()]) 750 vif_data.append("'%s'" % (nic_str, )) 751 self._WriteNICInfoFile(instance, idx, nic) 752 753 config.write("vif = [%s]\n" % ",".join(vif_data))
754
755 - def _WriteConfigFile(self, instance_name, data):
756 """Write the Xen config file for the instance. 757 758 This version of the function just writes the config file from static data. 759 760 """ 761 # just in case it exists 762 utils.RemoveFile(utils.PathJoin(self._cfgdir, "auto", instance_name)) 763 764 cfg_file = self._ConfigFileName(instance_name) 765 try: 766 utils.WriteFile(cfg_file, data=data) 767 except EnvironmentError, err: 768 raise errors.HypervisorError("Cannot write Xen instance configuration" 769 " file %s: %s" % (cfg_file, err))
770
771 - def _ReadConfigFile(self, instance_name):
772 """Returns the contents of the instance config file. 773 774 """ 775 filename = self._ConfigFileName(instance_name) 776 777 try: 778 file_content = utils.ReadFile(filename) 779 except EnvironmentError, err: 780 raise errors.HypervisorError("Failed to load Xen config file: %s" % err) 781 782 return file_content
783
784 - def _RemoveConfigFile(self, instance_name):
785 """Remove the xen configuration file. 786 787 """ 788 utils.RemoveFile(self._ConfigFileName(instance_name)) 789 try: 790 shutil.rmtree(self._InstanceNICDir(instance_name)) 791 except OSError, err: 792 if err.errno != errno.ENOENT: 793 raise
794
795 - def _StashConfigFile(self, instance_name):
796 """Move the Xen config file to the log directory and return its new path. 797 798 """ 799 old_filename = self._ConfigFileName(instance_name) 800 base = ("%s-%s" % 801 (instance_name, utils.TimestampForFilename())) 802 new_filename = utils.PathJoin(pathutils.LOG_XEN_DIR, base) 803 utils.RenameFile(old_filename, new_filename) 804 return new_filename
805
806 - def _GetInstanceList(self, include_node, hvparams):
807 """Wrapper around module level L{_GetAllInstanceList}. 808 809 @type hvparams: dict of strings 810 @param hvparams: hypervisor parameters to be used on this node 811 812 """ 813 return _GetAllInstanceList(lambda: self._RunXen(["list"], hvparams), 814 include_node, delays=self._INSTANCE_LIST_DELAYS, 815 timeout=self._INSTANCE_LIST_TIMEOUT)
816
817 - def ListInstances(self, hvparams=None):
818 """Get the list of running instances. 819 820 @type hvparams: dict of strings 821 @param hvparams: the instance's hypervisor params 822 823 @rtype: list of strings 824 @return: names of running instances 825 826 """ 827 instance_list = _GetRunningInstanceList( 828 lambda: self._RunXen(["list"], hvparams), 829 False, delays=self._INSTANCE_LIST_DELAYS, 830 timeout=self._INSTANCE_LIST_TIMEOUT) 831 return [info[0] for info in instance_list]
832
833 - def GetInstanceInfo(self, instance_name, hvparams=None):
834 """Get instance properties. 835 836 @type instance_name: string 837 @param instance_name: the instance name 838 @type hvparams: dict of strings 839 @param hvparams: the instance's hypervisor params 840 841 @return: tuple (name, id, memory, vcpus, stat, times) 842 843 """ 844 instance_list = self._GetInstanceList(instance_name == _DOM0_NAME, hvparams) 845 result = None 846 for data in instance_list: 847 if data[0] == instance_name: 848 result = data 849 break 850 return result
851
852 - def GetAllInstancesInfo(self, hvparams=None):
853 """Get properties of all instances. 854 855 @type hvparams: dict of strings 856 @param hvparams: hypervisor parameters 857 858 @rtype: (string, string, int, int, HypervisorInstanceState, int) 859 @return: list of tuples (name, id, memory, vcpus, state, times) 860 861 """ 862 return self._GetInstanceList(False, hvparams)
863
864 - def _MakeConfigFile(self, instance, startup_memory, block_devices):
865 """Gather configuration details and write to disk. 866 867 See L{_GetConfig} for arguments. 868 869 """ 870 buf = StringIO() 871 buf.write("# Automatically generated by Ganeti. Do not edit!\n") 872 buf.write("\n") 873 buf.write(self._GetConfig(instance, startup_memory, block_devices)) 874 buf.write("\n") 875 876 self._WriteConfigFile(instance.name, buf.getvalue())
877
878 - def StartInstance(self, instance, block_devices, startup_paused):
879 """Start an instance. 880 881 """ 882 startup_memory = self._InstanceStartupMemory(instance) 883 884 self._MakeConfigFile(instance, startup_memory, block_devices) 885 886 cmd = ["create"] 887 if startup_paused: 888 cmd.append("-p") 889 cmd.append(self._ConfigFileName(instance.name)) 890 891 result = self._RunXen(cmd, instance.hvparams) 892 if result.failed: 893 # Move the Xen configuration file to the log directory to avoid 894 # leaving a stale config file behind. 895 stashed_config = self._StashConfigFile(instance.name) 896 raise errors.HypervisorError("Failed to start instance %s: %s (%s). Moved" 897 " config file to %s" % 898 (instance.name, result.fail_reason, 899 result.output, stashed_config)) 900 901 for nic_seq, nic in enumerate(instance.nics): 902 if nic.name and nic.name.startswith("gnt.com."): 903 _ConfigureNIC(instance, nic_seq, nic, nic.name)
904
905 - def StopInstance(self, instance, force=False, retry=False, name=None, 906 timeout=None):
907 """Stop an instance. 908 909 A soft shutdown can be interrupted. A hard shutdown tries forever. 910 911 """ 912 assert(timeout is None or force is not None) 913 914 if name is None: 915 name = instance.name 916 917 return self._StopInstance(name, force, instance.hvparams, timeout)
918
919 - def _ShutdownInstance(self, name, hvparams, timeout):
920 """Shutdown an instance if the instance is running. 921 922 The '-w' flag waits for shutdown to complete which avoids the need 923 to poll in the case where we want to destroy the domain 924 immediately after shutdown. 925 926 @type name: string 927 @param name: name of the instance to stop 928 @type hvparams: dict of string 929 @param hvparams: hypervisor parameters of the instance 930 @type timeout: int or None 931 @param timeout: a timeout after which the shutdown command should be killed, 932 or None for no timeout 933 934 """ 935 instance_info = self.GetInstanceInfo(name, hvparams=hvparams) 936 937 if instance_info is None or _IsInstanceShutdown(instance_info[4]): 938 logging.info("Failed to shutdown instance %s, not running", name) 939 return None 940 941 return self._RunXen(["shutdown", "-w", name], hvparams, timeout)
942
943 - def _DestroyInstance(self, name, hvparams):
944 """Destroy an instance if the instance if the instance exists. 945 946 @type name: string 947 @param name: name of the instance to destroy 948 @type hvparams: dict of string 949 @param hvparams: hypervisor parameters of the instance 950 951 """ 952 instance_info = self.GetInstanceInfo(name, hvparams=hvparams) 953 954 if instance_info is None: 955 logging.info("Failed to destroy instance %s, does not exist", name) 956 return None 957 958 return self._RunXen(["destroy", name], hvparams)
959 960 # Destroy a domain only if necessary 961 # 962 # This method checks if the domain has already been destroyed before 963 # issuing the 'destroy' command. This step is necessary to handle 964 # domains created by other versions of Ganeti. For example, an 965 # instance created with 2.10 will be destroy by the 966 # '_ShutdownInstance', thus not requiring an additional destroy, 967 # which would cause an error if issued. See issue 619.
968 - def _DestroyInstanceIfAlive(self, name, hvparams):
969 instance_info = self.GetInstanceInfo(name, hvparams=hvparams) 970 971 if instance_info is None: 972 raise errors.HypervisorError("Failed to destroy instance %s, already" 973 " destroyed" % name) 974 else: 975 self._DestroyInstance(name, hvparams)
976
977 - def _StopInstance(self, name, force, hvparams, timeout):
978 """Stop an instance. 979 980 @type name: string 981 @param name: name of the instance to destroy 982 983 @type force: boolean 984 @param force: whether to do a "hard" stop (destroy) 985 986 @type hvparams: dict of string 987 @param hvparams: hypervisor parameters of the instance 988 989 @type timeout: int or None 990 @param timeout: a timeout after which the shutdown command should be killed, 991 or None for no timeout 992 993 """ 994 instance_info = self.GetInstanceInfo(name, hvparams=hvparams) 995 996 if instance_info is None: 997 raise errors.HypervisorError("Failed to shutdown instance %s," 998 " not running" % name) 999 1000 if not force: 1001 self._ShutdownInstance(name, hvparams, timeout) 1002 1003 # TODO: Xen does always destroy the instnace after trying a gracefull 1004 # shutdown. That means doing another attempt with force=True will not make 1005 # any difference. This differs in behaviour from other hypervisors and 1006 # should be cleaned up. 1007 result = self._DestroyInstanceIfAlive(name, hvparams) 1008 if result is not None and result.failed and \ 1009 self.GetInstanceInfo(name, hvparams=hvparams) is not None: 1010 raise errors.HypervisorError("Failed to stop instance %s: %s, %s" % 1011 (name, result.fail_reason, result.output)) 1012 1013 # Remove configuration file if stopping/starting instance was successful 1014 self._RemoveConfigFile(name)
1015
1016 - def RebootInstance(self, instance):
1017 """Reboot an instance. 1018 1019 """ 1020 ini_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams) 1021 1022 if ini_info is None: 1023 raise errors.HypervisorError("Failed to reboot instance %s," 1024 " not running" % instance.name) 1025 1026 result = self._RunXen(["reboot", instance.name], instance.hvparams) 1027 if result.failed: 1028 raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" % 1029 (instance.name, result.fail_reason, 1030 result.output)) 1031 1032 def _CheckInstance(): 1033 new_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams) 1034 1035 # check if the domain ID has changed or the run time has decreased 1036 if (new_info is not None and 1037 (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])): 1038 return 1039 1040 raise utils.RetryAgain()
1041 1042 try: 1043 utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL, 1044 self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT) 1045 except utils.RetryTimeout: 1046 raise errors.HypervisorError("Failed to reboot instance %s: instance" 1047 " did not reboot in the expected interval" % 1048 (instance.name, ))
1049
1050 - def BalloonInstanceMemory(self, instance, mem):
1051 """Balloon an instance memory to a certain value. 1052 1053 @type instance: L{objects.Instance} 1054 @param instance: instance to be accepted 1055 @type mem: int 1056 @param mem: actual memory size to use for instance runtime 1057 1058 """ 1059 result = self._RunXen(["mem-set", instance.name, mem], instance.hvparams) 1060 if result.failed: 1061 raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" % 1062 (instance.name, result.fail_reason, 1063 result.output)) 1064 1065 # Update configuration file 1066 cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem] 1067 cmd.append(self._ConfigFileName(instance.name)) 1068 1069 result = utils.RunCmd(cmd) 1070 if result.failed: 1071 raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" % 1072 (instance.name, result.fail_reason, 1073 result.output))
1074
1075 - def GetNodeInfo(self, hvparams=None):
1076 """Return information about the node. 1077 1078 @see: L{_GetNodeInfo} and L{_ParseNodeInfo} 1079 1080 """ 1081 result = self._RunXen(["info"], hvparams) 1082 if result.failed: 1083 logging.error("Can't retrieve xen hypervisor information (%s): %s", 1084 result.fail_reason, result.output) 1085 return None 1086 1087 instance_list = self._GetInstanceList(True, hvparams) 1088 return _GetNodeInfo(result.stdout, instance_list)
1089 1090 @classmethod
1091 - def GetInstanceConsole(cls, instance, primary_node, node_group, 1092 hvparams, beparams):
1093 """Return a command for connecting to the console of an instance. 1094 1095 """ 1096 xen_cmd = XenHypervisor._GetCommandFromHvparams(hvparams) 1097 ndparams = node_group.FillND(primary_node) 1098 return objects.InstanceConsole(instance=instance.name, 1099 kind=constants.CONS_SSH, 1100 host=primary_node.name, 1101 port=ndparams.get(constants.ND_SSH_PORT), 1102 user=constants.SSH_CONSOLE_USER, 1103 command=[pathutils.XEN_CONSOLE_WRAPPER, 1104 xen_cmd, instance.name])
1105
1106 - def Verify(self, hvparams=None):
1107 """Verify the hypervisor. 1108 1109 For Xen, this verifies that the xend process is running. 1110 1111 @type hvparams: dict of strings 1112 @param hvparams: hypervisor parameters to be verified against 1113 1114 @return: Problem description if something is wrong, C{None} otherwise 1115 1116 """ 1117 if hvparams is None: 1118 return "Could not verify the hypervisor, because no hvparams were" \ 1119 " provided." 1120 1121 if constants.HV_XEN_CMD in hvparams: 1122 xen_cmd = hvparams[constants.HV_XEN_CMD] 1123 try: 1124 self._CheckToolstack(xen_cmd) 1125 except errors.HypervisorError: 1126 return "The configured xen toolstack '%s' is not available on this" \ 1127 " node." % xen_cmd 1128 1129 result = self._RunXen(["info"], hvparams) 1130 if result.failed: 1131 return "Retrieving information from xen failed: %s, %s" % \ 1132 (result.fail_reason, result.output) 1133 1134 return None
1135
1136 - def MigrationInfo(self, instance):
1137 """Get instance information to perform a migration. 1138 1139 @type instance: L{objects.Instance} 1140 @param instance: instance to be migrated 1141 @rtype: string 1142 @return: content of the xen config file 1143 1144 """ 1145 return self._ReadConfigFile(instance.name)
1146
1147 - def _UseMigrationDaemon(self, hvparams):
1148 """Whether to start a socat daemon when accepting an instance. 1149 1150 @rtype: bool 1151 1152 """ 1153 return self._GetCommand(hvparams) == constants.XEN_CMD_XL
1154 1155 @classmethod
1156 - def _KillMigrationDaemon(cls, instance):
1157 """Kills the migration daemon if present. 1158 1159 """ 1160 pidfile = cls._InstanceMigrationPidfile(instance.name) 1161 read_pid = utils.ReadPidFile(pidfile) 1162 1163 # There is no pidfile, hence nothing for us to do 1164 if read_pid == 0: 1165 return 1166 1167 if utils.IsProcessAlive(read_pid): 1168 # If the process is alive, let's make sure we are killing the right one 1169 cmdline = ' '.join(utils.GetProcCmdline(read_pid)) 1170 if cmdline.count("xl migrate-receive") > 0: 1171 utils.KillProcess(read_pid) 1172 1173 # By this point the process is not running, whether killed or initially 1174 # nonexistent, so it is safe to remove the pidfile. 1175 utils.RemoveFile(pidfile)
1176
1177 - def AcceptInstance(self, instance, info, target):
1178 """Prepare to accept an instance. 1179 1180 @type instance: L{objects.Instance} 1181 @param instance: instance to be accepted 1182 @type info: string 1183 @param info: content of the xen config file on the source node 1184 @type target: string 1185 @param target: target host (usually ip), on this node 1186 1187 """ 1188 if self._UseMigrationDaemon(instance.hvparams): 1189 port = instance.hvparams[constants.HV_MIGRATION_PORT] 1190 1191 # Make sure there is somewhere to put the pidfile. 1192 XenHypervisor._EnsureDirs() 1193 pidfile = XenHypervisor._InstanceMigrationPidfile(instance.name) 1194 1195 # And try and kill a previous daemon 1196 XenHypervisor._KillMigrationDaemon(instance) 1197 1198 listening_arg = "TCP-LISTEN:%d,bind=%s,reuseaddr" % (port, target) 1199 socat_pid = utils.StartDaemon(["socat", "-b524288", listening_arg, 1200 "SYSTEM:'xl migrate-receive'"], 1201 pidfile=pidfile) 1202 1203 # Wait for a while to make sure the socat process has successfully started 1204 # listening 1205 time.sleep(1) 1206 if not utils.IsProcessAlive(socat_pid): 1207 raise errors.HypervisorError("Could not start receiving socat process" 1208 " on port %d: check if port is available" % 1209 port)
1210
1211 - def FinalizeMigrationDst(self, instance, info, success):
1212 """Finalize an instance migration. 1213 1214 After a successful migration we write the xen config file. 1215 We do nothing on a failure, as we did not change anything at accept time. 1216 1217 @type instance: L{objects.Instance} 1218 @param instance: instance whose migration is being finalized 1219 @type info: string 1220 @param info: content of the xen config file on the source node 1221 @type success: boolean 1222 @param success: whether the migration was a success or a failure 1223 1224 """ 1225 if success: 1226 self._WriteConfigFile(instance.name, info) 1227 elif self._UseMigrationDaemon(instance.hvparams): 1228 XenHypervisor._KillMigrationDaemon(instance)
1229
1230 - def MigrateInstance(self, _cluster_name, instance, target, live):
1231 """Migrate an instance to a target node. 1232 1233 The migration will not be attempted if the instance is not 1234 currently running. 1235 1236 @type instance: L{objects.Instance} 1237 @param instance: the instance to be migrated 1238 @type target: string 1239 @param target: ip address of the target node 1240 @type live: boolean 1241 @param live: perform a live migration 1242 1243 """ 1244 port = instance.hvparams[constants.HV_MIGRATION_PORT] 1245 1246 return self._MigrateInstance(instance.name, target, port, live, 1247 instance.hvparams)
1248
1249 - def _MigrateInstance(self, instance_name, target, port, live, hvparams, 1250 _ping_fn=netutils.TcpPing):
1251 """Migrate an instance to a target node. 1252 1253 @see: L{MigrateInstance} for details 1254 1255 """ 1256 if hvparams is None: 1257 raise errors.HypervisorError("No hvparams provided.") 1258 1259 if self.GetInstanceInfo(instance_name, hvparams=hvparams) is None: 1260 raise errors.HypervisorError("Instance not running, cannot migrate") 1261 1262 cmd = self._GetCommand(hvparams) 1263 1264 args = ["migrate"] 1265 1266 if cmd == constants.XEN_CMD_XM: 1267 # Try and see if xm is listening on the specified port 1268 if not _ping_fn(target, port, live_port_needed=True): 1269 raise errors.HypervisorError("Remote host %s not listening on port" 1270 " %s, cannot migrate" % (target, port)) 1271 1272 args.extend(["-p", "%d" % port]) 1273 if live: 1274 args.append("-l") 1275 1276 elif cmd == constants.XEN_CMD_XL: 1277 # Rather than using SSH, use socat as Ganeti cannot guarantee the presence 1278 # of usable SSH keys as of 2.13 1279 args.extend([ 1280 "-s", constants.XL_SOCAT_CMD % (target, port), 1281 "-C", self._ConfigFileName(instance_name), 1282 ]) 1283 1284 else: 1285 raise errors.HypervisorError("Unsupported Xen command: %s" % cmd) 1286 1287 args.extend([instance_name, target]) 1288 1289 result = self._RunXen(args, hvparams) 1290 if result.failed: 1291 raise errors.HypervisorError("Failed to migrate instance %s: %s" % 1292 (instance_name, result.output))
1293
1294 - def FinalizeMigrationSource(self, instance, success, live):
1295 """Finalize the instance migration on the source node. 1296 1297 @type instance: L{objects.Instance} 1298 @param instance: the instance that was migrated 1299 @type success: bool 1300 @param success: whether the migration succeeded or not 1301 @type live: bool 1302 @param live: whether the user requested a live migration or not 1303 1304 """ 1305 # pylint: disable=W0613 1306 if success: 1307 # remove old xen file after migration succeeded 1308 try: 1309 self._RemoveConfigFile(instance.name) 1310 except EnvironmentError: 1311 logging.exception("Failure while removing instance config file")
1312
1313 - def GetMigrationStatus(self, instance):
1314 """Get the migration status 1315 1316 As MigrateInstance for Xen is still blocking, if this method is called it 1317 means that MigrateInstance has completed successfully. So we can safely 1318 assume that the migration was successful and notify this fact to the client. 1319 1320 @type instance: L{objects.Instance} 1321 @param instance: the instance that is being migrated 1322 @rtype: L{objects.MigrationStatus} 1323 @return: the status of the current migration (one of 1324 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional 1325 progress info that can be retrieved from the hypervisor 1326 1327 """ 1328 return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
1329
1330 - def PowercycleNode(self, hvparams=None):
1331 """Xen-specific powercycle. 1332 1333 This first does a Linux reboot (which triggers automatically a Xen 1334 reboot), and if that fails it tries to do a Xen reboot. The reason 1335 we don't try a Xen reboot first is that the xen reboot launches an 1336 external command which connects to the Xen hypervisor, and that 1337 won't work in case the root filesystem is broken and/or the xend 1338 daemon is not working. 1339 1340 @type hvparams: dict of strings 1341 @param hvparams: hypervisor params to be used on this node 1342 1343 """ 1344 try: 1345 self.LinuxPowercycle() 1346 finally: 1347 xen_cmd = self._GetCommand(hvparams) 1348 utils.RunCmd([xen_cmd, "debug", "R"])
1349
1350 - def _CheckToolstack(self, xen_cmd):
1351 """Check whether the given toolstack is available on the node. 1352 1353 @type xen_cmd: string 1354 @param xen_cmd: xen command (e.g. 'xm' or 'xl') 1355 1356 """ 1357 binary_found = self._CheckToolstackBinary(xen_cmd) 1358 if not binary_found: 1359 raise errors.HypervisorError("No '%s' binary found on node." % xen_cmd) 1360 elif xen_cmd == constants.XEN_CMD_XL: 1361 if not self._CheckToolstackXlConfigured(): 1362 raise errors.HypervisorError("Toolstack '%s' is not enabled on this" 1363 "node." % xen_cmd)
1364
1365 - def _CheckToolstackBinary(self, xen_cmd):
1366 """Checks whether the xen command's binary is found on the machine. 1367 1368 """ 1369 if xen_cmd not in constants.KNOWN_XEN_COMMANDS: 1370 raise errors.HypervisorError("Unknown xen command '%s'." % xen_cmd) 1371 result = self._run_cmd_fn(["which", xen_cmd]) 1372 return not result.failed
1373
1374 - def _CheckToolstackXlConfigured(self):
1375 """Checks whether xl is enabled on an xl-capable node. 1376 1377 @rtype: bool 1378 @returns: C{True} if 'xl' is enabled, C{False} otherwise 1379 1380 """ 1381 result = self._run_cmd_fn([constants.XEN_CMD_XL, "help"]) 1382 if not result.failed: 1383 return True 1384 elif result.failed: 1385 if "toolstack" in result.stderr: 1386 return False 1387 # xl fails for some other reason than the toolstack 1388 else: 1389 raise errors.HypervisorError("Cannot run xen ('%s'). Error: %s." 1390 % (constants.XEN_CMD_XL, result.stderr))
1391
1392 1393 -def WriteXenConfigEvents(config, hvp):
1394 config.write("on_poweroff = 'preserve'\n") 1395 if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED: 1396 config.write("on_reboot = 'restart'\n") 1397 else: 1398 config.write("on_reboot = 'destroy'\n") 1399 config.write("on_crash = 'restart'\n")
1400
1401 1402 -class XenPvmHypervisor(XenHypervisor):
1403 """Xen PVM hypervisor interface""" 1404 1405 PARAMETERS = { 1406 constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK, 1407 constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK, 1408 constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK, 1409 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK, 1410 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK, 1411 constants.HV_ROOT_PATH: hv_base.NO_CHECK, 1412 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK, 1413 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK, 1414 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK, 1415 # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar). 1416 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK, 1417 constants.HV_REBOOT_BEHAVIOR: 1418 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS), 1419 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK, 1420 constants.HV_CPU_CAP: hv_base.OPT_NONNEGATIVE_INT_CHECK, 1421 constants.HV_CPU_WEIGHT: 1422 (False, lambda x: 0 < x < 65536, "invalid weight", None, None), 1423 constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK, 1424 constants.HV_XEN_CMD: 1425 hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS), 1426 constants.HV_XEN_CPUID: hv_base.NO_CHECK, 1427 constants.HV_SOUNDHW: hv_base.NO_CHECK, 1428 } 1429
1430 - def _GetConfig(self, instance, startup_memory, block_devices):
1431 """Write the Xen config file for the instance. 1432 1433 """ 1434 hvp = instance.hvparams 1435 config = StringIO() 1436 config.write("# this is autogenerated by Ganeti, please do not edit\n#\n") 1437 1438 # if bootloader is True, use bootloader instead of kernel and ramdisk 1439 # parameters. 1440 if hvp[constants.HV_USE_BOOTLOADER]: 1441 # bootloader handling 1442 bootloader_path = hvp[constants.HV_BOOTLOADER_PATH] 1443 if bootloader_path: 1444 config.write("bootloader = '%s'\n" % bootloader_path) 1445 else: 1446 raise errors.HypervisorError("Bootloader enabled, but missing" 1447 " bootloader path") 1448 1449 bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS] 1450 if bootloader_args: 1451 config.write("bootargs = '%s'\n" % bootloader_args) 1452 else: 1453 # kernel handling 1454 kpath = hvp[constants.HV_KERNEL_PATH] 1455 config.write("kernel = '%s'\n" % kpath) 1456 1457 # initrd handling 1458 initrd_path = hvp[constants.HV_INITRD_PATH] 1459 if initrd_path: 1460 config.write("ramdisk = '%s'\n" % initrd_path) 1461 1462 # rest of the settings 1463 config.write("memory = %d\n" % startup_memory) 1464 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM]) 1465 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS]) 1466 cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK]) 1467 if cpu_pinning: 1468 config.write("%s\n" % cpu_pinning) 1469 cpu_cap = hvp[constants.HV_CPU_CAP] 1470 if cpu_cap: 1471 config.write("cpu_cap=%d\n" % cpu_cap) 1472 cpu_weight = hvp[constants.HV_CPU_WEIGHT] 1473 if cpu_weight: 1474 config.write("cpu_weight=%d\n" % cpu_weight) 1475 1476 config.write("name = '%s'\n" % instance.name) 1477 1478 self._WriteNicConfig(config, instance, hvp) 1479 1480 disk_data = \ 1481 _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX]) 1482 config.write("disk = [%s]\n" % ",".join(disk_data)) 1483 1484 if hvp[constants.HV_ROOT_PATH]: 1485 config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH]) 1486 1487 WriteXenConfigEvents(config, hvp) 1488 config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS]) 1489 1490 cpuid = hvp[constants.HV_XEN_CPUID] 1491 if cpuid: 1492 config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid)) 1493 1494 if hvp[constants.HV_SOUNDHW]: 1495 config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW]) 1496 1497 return config.getvalue()
1498
1499 1500 -class XenHvmHypervisor(XenHypervisor):
1501 """Xen HVM hypervisor interface""" 1502 1503 ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [ 1504 pathutils.VNC_PASSWORD_FILE, 1505 ] 1506 ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [ 1507 pathutils.VNC_PASSWORD_FILE, 1508 ] 1509 1510 PARAMETERS = { 1511 constants.HV_ACPI: hv_base.NO_CHECK, 1512 constants.HV_BOOT_ORDER: (True, ) + 1513 (lambda x: x and len(x.strip("acdn")) == 0, 1514 "Invalid boot order specified, must be one or more of [acdn]", 1515 None, None), 1516 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK, 1517 constants.HV_DISK_TYPE: 1518 hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES), 1519 constants.HV_NIC_TYPE: 1520 hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES), 1521 constants.HV_PAE: hv_base.NO_CHECK, 1522 constants.HV_VNC_BIND_ADDRESS: 1523 (False, netutils.IP4Address.IsValid, 1524 "VNC bind address is not a valid IP address", None, None), 1525 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK, 1526 constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK, 1527 constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK, 1528 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK, 1529 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK, 1530 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK, 1531 # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar). 1532 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK, 1533 # Add PCI passthrough 1534 constants.HV_PASSTHROUGH: hv_base.NO_CHECK, 1535 constants.HV_REBOOT_BEHAVIOR: 1536 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS), 1537 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK, 1538 constants.HV_CPU_CAP: hv_base.NO_CHECK, 1539 constants.HV_CPU_WEIGHT: 1540 (False, lambda x: 0 < x < 65535, "invalid weight", None, None), 1541 constants.HV_VIF_TYPE: 1542 hv_base.ParamInSet(False, constants.HT_HVM_VALID_VIF_TYPES), 1543 constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK, 1544 constants.HV_VIRIDIAN: hv_base.NO_CHECK, 1545 constants.HV_XEN_CMD: 1546 hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS), 1547 constants.HV_XEN_CPUID: hv_base.NO_CHECK, 1548 constants.HV_SOUNDHW: hv_base.NO_CHECK, 1549 } 1550
1551 - def _GetConfig(self, instance, startup_memory, block_devices):
1552 """Create a Xen 3.1 HVM config file. 1553 1554 """ 1555 hvp = instance.hvparams 1556 1557 config = StringIO() 1558 1559 # kernel handling 1560 kpath = hvp[constants.HV_KERNEL_PATH] 1561 config.write("kernel = '%s'\n" % kpath) 1562 1563 config.write("builder = 'hvm'\n") 1564 config.write("memory = %d\n" % startup_memory) 1565 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM]) 1566 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS]) 1567 cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK]) 1568 if cpu_pinning: 1569 config.write("%s\n" % cpu_pinning) 1570 cpu_cap = hvp[constants.HV_CPU_CAP] 1571 if cpu_cap: 1572 config.write("cpu_cap=%d\n" % cpu_cap) 1573 cpu_weight = hvp[constants.HV_CPU_WEIGHT] 1574 if cpu_weight: 1575 config.write("cpu_weight=%d\n" % cpu_weight) 1576 1577 config.write("name = '%s'\n" % instance.name) 1578 if hvp[constants.HV_PAE]: 1579 config.write("pae = 1\n") 1580 else: 1581 config.write("pae = 0\n") 1582 if hvp[constants.HV_ACPI]: 1583 config.write("acpi = 1\n") 1584 else: 1585 config.write("acpi = 0\n") 1586 if hvp[constants.HV_VIRIDIAN]: 1587 config.write("viridian = 1\n") 1588 else: 1589 config.write("viridian = 0\n") 1590 1591 config.write("apic = 1\n") 1592 config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL]) 1593 config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER]) 1594 config.write("sdl = 0\n") 1595 config.write("usb = 1\n") 1596 config.write("usbdevice = 'tablet'\n") 1597 config.write("vnc = 1\n") 1598 if hvp[constants.HV_VNC_BIND_ADDRESS] is None: 1599 config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS) 1600 else: 1601 config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS]) 1602 1603 if instance.network_port > constants.VNC_BASE_PORT: 1604 display = instance.network_port - constants.VNC_BASE_PORT 1605 config.write("vncdisplay = %s\n" % display) 1606 config.write("vncunused = 0\n") 1607 else: 1608 config.write("# vncdisplay = 1\n") 1609 config.write("vncunused = 1\n") 1610 1611 vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE] 1612 try: 1613 password = utils.ReadFile(vnc_pwd_file) 1614 except EnvironmentError, err: 1615 raise errors.HypervisorError("Failed to open VNC password file %s: %s" % 1616 (vnc_pwd_file, err)) 1617 1618 config.write("vncpasswd = '%s'\n" % password.rstrip()) 1619 1620 config.write("serial = 'pty'\n") 1621 if hvp[constants.HV_USE_LOCALTIME]: 1622 config.write("localtime = 1\n") 1623 1624 self._WriteNicConfig(config, instance, hvp) 1625 1626 disk_data = \ 1627 _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX]) 1628 1629 iso_path = hvp[constants.HV_CDROM_IMAGE_PATH] 1630 if iso_path: 1631 iso = "'file:%s,hdc:cdrom,r'" % iso_path 1632 disk_data.append(iso) 1633 1634 config.write("disk = [%s]\n" % (",".join(disk_data))) 1635 # Add PCI passthrough 1636 pci_pass_arr = [] 1637 pci_pass = hvp[constants.HV_PASSTHROUGH] 1638 if pci_pass: 1639 pci_pass_arr = pci_pass.split(";") 1640 config.write("pci = %s\n" % pci_pass_arr) 1641 1642 WriteXenConfigEvents(config, hvp) 1643 1644 cpuid = hvp[constants.HV_XEN_CPUID] 1645 if cpuid: 1646 config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid)) 1647 1648 if hvp[constants.HV_SOUNDHW]: 1649 config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW]) 1650 1651 return config.getvalue()
1652