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