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