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