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  # 
   6  # This program is free software; you can redistribute it and/or modify 
   7  # it under the terms of the GNU General Public License as published by 
   8  # the Free Software Foundation; either version 2 of the License, or 
   9  # (at your option) any later version. 
  10  # 
  11  # This program is distributed in the hope that it will be useful, but 
  12  # WITHOUT ANY WARRANTY; without even the implied warranty of 
  13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
  14  # General Public License for more details. 
  15  # 
  16  # You should have received a copy of the GNU General Public License 
  17  # along with this program; if not, write to the Free Software 
  18  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
  19  # 02110-1301, USA. 
  20   
  21   
  22  """Xen hypervisors 
  23   
  24  """ 
  25   
  26  import logging 
  27  import errno 
  28  import string # pylint: disable=W0402 
  29  import shutil 
  30  from cStringIO import StringIO 
  31   
  32  from ganeti import constants 
  33  from ganeti import errors 
  34  from ganeti import utils 
  35  from ganeti.hypervisor import hv_base 
  36  from ganeti import netutils 
  37  from ganeti import objects 
  38  from ganeti import pathutils 
  39   
  40   
  41  XEND_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xend-config.sxp") 
  42  XL_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xen/xl.conf") 
  43  VIF_BRIDGE_SCRIPT = utils.PathJoin(pathutils.XEN_CONFIG_DIR, 
  44                                     "scripts/vif-bridge") 
  45  _DOM0_NAME = "Domain-0" 
  46  _DISK_LETTERS = string.ascii_lowercase 
  47   
  48  _FILE_DRIVER_MAP = { 
  49    constants.FD_LOOP: "file", 
  50    constants.FD_BLKTAP: "tap:aio", 
  51    constants.FD_BLKTAP2: "tap2:tapdisk:aio", 
  52    } 
53 54 55 -def _CreateConfigCpus(cpu_mask):
56 """Create a CPU config string for Xen's config file. 57 58 """ 59 # Convert the string CPU mask to a list of list of int's 60 cpu_list = utils.ParseMultiCpuMask(cpu_mask) 61 62 if len(cpu_list) == 1: 63 all_cpu_mapping = cpu_list[0] 64 if all_cpu_mapping == constants.CPU_PINNING_OFF: 65 # If CPU pinning has 1 entry that's "all", then remove the 66 # parameter from the config file 67 return None 68 else: 69 # If CPU pinning has one non-all entry, mapping all vCPUS (the entire 70 # VM) to one physical CPU, using format 'cpu = "C"' 71 return "cpu = \"%s\"" % ",".join(map(str, all_cpu_mapping)) 72 else: 73 74 def _GetCPUMap(vcpu): 75 if vcpu[0] == constants.CPU_PINNING_ALL_VAL: 76 cpu_map = constants.CPU_PINNING_ALL_XEN 77 else: 78 cpu_map = ",".join(map(str, vcpu)) 79 return "\"%s\"" % cpu_map
80 81 # build the result string in format 'cpus = [ "c", "c", "c" ]', 82 # where each c is a physical CPU number, a range, a list, or any 83 # combination 84 return "cpus = [ %s ]" % ", ".join(map(_GetCPUMap, cpu_list)) 85
86 87 -def _RunInstanceList(fn, instance_list_errors):
88 """Helper function for L{_GetInstanceList} to retrieve the list of instances 89 from xen. 90 91 @type fn: callable 92 @param fn: Function to query xen for the list of instances 93 @type instance_list_errors: list 94 @param instance_list_errors: Error list 95 @rtype: list 96 97 """ 98 result = fn() 99 if result.failed: 100 logging.error("Retrieving the instance list from xen failed (%s): %s", 101 result.fail_reason, result.output) 102 instance_list_errors.append(result) 103 raise utils.RetryAgain() 104 105 # skip over the heading 106 return result.stdout.splitlines()
107
108 109 -def _ParseInstanceList(lines, include_node):
110 """Parses the output of listing instances by xen. 111 112 @type lines: list 113 @param lines: Result of retrieving the instance list from xen 114 @type include_node: boolean 115 @param include_node: If True, return information for Dom0 116 @return: list of tuple containing (name, id, memory, vcpus, state, time 117 spent) 118 119 """ 120 result = [] 121 122 # Iterate through all lines while ignoring header 123 for line in lines[1:]: 124 # The format of lines is: 125 # Name ID Mem(MiB) VCPUs State Time(s) 126 # Domain-0 0 3418 4 r----- 266.2 127 data = line.split() 128 if len(data) != 6: 129 raise errors.HypervisorError("Can't parse instance list," 130 " line: %s" % line) 131 try: 132 data[1] = int(data[1]) 133 data[2] = int(data[2]) 134 data[3] = int(data[3]) 135 data[5] = float(data[5]) 136 except (TypeError, ValueError), err: 137 raise errors.HypervisorError("Can't parse instance list," 138 " line: %s, error: %s" % (line, err)) 139 140 # skip the Domain-0 (optional) 141 if include_node or data[0] != _DOM0_NAME: 142 result.append(data) 143 144 return result
145
146 147 -def _GetInstanceList(fn, include_node, _timeout=5):
148 """Return the list of running instances. 149 150 See L{_RunInstanceList} and L{_ParseInstanceList} for parameter details. 151 152 """ 153 instance_list_errors = [] 154 try: 155 lines = utils.Retry(_RunInstanceList, (0.3, 1.5, 1.0), _timeout, 156 args=(fn, instance_list_errors)) 157 except utils.RetryTimeout: 158 if instance_list_errors: 159 instance_list_result = instance_list_errors.pop() 160 161 errmsg = ("listing instances failed, timeout exceeded (%s): %s" % 162 (instance_list_result.fail_reason, instance_list_result.output)) 163 else: 164 errmsg = "listing instances failed" 165 166 raise errors.HypervisorError(errmsg) 167 168 return _ParseInstanceList(lines, include_node)
169
170 171 -def _IsInstanceRunning(instance_info):
172 return instance_info == "r-----" \ 173 or instance_info == "-b----"
174
175 176 -def _IsInstanceShutdown(instance_info):
177 return instance_info == "---s--"
178
179 180 -def _ParseNodeInfo(info):
181 """Return information about the node. 182 183 @return: a dict with the following keys (memory values in MiB): 184 - memory_total: the total memory size on the node 185 - memory_free: the available memory on the node for instances 186 - nr_cpus: total number of CPUs 187 - nr_nodes: in a NUMA system, the number of domains 188 - nr_sockets: the number of physical CPU sockets in the node 189 - hv_version: the hypervisor version in the form (major, minor) 190 191 """ 192 result = {} 193 cores_per_socket = threads_per_core = nr_cpus = None 194 xen_major, xen_minor = None, None 195 memory_total = None 196 memory_free = None 197 198 for line in info.splitlines(): 199 fields = line.split(":", 1) 200 201 if len(fields) < 2: 202 continue 203 204 (key, val) = map(lambda s: s.strip(), fields) 205 206 # Note: in Xen 3, memory has changed to total_memory 207 if key in ("memory", "total_memory"): 208 memory_total = int(val) 209 elif key == "free_memory": 210 memory_free = int(val) 211 elif key == "nr_cpus": 212 nr_cpus = result["cpu_total"] = int(val) 213 elif key == "nr_nodes": 214 result["cpu_nodes"] = int(val) 215 elif key == "cores_per_socket": 216 cores_per_socket = int(val) 217 elif key == "threads_per_core": 218 threads_per_core = int(val) 219 elif key == "xen_major": 220 xen_major = int(val) 221 elif key == "xen_minor": 222 xen_minor = int(val) 223 224 if None not in [cores_per_socket, threads_per_core, nr_cpus]: 225 result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core) 226 227 if memory_free is not None: 228 result["memory_free"] = memory_free 229 230 if memory_total is not None: 231 result["memory_total"] = memory_total 232 233 if not (xen_major is None or xen_minor is None): 234 result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor) 235 236 return result
237
238 239 -def _MergeInstanceInfo(info, instance_list):
240 """Updates node information from L{_ParseNodeInfo} with instance info. 241 242 @type info: dict 243 @param info: Result from L{_ParseNodeInfo} 244 @type instance_list: list of tuples 245 @param instance_list: list of instance information; one tuple per instance 246 @rtype: dict 247 248 """ 249 total_instmem = 0 250 251 for (name, _, mem, vcpus, _, _) in instance_list: 252 if name == _DOM0_NAME: 253 info["memory_dom0"] = mem 254 info["cpu_dom0"] = vcpus 255 256 # Include Dom0 in total memory usage 257 total_instmem += mem 258 259 memory_free = info.get("memory_free") 260 memory_total = info.get("memory_total") 261 262 # Calculate memory used by hypervisor 263 if None not in [memory_total, memory_free, total_instmem]: 264 info["memory_hv"] = memory_total - memory_free - total_instmem 265 266 return info
267
268 269 -def _GetNodeInfo(info, instance_list):
270 """Combines L{_MergeInstanceInfo} and L{_ParseNodeInfo}. 271 272 @type instance_list: list of tuples 273 @param instance_list: list of instance information; one tuple per instance 274 275 """ 276 return _MergeInstanceInfo(_ParseNodeInfo(info), instance_list)
277
278 279 -def _GetConfigFileDiskData(block_devices, blockdev_prefix, 280 _letters=_DISK_LETTERS):
281 """Get disk directives for Xen config file. 282 283 This method builds the xen config disk directive according to the 284 given disk_template and block_devices. 285 286 @param block_devices: list of tuples (cfdev, rldev): 287 - cfdev: dict containing ganeti config disk part 288 - rldev: ganeti.block.bdev.BlockDev object 289 @param blockdev_prefix: a string containing blockdevice prefix, 290 e.g. "sd" for /dev/sda 291 292 @return: string containing disk directive for xen instance config file 293 294 """ 295 if len(block_devices) > len(_letters): 296 raise errors.HypervisorError("Too many disks") 297 298 disk_data = [] 299 300 for sd_suffix, (cfdev, dev_path) in zip(_letters, block_devices): 301 sd_name = blockdev_prefix + sd_suffix 302 303 if cfdev.mode == constants.DISK_RDWR: 304 mode = "w" 305 else: 306 mode = "r" 307 308 if cfdev.dev_type in [constants.DT_FILE, constants.DT_SHARED_FILE]: 309 driver = _FILE_DRIVER_MAP[cfdev.physical_id[0]] 310 else: 311 driver = "phy" 312 313 disk_data.append("'%s:%s,%s,%s'" % (driver, dev_path, sd_name, mode)) 314 315 return disk_data
316
317 318 -class XenHypervisor(hv_base.BaseHypervisor):
319 """Xen generic hypervisor interface 320 321 This is the Xen base class used for both Xen PVM and HVM. It contains 322 all the functionality that is identical for both. 323 324 """ 325 CAN_MIGRATE = True 326 REBOOT_RETRY_COUNT = 60 327 REBOOT_RETRY_INTERVAL = 10 328 _ROOT_DIR = pathutils.RUN_DIR + "/xen-hypervisor" 329 _NICS_DIR = _ROOT_DIR + "/nic" # contains NICs' info 330 _DIRS = [_ROOT_DIR, _NICS_DIR] 331 332 ANCILLARY_FILES = [ 333 XEND_CONFIG_FILE, 334 XL_CONFIG_FILE, 335 VIF_BRIDGE_SCRIPT, 336 ] 337 ANCILLARY_FILES_OPT = [ 338 XL_CONFIG_FILE, 339 ] 340
341 - def __init__(self, _cfgdir=None, _run_cmd_fn=None, _cmd=None):
342 hv_base.BaseHypervisor.__init__(self) 343 344 if _cfgdir is None: 345 self._cfgdir = pathutils.XEN_CONFIG_DIR 346 else: 347 self._cfgdir = _cfgdir 348 349 if _run_cmd_fn is None: 350 self._run_cmd_fn = utils.RunCmd 351 else: 352 self._run_cmd_fn = _run_cmd_fn 353 354 self._cmd = _cmd
355 356 @staticmethod
357 - def _GetCommandFromHvparams(hvparams):
358 """Returns the Xen command extracted from the given hvparams. 359 360 @type hvparams: dict of strings 361 @param hvparams: hypervisor parameters 362 363 """ 364 if hvparams is None or constants.HV_XEN_CMD not in hvparams: 365 raise errors.HypervisorError("Cannot determine xen command.") 366 else: 367 return hvparams[constants.HV_XEN_CMD]
368
369 - def _GetCommand(self, hvparams):
370 """Returns Xen command to use. 371 372 @type hvparams: dict of strings 373 @param hvparams: hypervisor parameters 374 375 """ 376 if self._cmd is None: 377 cmd = XenHypervisor._GetCommandFromHvparams(hvparams) 378 else: 379 cmd = self._cmd 380 381 if cmd not in constants.KNOWN_XEN_COMMANDS: 382 raise errors.ProgrammerError("Unknown Xen command '%s'" % cmd) 383 384 return cmd
385
386 - def _RunXen(self, args, hvparams, timeout=None):
387 """Wrapper around L{utils.process.RunCmd} to run Xen command. 388 389 @type hvparams: dict of strings 390 @param hvparams: dictionary of hypervisor params 391 @type timeout: int or None 392 @param timeout: if a timeout (in seconds) is specified, the command will be 393 terminated after that number of seconds. 394 @see: L{utils.process.RunCmd} 395 396 """ 397 cmd = [] 398 399 if timeout is not None: 400 cmd.extend(["timeout", str(timeout)]) 401 402 cmd.extend([self._GetCommand(hvparams)]) 403 cmd.extend(args) 404 405 return self._run_cmd_fn(cmd)
406
407 - def _ConfigFileName(self, instance_name):
408 """Get the config file name for an instance. 409 410 @param instance_name: instance name 411 @type instance_name: str 412 @return: fully qualified path to instance config file 413 @rtype: str 414 415 """ 416 return utils.PathJoin(self._cfgdir, instance_name)
417 418 @classmethod
419 - def _WriteNICInfoFile(cls, instance_name, idx, nic):
420 """Write the Xen config file for the instance. 421 422 This version of the function just writes the config file from static data. 423 424 """ 425 dirs = [(dname, constants.RUN_DIRS_MODE) 426 for dname in cls._DIRS + [cls._InstanceNICDir(instance_name)]] 427 utils.EnsureDirs(dirs) 428 429 cfg_file = cls._InstanceNICFile(instance_name, idx) 430 data = StringIO() 431 432 if nic.netinfo: 433 netinfo = objects.Network.FromDict(nic.netinfo) 434 data.write("NETWORK_NAME=%s\n" % netinfo.name) 435 if netinfo.network: 436 data.write("NETWORK_SUBNET=%s\n" % netinfo.network) 437 if netinfo.gateway: 438 data.write("NETWORK_GATEWAY=%s\n" % netinfo.gateway) 439 if netinfo.network6: 440 data.write("NETWORK_SUBNET6=%s\n" % netinfo.network6) 441 if netinfo.gateway6: 442 data.write("NETWORK_GATEWAY6=%s\n" % netinfo.gateway6) 443 if netinfo.mac_prefix: 444 data.write("NETWORK_MAC_PREFIX=%s\n" % netinfo.mac_prefix) 445 if netinfo.tags: 446 data.write("NETWORK_TAGS=%s\n" % r"\ ".join(netinfo.tags)) 447 448 data.write("MAC=%s\n" % nic.mac) 449 data.write("IP=%s\n" % nic.ip) 450 data.write("MODE=%s\n" % nic.nicparams[constants.NIC_MODE]) 451 data.write("LINK=%s\n" % nic.nicparams[constants.NIC_LINK]) 452 453 try: 454 utils.WriteFile(cfg_file, data=data.getvalue()) 455 except EnvironmentError, err: 456 raise errors.HypervisorError("Cannot write Xen instance configuration" 457 " file %s: %s" % (cfg_file, err))
458 459 @classmethod
460 - def _InstanceNICDir(cls, instance_name):
461 """Returns the directory holding the tap device files for a given instance. 462 463 """ 464 return utils.PathJoin(cls._NICS_DIR, instance_name)
465 466 @classmethod
467 - def _InstanceNICFile(cls, instance_name, seq):
468 """Returns the name of the file containing the tap device for a given NIC 469 470 """ 471 return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
472 473 @classmethod
474 - def _GetConfig(cls, instance, startup_memory, block_devices):
475 """Build Xen configuration for an instance. 476 477 """ 478 raise NotImplementedError
479
480 - def _WriteConfigFile(self, instance_name, data):
481 """Write the Xen config file for the instance. 482 483 This version of the function just writes the config file from static data. 484 485 """ 486 # just in case it exists 487 utils.RemoveFile(utils.PathJoin(self._cfgdir, "auto", instance_name)) 488 489 cfg_file = self._ConfigFileName(instance_name) 490 try: 491 utils.WriteFile(cfg_file, data=data) 492 except EnvironmentError, err: 493 raise errors.HypervisorError("Cannot write Xen instance configuration" 494 " file %s: %s" % (cfg_file, err))
495
496 - def _ReadConfigFile(self, instance_name):
497 """Returns the contents of the instance config file. 498 499 """ 500 filename = self._ConfigFileName(instance_name) 501 502 try: 503 file_content = utils.ReadFile(filename) 504 except EnvironmentError, err: 505 raise errors.HypervisorError("Failed to load Xen config file: %s" % err) 506 507 return file_content
508
509 - def _RemoveConfigFile(self, instance_name):
510 """Remove the xen configuration file. 511 512 """ 513 utils.RemoveFile(self._ConfigFileName(instance_name)) 514 try: 515 shutil.rmtree(self._InstanceNICDir(instance_name)) 516 except OSError, err: 517 if err.errno != errno.ENOENT: 518 raise
519
520 - def _StashConfigFile(self, instance_name):
521 """Move the Xen config file to the log directory and return its new path. 522 523 """ 524 old_filename = self._ConfigFileName(instance_name) 525 base = ("%s-%s" % 526 (instance_name, utils.TimestampForFilename())) 527 new_filename = utils.PathJoin(pathutils.LOG_XEN_DIR, base) 528 utils.RenameFile(old_filename, new_filename) 529 return new_filename
530
531 - def _GetInstanceList(self, include_node, hvparams):
532 """Wrapper around module level L{_GetInstanceList}. 533 534 @type hvparams: dict of strings 535 @param hvparams: hypervisor parameters to be used on this node 536 537 """ 538 return _GetInstanceList(lambda: self._RunXen(["list"], hvparams), 539 include_node)
540
541 - def ListInstances(self, hvparams=None):
542 """Get the list of running instances. 543 544 """ 545 instance_list = self._GetInstanceList(False, hvparams) 546 names = [info[0] for info in instance_list] 547 return names
548
549 - def GetInstanceInfo(self, instance_name, hvparams=None):
550 """Get instance properties. 551 552 @type instance_name: string 553 @param instance_name: the instance name 554 @type hvparams: dict of strings 555 @param hvparams: the instance's hypervisor params 556 557 @return: tuple (name, id, memory, vcpus, stat, times) 558 559 """ 560 instance_list = self._GetInstanceList(instance_name == _DOM0_NAME, hvparams) 561 result = None 562 for data in instance_list: 563 if data[0] == instance_name: 564 result = data 565 break 566 return result
567
568 - def GetAllInstancesInfo(self, hvparams=None):
569 """Get properties of all instances. 570 571 @type hvparams: dict of strings 572 @param hvparams: hypervisor parameters 573 @return: list of tuples (name, id, memory, vcpus, stat, times) 574 575 """ 576 return self._GetInstanceList(False, hvparams)
577
578 - def _MakeConfigFile(self, instance, startup_memory, block_devices):
579 """Gather configuration details and write to disk. 580 581 See L{_GetConfig} for arguments. 582 583 """ 584 buf = StringIO() 585 buf.write("# Automatically generated by Ganeti. Do not edit!\n") 586 buf.write("\n") 587 buf.write(self._GetConfig(instance, startup_memory, block_devices)) 588 buf.write("\n") 589 590 self._WriteConfigFile(instance.name, buf.getvalue())
591
592 - def StartInstance(self, instance, block_devices, startup_paused):
593 """Start an instance. 594 595 """ 596 startup_memory = self._InstanceStartupMemory(instance) 597 598 self._MakeConfigFile(instance, startup_memory, block_devices) 599 600 cmd = ["create"] 601 if startup_paused: 602 cmd.append("-p") 603 cmd.append(self._ConfigFileName(instance.name)) 604 605 result = self._RunXen(cmd, instance.hvparams) 606 if result.failed: 607 # Move the Xen configuration file to the log directory to avoid 608 # leaving a stale config file behind. 609 stashed_config = self._StashConfigFile(instance.name) 610 raise errors.HypervisorError("Failed to start instance %s: %s (%s). Moved" 611 " config file to %s" % 612 (instance.name, result.fail_reason, 613 result.output, stashed_config))
614
615 - def StopInstance(self, instance, force=False, retry=False, name=None, 616 timeout=None):
617 """Stop an instance. 618 619 A soft shutdown can be interrupted. A hard shutdown tries forever. 620 621 """ 622 assert(timeout is None or force is not None) 623 624 if name is None: 625 name = instance.name 626 627 return self._StopInstance(name, force, instance.hvparams, timeout)
628
629 - def _ShutdownInstance(self, name, hvparams, timeout):
630 """Shutdown an instance if the instance is running. 631 632 The '-w' flag waits for shutdown to complete which avoids the need 633 to poll in the case where we want to destroy the domain 634 immediately after shutdown. 635 636 @type name: string 637 @param name: name of the instance to stop 638 @type hvparams: dict of string 639 @param hvparams: hypervisor parameters of the instance 640 @type timeout: int or None 641 @param timeout: a timeout after which the shutdown command should be killed, 642 or None for no timeout 643 644 """ 645 instance_info = self.GetInstanceInfo(name, hvparams=hvparams) 646 647 if instance_info is None or _IsInstanceShutdown(instance_info[4]): 648 logging.info("Failed to shutdown instance %s, not running", name) 649 return None 650 651 return self._RunXen(["shutdown", "-w", name], hvparams, timeout)
652
653 - def _DestroyInstance(self, name, hvparams):
654 """Destroy an instance if the instance if the instance exists. 655 656 @type name: string 657 @param name: name of the instance to destroy 658 @type hvparams: dict of string 659 @param hvparams: hypervisor parameters of the instance 660 661 """ 662 instance_info = self.GetInstanceInfo(name, hvparams=hvparams) 663 664 if instance_info is None: 665 logging.info("Failed to destroy instance %s, does not exist", name) 666 return None 667 668 return self._RunXen(["destroy", name], hvparams)
669
670 - def _StopInstance(self, name, force, hvparams, timeout):
671 """Stop an instance. 672 673 @type name: string 674 @param name: name of the instance to destroy 675 676 @type force: boolean 677 @param force: whether to do a "hard" stop (destroy) 678 679 @type hvparams: dict of string 680 @param hvparams: hypervisor parameters of the instance 681 682 @type timeout: int or None 683 @param timeout: a timeout after which the shutdown command should be killed, 684 or None for no timeout 685 686 """ 687 if force: 688 result = self._DestroyInstance(name, hvparams) 689 else: 690 self._ShutdownInstance(name, hvparams, timeout) 691 result = self._DestroyInstance(name, hvparams) 692 693 if result is not None and result.failed and \ 694 self.GetInstanceInfo(name, hvparams=hvparams) is not None: 695 raise errors.HypervisorError("Failed to stop instance %s: %s, %s" % 696 (name, result.fail_reason, result.output)) 697 698 # Remove configuration file if stopping/starting instance was successful 699 self._RemoveConfigFile(name)
700
701 - def RebootInstance(self, instance):
702 """Reboot an instance. 703 704 """ 705 ini_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams) 706 707 if ini_info is None: 708 raise errors.HypervisorError("Failed to reboot instance %s," 709 " not running" % instance.name) 710 711 result = self._RunXen(["reboot", instance.name], instance.hvparams) 712 if result.failed: 713 raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" % 714 (instance.name, result.fail_reason, 715 result.output)) 716 717 def _CheckInstance(): 718 new_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams) 719 720 # check if the domain ID has changed or the run time has decreased 721 if (new_info is not None and 722 (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])): 723 return 724 725 raise utils.RetryAgain()
726 727 try: 728 utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL, 729 self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT) 730 except utils.RetryTimeout: 731 raise errors.HypervisorError("Failed to reboot instance %s: instance" 732 " did not reboot in the expected interval" % 733 (instance.name, ))
734
735 - def BalloonInstanceMemory(self, instance, mem):
736 """Balloon an instance memory to a certain value. 737 738 @type instance: L{objects.Instance} 739 @param instance: instance to be accepted 740 @type mem: int 741 @param mem: actual memory size to use for instance runtime 742 743 """ 744 result = self._RunXen(["mem-set", instance.name, mem], instance.hvparams) 745 if result.failed: 746 raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" % 747 (instance.name, result.fail_reason, 748 result.output)) 749 750 # Update configuration file 751 cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem] 752 cmd.append(self._ConfigFileName(instance.name)) 753 754 result = utils.RunCmd(cmd) 755 if result.failed: 756 raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" % 757 (instance.name, result.fail_reason, 758 result.output))
759
760 - def GetNodeInfo(self, hvparams=None):
761 """Return information about the node. 762 763 @see: L{_GetNodeInfo} and L{_ParseNodeInfo} 764 765 """ 766 result = self._RunXen(["info"], hvparams) 767 if result.failed: 768 logging.error("Can't retrieve xen hypervisor information (%s): %s", 769 result.fail_reason, result.output) 770 return None 771 772 instance_list = self._GetInstanceList(True, hvparams) 773 return _GetNodeInfo(result.stdout, instance_list)
774 775 @classmethod
776 - def GetInstanceConsole(cls, instance, primary_node, hvparams, beparams):
777 """Return a command for connecting to the console of an instance. 778 779 """ 780 xen_cmd = XenHypervisor._GetCommandFromHvparams(hvparams) 781 return objects.InstanceConsole(instance=instance.name, 782 kind=constants.CONS_SSH, 783 host=primary_node.name, 784 user=constants.SSH_CONSOLE_USER, 785 command=[pathutils.XEN_CONSOLE_WRAPPER, 786 xen_cmd, instance.name])
787
788 - def Verify(self, hvparams=None):
789 """Verify the hypervisor. 790 791 For Xen, this verifies that the xend process is running. 792 793 @type hvparams: dict of strings 794 @param hvparams: hypervisor parameters to be verified against 795 796 @return: Problem description if something is wrong, C{None} otherwise 797 798 """ 799 if hvparams is None: 800 return "Could not verify the hypervisor, because no hvparams were" \ 801 " provided." 802 803 if constants.HV_XEN_CMD in hvparams: 804 xen_cmd = hvparams[constants.HV_XEN_CMD] 805 try: 806 self._CheckToolstack(xen_cmd) 807 except errors.HypervisorError: 808 return "The configured xen toolstack '%s' is not available on this" \ 809 " node." % xen_cmd 810 811 result = self._RunXen(["info"], hvparams) 812 if result.failed: 813 return "Retrieving information from xen failed: %s, %s" % \ 814 (result.fail_reason, result.output) 815 816 return None
817
818 - def MigrationInfo(self, instance):
819 """Get instance information to perform a migration. 820 821 @type instance: L{objects.Instance} 822 @param instance: instance to be migrated 823 @rtype: string 824 @return: content of the xen config file 825 826 """ 827 return self._ReadConfigFile(instance.name)
828
829 - def AcceptInstance(self, instance, info, target):
830 """Prepare to accept an instance. 831 832 @type instance: L{objects.Instance} 833 @param instance: instance to be accepted 834 @type info: string 835 @param info: content of the xen config file on the source node 836 @type target: string 837 @param target: target host (usually ip), on this node 838 839 """ 840 pass
841
842 - def FinalizeMigrationDst(self, instance, info, success):
843 """Finalize an instance migration. 844 845 After a successful migration we write the xen config file. 846 We do nothing on a failure, as we did not change anything at accept time. 847 848 @type instance: L{objects.Instance} 849 @param instance: instance whose migration is being finalized 850 @type info: string 851 @param info: content of the xen config file on the source node 852 @type success: boolean 853 @param success: whether the migration was a success or a failure 854 855 """ 856 if success: 857 self._WriteConfigFile(instance.name, info)
858
859 - def MigrateInstance(self, cluster_name, instance, target, live):
860 """Migrate an instance to a target node. 861 862 The migration will not be attempted if the instance is not 863 currently running. 864 865 @type instance: L{objects.Instance} 866 @param instance: the instance to be migrated 867 @type target: string 868 @param target: ip address of the target node 869 @type live: boolean 870 @param live: perform a live migration 871 872 """ 873 port = instance.hvparams[constants.HV_MIGRATION_PORT] 874 875 return self._MigrateInstance(cluster_name, instance.name, target, port, 876 live, instance.hvparams)
877
878 - def _MigrateInstance(self, cluster_name, instance_name, target, port, live, 879 hvparams, _ping_fn=netutils.TcpPing):
880 """Migrate an instance to a target node. 881 882 @see: L{MigrateInstance} for details 883 884 """ 885 if hvparams is None: 886 raise errors.HypervisorError("No hvparams provided.") 887 888 if self.GetInstanceInfo(instance_name, hvparams=hvparams) is None: 889 raise errors.HypervisorError("Instance not running, cannot migrate") 890 891 cmd = self._GetCommand(hvparams) 892 893 if (cmd == constants.XEN_CMD_XM and 894 not _ping_fn(target, port, live_port_needed=True)): 895 raise errors.HypervisorError("Remote host %s not listening on port" 896 " %s, cannot migrate" % (target, port)) 897 898 args = ["migrate"] 899 900 if cmd == constants.XEN_CMD_XM: 901 args.extend(["-p", "%d" % port]) 902 if live: 903 args.append("-l") 904 905 elif cmd == constants.XEN_CMD_XL: 906 args.extend([ 907 "-s", constants.XL_SSH_CMD % cluster_name, 908 "-C", self._ConfigFileName(instance_name), 909 ]) 910 911 else: 912 raise errors.HypervisorError("Unsupported Xen command: %s" % self._cmd) 913 914 args.extend([instance_name, target]) 915 916 result = self._RunXen(args, hvparams) 917 if result.failed: 918 raise errors.HypervisorError("Failed to migrate instance %s: %s" % 919 (instance_name, result.output))
920
921 - def FinalizeMigrationSource(self, instance, success, live):
922 """Finalize the instance migration on the source node. 923 924 @type instance: L{objects.Instance} 925 @param instance: the instance that was migrated 926 @type success: bool 927 @param success: whether the migration succeeded or not 928 @type live: bool 929 @param live: whether the user requested a live migration or not 930 931 """ 932 # pylint: disable=W0613 933 if success: 934 # remove old xen file after migration succeeded 935 try: 936 self._RemoveConfigFile(instance.name) 937 except EnvironmentError: 938 logging.exception("Failure while removing instance config file")
939
940 - def GetMigrationStatus(self, instance):
941 """Get the migration status 942 943 As MigrateInstance for Xen is still blocking, if this method is called it 944 means that MigrateInstance has completed successfully. So we can safely 945 assume that the migration was successful and notify this fact to the client. 946 947 @type instance: L{objects.Instance} 948 @param instance: the instance that is being migrated 949 @rtype: L{objects.MigrationStatus} 950 @return: the status of the current migration (one of 951 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional 952 progress info that can be retrieved from the hypervisor 953 954 """ 955 return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
956
957 - def PowercycleNode(self, hvparams=None):
958 """Xen-specific powercycle. 959 960 This first does a Linux reboot (which triggers automatically a Xen 961 reboot), and if that fails it tries to do a Xen reboot. The reason 962 we don't try a Xen reboot first is that the xen reboot launches an 963 external command which connects to the Xen hypervisor, and that 964 won't work in case the root filesystem is broken and/or the xend 965 daemon is not working. 966 967 @type hvparams: dict of strings 968 @param hvparams: hypervisor params to be used on this node 969 970 """ 971 try: 972 self.LinuxPowercycle() 973 finally: 974 xen_cmd = self._GetCommand(hvparams) 975 utils.RunCmd([xen_cmd, "debug", "R"])
976
977 - def _CheckToolstack(self, xen_cmd):
978 """Check whether the given toolstack is available on the node. 979 980 @type xen_cmd: string 981 @param xen_cmd: xen command (e.g. 'xm' or 'xl') 982 983 """ 984 binary_found = self._CheckToolstackBinary(xen_cmd) 985 if not binary_found: 986 raise errors.HypervisorError("No '%s' binary found on node." % xen_cmd) 987 elif xen_cmd == constants.XEN_CMD_XL: 988 if not self._CheckToolstackXlConfigured(): 989 raise errors.HypervisorError("Toolstack '%s' is not enabled on this" 990 "node." % xen_cmd)
991
992 - def _CheckToolstackBinary(self, xen_cmd):
993 """Checks whether the xen command's binary is found on the machine. 994 995 """ 996 if xen_cmd not in constants.KNOWN_XEN_COMMANDS: 997 raise errors.HypervisorError("Unknown xen command '%s'." % xen_cmd) 998 result = self._run_cmd_fn(["which", xen_cmd]) 999 return not result.failed
1000
1001 - def _CheckToolstackXlConfigured(self):
1002 """Checks whether xl is enabled on an xl-capable node. 1003 1004 @rtype: bool 1005 @returns: C{True} if 'xl' is enabled, C{False} otherwise 1006 1007 """ 1008 result = self._run_cmd_fn([constants.XEN_CMD_XL, "help"]) 1009 if not result.failed: 1010 return True 1011 elif result.failed: 1012 if "toolstack" in result.stderr: 1013 return False 1014 # xl fails for some other reason than the toolstack 1015 else: 1016 raise errors.HypervisorError("Cannot run xen ('%s'). Error: %s." 1017 % (constants.XEN_CMD_XL, result.stderr))
1018
1019 1020 -class XenPvmHypervisor(XenHypervisor):
1021 """Xen PVM hypervisor interface""" 1022 1023 PARAMETERS = { 1024 constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK, 1025 constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK, 1026 constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK, 1027 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK, 1028 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK, 1029 constants.HV_ROOT_PATH: hv_base.NO_CHECK, 1030 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK, 1031 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK, 1032 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK, 1033 # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar). 1034 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK, 1035 constants.HV_REBOOT_BEHAVIOR: 1036 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS), 1037 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK, 1038 constants.HV_CPU_CAP: hv_base.OPT_NONNEGATIVE_INT_CHECK, 1039 constants.HV_CPU_WEIGHT: 1040 (False, lambda x: 0 < x < 65536, "invalid weight", None, None), 1041 constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK, 1042 constants.HV_XEN_CMD: 1043 hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS), 1044 } 1045
1046 - def _GetConfig(self, instance, startup_memory, block_devices):
1047 """Write the Xen config file for the instance. 1048 1049 """ 1050 hvp = instance.hvparams 1051 config = StringIO() 1052 config.write("# this is autogenerated by Ganeti, please do not edit\n#\n") 1053 1054 # if bootloader is True, use bootloader instead of kernel and ramdisk 1055 # parameters. 1056 if hvp[constants.HV_USE_BOOTLOADER]: 1057 # bootloader handling 1058 bootloader_path = hvp[constants.HV_BOOTLOADER_PATH] 1059 if bootloader_path: 1060 config.write("bootloader = '%s'\n" % bootloader_path) 1061 else: 1062 raise errors.HypervisorError("Bootloader enabled, but missing" 1063 " bootloader path") 1064 1065 bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS] 1066 if bootloader_args: 1067 config.write("bootargs = '%s'\n" % bootloader_args) 1068 else: 1069 # kernel handling 1070 kpath = hvp[constants.HV_KERNEL_PATH] 1071 config.write("kernel = '%s'\n" % kpath) 1072 1073 # initrd handling 1074 initrd_path = hvp[constants.HV_INITRD_PATH] 1075 if initrd_path: 1076 config.write("ramdisk = '%s'\n" % initrd_path) 1077 1078 # rest of the settings 1079 config.write("memory = %d\n" % startup_memory) 1080 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM]) 1081 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS]) 1082 cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK]) 1083 if cpu_pinning: 1084 config.write("%s\n" % cpu_pinning) 1085 cpu_cap = hvp[constants.HV_CPU_CAP] 1086 if cpu_cap: 1087 config.write("cpu_cap=%d\n" % cpu_cap) 1088 cpu_weight = hvp[constants.HV_CPU_WEIGHT] 1089 if cpu_weight: 1090 config.write("cpu_weight=%d\n" % cpu_weight) 1091 1092 config.write("name = '%s'\n" % instance.name) 1093 1094 vif_data = [] 1095 for idx, nic in enumerate(instance.nics): 1096 nic_str = "mac=%s" % (nic.mac) 1097 ip = getattr(nic, "ip", None) 1098 if ip is not None: 1099 nic_str += ", ip=%s" % ip 1100 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED: 1101 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK] 1102 if hvp[constants.HV_VIF_SCRIPT]: 1103 nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT] 1104 vif_data.append("'%s'" % nic_str) 1105 self._WriteNICInfoFile(instance.name, idx, nic) 1106 1107 disk_data = \ 1108 _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX]) 1109 1110 config.write("vif = [%s]\n" % ",".join(vif_data)) 1111 config.write("disk = [%s]\n" % ",".join(disk_data)) 1112 1113 if hvp[constants.HV_ROOT_PATH]: 1114 config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH]) 1115 config.write("on_poweroff = 'destroy'\n") 1116 if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED: 1117 config.write("on_reboot = 'restart'\n") 1118 else: 1119 config.write("on_reboot = 'destroy'\n") 1120 config.write("on_crash = 'restart'\n") 1121 config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS]) 1122 1123 return config.getvalue()
1124
1125 1126 -class XenHvmHypervisor(XenHypervisor):
1127 """Xen HVM hypervisor interface""" 1128 1129 ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [ 1130 pathutils.VNC_PASSWORD_FILE, 1131 ] 1132 ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [ 1133 pathutils.VNC_PASSWORD_FILE, 1134 ] 1135 1136 PARAMETERS = { 1137 constants.HV_ACPI: hv_base.NO_CHECK, 1138 constants.HV_BOOT_ORDER: (True, ) + 1139 (lambda x: x and len(x.strip("acdn")) == 0, 1140 "Invalid boot order specified, must be one or more of [acdn]", 1141 None, None), 1142 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK, 1143 constants.HV_DISK_TYPE: 1144 hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES), 1145 constants.HV_NIC_TYPE: 1146 hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES), 1147 constants.HV_PAE: hv_base.NO_CHECK, 1148 constants.HV_VNC_BIND_ADDRESS: 1149 (False, netutils.IP4Address.IsValid, 1150 "VNC bind address is not a valid IP address", None, None), 1151 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK, 1152 constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK, 1153 constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK, 1154 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK, 1155 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK, 1156 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK, 1157 # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar). 1158 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK, 1159 # Add PCI passthrough 1160 constants.HV_PASSTHROUGH: hv_base.NO_CHECK, 1161 constants.HV_REBOOT_BEHAVIOR: 1162 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS), 1163 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK, 1164 constants.HV_CPU_CAP: hv_base.NO_CHECK, 1165 constants.HV_CPU_WEIGHT: 1166 (False, lambda x: 0 < x < 65535, "invalid weight", None, None), 1167 constants.HV_VIF_TYPE: 1168 hv_base.ParamInSet(False, constants.HT_HVM_VALID_VIF_TYPES), 1169 constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK, 1170 constants.HV_VIRIDIAN: hv_base.NO_CHECK, 1171 constants.HV_XEN_CMD: 1172 hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS), 1173 } 1174
1175 - def _GetConfig(self, instance, startup_memory, block_devices):
1176 """Create a Xen 3.1 HVM config file. 1177 1178 """ 1179 hvp = instance.hvparams 1180 1181 config = StringIO() 1182 1183 # kernel handling 1184 kpath = hvp[constants.HV_KERNEL_PATH] 1185 config.write("kernel = '%s'\n" % kpath) 1186 1187 config.write("builder = 'hvm'\n") 1188 config.write("memory = %d\n" % startup_memory) 1189 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM]) 1190 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS]) 1191 cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK]) 1192 if cpu_pinning: 1193 config.write("%s\n" % cpu_pinning) 1194 cpu_cap = hvp[constants.HV_CPU_CAP] 1195 if cpu_cap: 1196 config.write("cpu_cap=%d\n" % cpu_cap) 1197 cpu_weight = hvp[constants.HV_CPU_WEIGHT] 1198 if cpu_weight: 1199 config.write("cpu_weight=%d\n" % cpu_weight) 1200 1201 config.write("name = '%s'\n" % instance.name) 1202 if hvp[constants.HV_PAE]: 1203 config.write("pae = 1\n") 1204 else: 1205 config.write("pae = 0\n") 1206 if hvp[constants.HV_ACPI]: 1207 config.write("acpi = 1\n") 1208 else: 1209 config.write("acpi = 0\n") 1210 if hvp[constants.HV_VIRIDIAN]: 1211 config.write("viridian = 1\n") 1212 else: 1213 config.write("viridian = 0\n") 1214 1215 config.write("apic = 1\n") 1216 config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL]) 1217 config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER]) 1218 config.write("sdl = 0\n") 1219 config.write("usb = 1\n") 1220 config.write("usbdevice = 'tablet'\n") 1221 config.write("vnc = 1\n") 1222 if hvp[constants.HV_VNC_BIND_ADDRESS] is None: 1223 config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS) 1224 else: 1225 config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS]) 1226 1227 if instance.network_port > constants.VNC_BASE_PORT: 1228 display = instance.network_port - constants.VNC_BASE_PORT 1229 config.write("vncdisplay = %s\n" % display) 1230 config.write("vncunused = 0\n") 1231 else: 1232 config.write("# vncdisplay = 1\n") 1233 config.write("vncunused = 1\n") 1234 1235 vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE] 1236 try: 1237 password = utils.ReadFile(vnc_pwd_file) 1238 except EnvironmentError, err: 1239 raise errors.HypervisorError("Failed to open VNC password file %s: %s" % 1240 (vnc_pwd_file, err)) 1241 1242 config.write("vncpasswd = '%s'\n" % password.rstrip()) 1243 1244 config.write("serial = 'pty'\n") 1245 if hvp[constants.HV_USE_LOCALTIME]: 1246 config.write("localtime = 1\n") 1247 1248 vif_data = [] 1249 # Note: what is called 'nic_type' here, is used as value for the xen nic 1250 # vif config parameter 'model'. For the xen nic vif parameter 'type', we use 1251 # the 'vif_type' to avoid a clash of notation. 1252 nic_type = hvp[constants.HV_NIC_TYPE] 1253 1254 if nic_type is None: 1255 vif_type_str = "" 1256 if hvp[constants.HV_VIF_TYPE]: 1257 vif_type_str = ", type=%s" % hvp[constants.HV_VIF_TYPE] 1258 # ensure old instances don't change 1259 nic_type_str = vif_type_str 1260 elif nic_type == constants.HT_NIC_PARAVIRTUAL: 1261 nic_type_str = ", type=paravirtualized" 1262 else: 1263 # parameter 'model' is only valid with type 'ioemu' 1264 nic_type_str = ", model=%s, type=%s" % \ 1265 (nic_type, constants.HT_HVM_VIF_IOEMU) 1266 for idx, nic in enumerate(instance.nics): 1267 nic_str = "mac=%s%s" % (nic.mac, nic_type_str) 1268 ip = getattr(nic, "ip", None) 1269 if ip is not None: 1270 nic_str += ", ip=%s" % ip 1271 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED: 1272 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK] 1273 if hvp[constants.HV_VIF_SCRIPT]: 1274 nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT] 1275 vif_data.append("'%s'" % nic_str) 1276 self._WriteNICInfoFile(instance.name, idx, nic) 1277 1278 config.write("vif = [%s]\n" % ",".join(vif_data)) 1279 1280 disk_data = \ 1281 _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX]) 1282 1283 iso_path = hvp[constants.HV_CDROM_IMAGE_PATH] 1284 if iso_path: 1285 iso = "'file:%s,hdc:cdrom,r'" % iso_path 1286 disk_data.append(iso) 1287 1288 config.write("disk = [%s]\n" % (",".join(disk_data))) 1289 # Add PCI passthrough 1290 pci_pass_arr = [] 1291 pci_pass = hvp[constants.HV_PASSTHROUGH] 1292 if pci_pass: 1293 pci_pass_arr = pci_pass.split(";") 1294 config.write("pci = %s\n" % pci_pass_arr) 1295 config.write("on_poweroff = 'destroy'\n") 1296 if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED: 1297 config.write("on_reboot = 'restart'\n") 1298 else: 1299 config.write("on_reboot = 'destroy'\n") 1300 config.write("on_crash = 'restart'\n") 1301 1302 return config.getvalue()
1303