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

Source Code for Module ganeti.hypervisor.hv_xen

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Google Inc. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, but 
 12  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 14  # General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the Free Software 
 18  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
 19  # 02110-1301, USA. 
 20   
 21   
 22  """Xen hypervisors 
 23   
 24  """ 
 25   
 26  import logging 
 27  from cStringIO import StringIO 
 28   
 29  from ganeti import constants 
 30  from ganeti import errors 
 31  from ganeti import utils 
 32  from ganeti.hypervisor import hv_base 
 33  from ganeti import netutils 
 34  from ganeti import objects 
 35  from ganeti import pathutils 
 36  from ganeti import ssconf 
 37   
 38   
 39  XEND_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xend-config.sxp") 
 40  XL_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xen/xl.conf") 
 41  VIF_BRIDGE_SCRIPT = utils.PathJoin(pathutils.XEN_CONFIG_DIR, 
 42                                     "scripts/vif-bridge") 
 43  _DOM0_NAME = "Domain-0" 
44 45 46 -def _CreateConfigCpus(cpu_mask):
47 """Create a CPU config string for Xen's config file. 48 49 """ 50 # Convert the string CPU mask to a list of list of int's 51 cpu_list = utils.ParseMultiCpuMask(cpu_mask) 52 53 if len(cpu_list) == 1: 54 all_cpu_mapping = cpu_list[0] 55 if all_cpu_mapping == constants.CPU_PINNING_OFF: 56 # If CPU pinning has 1 entry that's "all", then remove the 57 # parameter from the config file 58 return None 59 else: 60 # If CPU pinning has one non-all entry, mapping all vCPUS (the entire 61 # VM) to one physical CPU, using format 'cpu = "C"' 62 return "cpu = \"%s\"" % ",".join(map(str, all_cpu_mapping)) 63 else: 64 65 def _GetCPUMap(vcpu): 66 if vcpu[0] == constants.CPU_PINNING_ALL_VAL: 67 cpu_map = constants.CPU_PINNING_ALL_XEN 68 else: 69 cpu_map = ",".join(map(str, vcpu)) 70 return "\"%s\"" % cpu_map
71 72 # build the result string in format 'cpus = [ "c", "c", "c" ]', 73 # where each c is a physical CPU number, a range, a list, or any 74 # combination 75 return "cpus = [ %s ]" % ", ".join(map(_GetCPUMap, cpu_list)) 76
77 78 -class XenHypervisor(hv_base.BaseHypervisor):
79 """Xen generic hypervisor interface 80 81 This is the Xen base class used for both Xen PVM and HVM. It contains 82 all the functionality that is identical for both. 83 84 """ 85 CAN_MIGRATE = True 86 REBOOT_RETRY_COUNT = 60 87 REBOOT_RETRY_INTERVAL = 10 88 89 ANCILLARY_FILES = [ 90 XEND_CONFIG_FILE, 91 XL_CONFIG_FILE, 92 VIF_BRIDGE_SCRIPT, 93 ] 94 ANCILLARY_FILES_OPT = [ 95 XL_CONFIG_FILE, 96 ] 97 98 @staticmethod
99 - def _ConfigFileName(instance_name):
100 """Get the config file name for an instance. 101 102 @param instance_name: instance name 103 @type instance_name: str 104 @return: fully qualified path to instance config file 105 @rtype: str 106 107 """ 108 return utils.PathJoin(pathutils.XEN_CONFIG_DIR, instance_name)
109 110 @classmethod
111 - def _WriteConfigFile(cls, instance, startup_memory, block_devices):
112 """Write the Xen config file for the instance. 113 114 """ 115 raise NotImplementedError
116 117 @staticmethod
118 - def _WriteConfigFileStatic(instance_name, data):
119 """Write the Xen config file for the instance. 120 121 This version of the function just writes the config file from static data. 122 123 """ 124 # just in case it exists 125 utils.RemoveFile(utils.PathJoin(pathutils.XEN_CONFIG_DIR, "auto", 126 instance_name)) 127 128 cfg_file = XenHypervisor._ConfigFileName(instance_name) 129 try: 130 utils.WriteFile(cfg_file, data=data) 131 except EnvironmentError, err: 132 raise errors.HypervisorError("Cannot write Xen instance configuration" 133 " file %s: %s" % (cfg_file, err))
134 135 @staticmethod
136 - def _ReadConfigFile(instance_name):
137 """Returns the contents of the instance config file. 138 139 """ 140 filename = XenHypervisor._ConfigFileName(instance_name) 141 142 try: 143 file_content = utils.ReadFile(filename) 144 except EnvironmentError, err: 145 raise errors.HypervisorError("Failed to load Xen config file: %s" % err) 146 147 return file_content
148 149 @staticmethod
150 - def _RemoveConfigFile(instance_name):
151 """Remove the xen configuration file. 152 153 """ 154 utils.RemoveFile(XenHypervisor._ConfigFileName(instance_name))
155 156 @staticmethod
157 - def _RunXmList(xmlist_errors):
158 """Helper function for L{_GetXMList} to run "xm list". 159 160 """ 161 result = utils.RunCmd([constants.XEN_CMD, "list"]) 162 if result.failed: 163 logging.error("xm list failed (%s): %s", result.fail_reason, 164 result.output) 165 xmlist_errors.append(result) 166 raise utils.RetryAgain() 167 168 # skip over the heading 169 return result.stdout.splitlines()[1:]
170 171 @classmethod
172 - def _GetXMList(cls, include_node):
173 """Return the list of running instances. 174 175 If the include_node argument is True, then we return information 176 for dom0 also, otherwise we filter that from the return value. 177 178 @return: list of (name, id, memory, vcpus, state, time spent) 179 180 """ 181 xmlist_errors = [] 182 try: 183 lines = utils.Retry(cls._RunXmList, 1, 5, args=(xmlist_errors, )) 184 except utils.RetryTimeout: 185 if xmlist_errors: 186 xmlist_result = xmlist_errors.pop() 187 188 errmsg = ("xm list failed, timeout exceeded (%s): %s" % 189 (xmlist_result.fail_reason, xmlist_result.output)) 190 else: 191 errmsg = "xm list failed" 192 193 raise errors.HypervisorError(errmsg) 194 195 result = [] 196 for line in lines: 197 # The format of lines is: 198 # Name ID Mem(MiB) VCPUs State Time(s) 199 # Domain-0 0 3418 4 r----- 266.2 200 data = line.split() 201 if len(data) != 6: 202 raise errors.HypervisorError("Can't parse output of xm list," 203 " line: %s" % line) 204 try: 205 data[1] = int(data[1]) 206 data[2] = int(data[2]) 207 data[3] = int(data[3]) 208 data[5] = float(data[5]) 209 except (TypeError, ValueError), err: 210 raise errors.HypervisorError("Can't parse output of xm list," 211 " line: %s, error: %s" % (line, err)) 212 213 # skip the Domain-0 (optional) 214 if include_node or data[0] != _DOM0_NAME: 215 result.append(data) 216 217 return result
218
219 - def ListInstances(self):
220 """Get the list of running instances. 221 222 """ 223 xm_list = self._GetXMList(False) 224 names = [info[0] for info in xm_list] 225 return names
226
227 - def GetInstanceInfo(self, instance_name):
228 """Get instance properties. 229 230 @param instance_name: the instance name 231 232 @return: tuple (name, id, memory, vcpus, stat, times) 233 234 """ 235 xm_list = self._GetXMList(instance_name == _DOM0_NAME) 236 result = None 237 for data in xm_list: 238 if data[0] == instance_name: 239 result = data 240 break 241 return result
242
243 - def GetAllInstancesInfo(self):
244 """Get properties of all instances. 245 246 @return: list of tuples (name, id, memory, vcpus, stat, times) 247 248 """ 249 xm_list = self._GetXMList(False) 250 return xm_list
251
252 - def StartInstance(self, instance, block_devices, startup_paused):
253 """Start an instance. 254 255 """ 256 startup_memory = self._InstanceStartupMemory(instance) 257 self._WriteConfigFile(instance, startup_memory, block_devices) 258 cmd = [constants.XEN_CMD, "create"] 259 if startup_paused: 260 cmd.extend(["-p"]) 261 cmd.extend([self._ConfigFileName(instance.name)]) 262 result = utils.RunCmd(cmd) 263 264 if result.failed: 265 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" % 266 (instance.name, result.fail_reason, 267 result.output))
268
269 - def StopInstance(self, instance, force=False, retry=False, name=None):
270 """Stop an instance. 271 272 """ 273 if name is None: 274 name = instance.name 275 276 if force: 277 command = [constants.XEN_CMD, "destroy", name] 278 else: 279 command = [constants.XEN_CMD, "shutdown", name] 280 result = utils.RunCmd(command) 281 282 if result.failed: 283 raise errors.HypervisorError("Failed to stop instance %s: %s, %s" % 284 (name, result.fail_reason, result.output)) 285 286 # Remove configuration file if stopping/starting instance was successful 287 self._RemoveConfigFile(name)
288
289 - def RebootInstance(self, instance):
290 """Reboot an instance. 291 292 """ 293 ini_info = self.GetInstanceInfo(instance.name) 294 295 if ini_info is None: 296 raise errors.HypervisorError("Failed to reboot instance %s," 297 " not running" % instance.name) 298 299 result = utils.RunCmd([constants.XEN_CMD, "reboot", instance.name]) 300 if result.failed: 301 raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" % 302 (instance.name, result.fail_reason, 303 result.output)) 304 305 def _CheckInstance(): 306 new_info = self.GetInstanceInfo(instance.name) 307 308 # check if the domain ID has changed or the run time has decreased 309 if (new_info is not None and 310 (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])): 311 return 312 313 raise utils.RetryAgain()
314 315 try: 316 utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL, 317 self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT) 318 except utils.RetryTimeout: 319 raise errors.HypervisorError("Failed to reboot instance %s: instance" 320 " did not reboot in the expected interval" % 321 (instance.name, ))
322
323 - def BalloonInstanceMemory(self, instance, mem):
324 """Balloon an instance memory to a certain value. 325 326 @type instance: L{objects.Instance} 327 @param instance: instance to be accepted 328 @type mem: int 329 @param mem: actual memory size to use for instance runtime 330 331 """ 332 cmd = [constants.XEN_CMD, "mem-set", instance.name, mem] 333 result = utils.RunCmd(cmd) 334 if result.failed: 335 raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" % 336 (instance.name, result.fail_reason, 337 result.output)) 338 cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem] 339 cmd.append(XenHypervisor._ConfigFileName(instance.name)) 340 result = utils.RunCmd(cmd) 341 if result.failed: 342 raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" % 343 (instance.name, result.fail_reason, 344 result.output))
345
346 - def GetNodeInfo(self):
347 """Return information about the node. 348 349 @return: a dict with the following keys (memory values in MiB): 350 - memory_total: the total memory size on the node 351 - memory_free: the available memory on the node for instances 352 - memory_dom0: the memory used by the node itself, if available 353 - nr_cpus: total number of CPUs 354 - nr_nodes: in a NUMA system, the number of domains 355 - nr_sockets: the number of physical CPU sockets in the node 356 - hv_version: the hypervisor version in the form (major, minor) 357 358 """ 359 result = utils.RunCmd([constants.XEN_CMD, "info"]) 360 if result.failed: 361 logging.error("Can't run 'xm info' (%s): %s", result.fail_reason, 362 result.output) 363 return None 364 365 xmoutput = result.stdout.splitlines() 366 result = {} 367 cores_per_socket = threads_per_core = nr_cpus = None 368 xen_major, xen_minor = None, None 369 memory_total = None 370 memory_free = None 371 372 for line in xmoutput: 373 splitfields = line.split(":", 1) 374 375 if len(splitfields) > 1: 376 key = splitfields[0].strip() 377 val = splitfields[1].strip() 378 379 # note: in xen 3, memory has changed to total_memory 380 if key == "memory" or key == "total_memory": 381 memory_total = int(val) 382 elif key == "free_memory": 383 memory_free = int(val) 384 elif key == "nr_cpus": 385 nr_cpus = result["cpu_total"] = int(val) 386 elif key == "nr_nodes": 387 result["cpu_nodes"] = int(val) 388 elif key == "cores_per_socket": 389 cores_per_socket = int(val) 390 elif key == "threads_per_core": 391 threads_per_core = int(val) 392 elif key == "xen_major": 393 xen_major = int(val) 394 elif key == "xen_minor": 395 xen_minor = int(val) 396 397 if None not in [cores_per_socket, threads_per_core, nr_cpus]: 398 result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core) 399 400 total_instmem = 0 401 for (name, _, mem, vcpus, _, _) in self._GetXMList(True): 402 if name == _DOM0_NAME: 403 result["memory_dom0"] = mem 404 result["dom0_cpus"] = vcpus 405 406 # Include Dom0 in total memory usage 407 total_instmem += mem 408 409 if memory_free is not None: 410 result["memory_free"] = memory_free 411 412 if memory_total is not None: 413 result["memory_total"] = memory_total 414 415 # Calculate memory used by hypervisor 416 if None not in [memory_total, memory_free, total_instmem]: 417 result["memory_hv"] = memory_total - memory_free - total_instmem 418 419 if not (xen_major is None or xen_minor is None): 420 result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor) 421 422 return result
423 424 @classmethod
425 - def GetInstanceConsole(cls, instance, hvparams, beparams):
426 """Return a command for connecting to the console of an instance. 427 428 """ 429 return objects.InstanceConsole(instance=instance.name, 430 kind=constants.CONS_SSH, 431 host=instance.primary_node, 432 user=constants.SSH_CONSOLE_USER, 433 command=[pathutils.XEN_CONSOLE_WRAPPER, 434 constants.XEN_CMD, instance.name])
435
436 - def Verify(self):
437 """Verify the hypervisor. 438 439 For Xen, this verifies that the xend process is running. 440 441 @return: Problem description if something is wrong, C{None} otherwise 442 443 """ 444 result = utils.RunCmd([constants.XEN_CMD, "info"]) 445 if result.failed: 446 return "'xm info' failed: %s, %s" % (result.fail_reason, result.output) 447 448 return None
449 450 @staticmethod
451 - def _GetConfigFileDiskData(block_devices, blockdev_prefix):
452 """Get disk directive for xen config file. 453 454 This method builds the xen config disk directive according to the 455 given disk_template and block_devices. 456 457 @param block_devices: list of tuples (cfdev, rldev): 458 - cfdev: dict containing ganeti config disk part 459 - rldev: ganeti.bdev.BlockDev object 460 @param blockdev_prefix: a string containing blockdevice prefix, 461 e.g. "sd" for /dev/sda 462 463 @return: string containing disk directive for xen instance config file 464 465 """ 466 FILE_DRIVER_MAP = { 467 constants.FD_LOOP: "file", 468 constants.FD_BLKTAP: "tap:aio", 469 } 470 disk_data = [] 471 if len(block_devices) > 24: 472 # 'z' - 'a' = 24 473 raise errors.HypervisorError("Too many disks") 474 namespace = [blockdev_prefix + chr(i + ord("a")) for i in range(24)] 475 for sd_name, (cfdev, dev_path) in zip(namespace, block_devices): 476 if cfdev.mode == constants.DISK_RDWR: 477 mode = "w" 478 else: 479 mode = "r" 480 if cfdev.dev_type == constants.LD_FILE: 481 line = "'%s:%s,%s,%s'" % (FILE_DRIVER_MAP[cfdev.physical_id[0]], 482 dev_path, sd_name, mode) 483 else: 484 line = "'phy:%s,%s,%s'" % (dev_path, sd_name, mode) 485 disk_data.append(line) 486 487 return disk_data
488
489 - def MigrationInfo(self, instance):
490 """Get instance information to perform a migration. 491 492 @type instance: L{objects.Instance} 493 @param instance: instance to be migrated 494 @rtype: string 495 @return: content of the xen config file 496 497 """ 498 return self._ReadConfigFile(instance.name)
499
500 - def AcceptInstance(self, instance, info, target):
501 """Prepare to accept an instance. 502 503 @type instance: L{objects.Instance} 504 @param instance: instance to be accepted 505 @type info: string 506 @param info: content of the xen config file on the source node 507 @type target: string 508 @param target: target host (usually ip), on this node 509 510 """ 511 pass
512
513 - def FinalizeMigrationDst(self, instance, info, success):
514 """Finalize an instance migration. 515 516 After a successful migration we write the xen config file. 517 We do nothing on a failure, as we did not change anything at accept time. 518 519 @type instance: L{objects.Instance} 520 @param instance: instance whose migration is being finalized 521 @type info: string 522 @param info: content of the xen config file on the source node 523 @type success: boolean 524 @param success: whether the migration was a success or a failure 525 526 """ 527 if success: 528 self._WriteConfigFileStatic(instance.name, info)
529
530 - def MigrateInstance(self, instance, target, live):
531 """Migrate an instance to a target node. 532 533 The migration will not be attempted if the instance is not 534 currently running. 535 536 @type instance: L{objects.Instance} 537 @param instance: the instance to be migrated 538 @type target: string 539 @param target: ip address of the target node 540 @type live: boolean 541 @param live: perform a live migration 542 543 """ 544 if self.GetInstanceInfo(instance.name) is None: 545 raise errors.HypervisorError("Instance not running, cannot migrate") 546 547 port = instance.hvparams[constants.HV_MIGRATION_PORT] 548 549 if (constants.XEN_CMD == constants.XEN_CMD_XM and 550 not netutils.TcpPing(target, port, live_port_needed=True)): 551 raise errors.HypervisorError("Remote host %s not listening on port" 552 " %s, cannot migrate" % (target, port)) 553 554 args = [constants.XEN_CMD, "migrate"] 555 if constants.XEN_CMD == constants.XEN_CMD_XM: 556 args.extend(["-p", "%d" % port]) 557 if live: 558 args.append("-l") 559 elif constants.XEN_CMD == constants.XEN_CMD_XL: 560 cluster_name = ssconf.SimpleStore().GetClusterName() 561 args.extend(["-s", constants.XL_SSH_CMD % cluster_name]) 562 args.extend(["-C", self._ConfigFileName(instance.name)]) 563 else: 564 raise errors.HypervisorError("Unsupported xen command: %s" % 565 constants.XEN_CMD) 566 567 args.extend([instance.name, target]) 568 result = utils.RunCmd(args) 569 if result.failed: 570 raise errors.HypervisorError("Failed to migrate instance %s: %s" % 571 (instance.name, result.output))
572
573 - def FinalizeMigrationSource(self, instance, success, live):
574 """Finalize the instance migration on the source node. 575 576 @type instance: L{objects.Instance} 577 @param instance: the instance that was migrated 578 @type success: bool 579 @param success: whether the migration succeeded or not 580 @type live: bool 581 @param live: whether the user requested a live migration or not 582 583 """ 584 # pylint: disable=W0613 585 if success: 586 # remove old xen file after migration succeeded 587 try: 588 self._RemoveConfigFile(instance.name) 589 except EnvironmentError: 590 logging.exception("Failure while removing instance config file")
591
592 - def GetMigrationStatus(self, instance):
593 """Get the migration status 594 595 As MigrateInstance for Xen is still blocking, if this method is called it 596 means that MigrateInstance has completed successfully. So we can safely 597 assume that the migration was successful and notify this fact to the client. 598 599 @type instance: L{objects.Instance} 600 @param instance: the instance that is being migrated 601 @rtype: L{objects.MigrationStatus} 602 @return: the status of the current migration (one of 603 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional 604 progress info that can be retrieved from the hypervisor 605 606 """ 607 return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
608 609 @classmethod
610 - def PowercycleNode(cls):
611 """Xen-specific powercycle. 612 613 This first does a Linux reboot (which triggers automatically a Xen 614 reboot), and if that fails it tries to do a Xen reboot. The reason 615 we don't try a Xen reboot first is that the xen reboot launches an 616 external command which connects to the Xen hypervisor, and that 617 won't work in case the root filesystem is broken and/or the xend 618 daemon is not working. 619 620 """ 621 try: 622 cls.LinuxPowercycle() 623 finally: 624 utils.RunCmd([constants.XEN_CMD, "debug", "R"])
625
626 627 -class XenPvmHypervisor(XenHypervisor):
628 """Xen PVM hypervisor interface""" 629 630 PARAMETERS = { 631 constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK, 632 constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK, 633 constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK, 634 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK, 635 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK, 636 constants.HV_ROOT_PATH: hv_base.NO_CHECK, 637 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK, 638 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK, 639 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK, 640 # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar). 641 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK, 642 constants.HV_REBOOT_BEHAVIOR: 643 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS), 644 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK, 645 constants.HV_CPU_CAP: hv_base.OPT_NONNEGATIVE_INT_CHECK, 646 constants.HV_CPU_WEIGHT: 647 (False, lambda x: 0 < x < 65536, "invalid weight", None, None), 648 } 649 650 @classmethod
651 - def _WriteConfigFile(cls, instance, startup_memory, block_devices):
652 """Write the Xen config file for the instance. 653 654 """ 655 hvp = instance.hvparams 656 config = StringIO() 657 config.write("# this is autogenerated by Ganeti, please do not edit\n#\n") 658 659 # if bootloader is True, use bootloader instead of kernel and ramdisk 660 # parameters. 661 if hvp[constants.HV_USE_BOOTLOADER]: 662 # bootloader handling 663 bootloader_path = hvp[constants.HV_BOOTLOADER_PATH] 664 if bootloader_path: 665 config.write("bootloader = '%s'\n" % bootloader_path) 666 else: 667 raise errors.HypervisorError("Bootloader enabled, but missing" 668 " bootloader path") 669 670 bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS] 671 if bootloader_args: 672 config.write("bootargs = '%s'\n" % bootloader_args) 673 else: 674 # kernel handling 675 kpath = hvp[constants.HV_KERNEL_PATH] 676 config.write("kernel = '%s'\n" % kpath) 677 678 # initrd handling 679 initrd_path = hvp[constants.HV_INITRD_PATH] 680 if initrd_path: 681 config.write("ramdisk = '%s'\n" % initrd_path) 682 683 # rest of the settings 684 config.write("memory = %d\n" % startup_memory) 685 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM]) 686 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS]) 687 cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK]) 688 if cpu_pinning: 689 config.write("%s\n" % cpu_pinning) 690 cpu_cap = hvp[constants.HV_CPU_CAP] 691 if cpu_cap: 692 config.write("cpu_cap=%d\n" % cpu_cap) 693 cpu_weight = hvp[constants.HV_CPU_WEIGHT] 694 if cpu_weight: 695 config.write("cpu_weight=%d\n" % cpu_weight) 696 697 config.write("name = '%s'\n" % instance.name) 698 699 vif_data = [] 700 for nic in instance.nics: 701 nic_str = "mac=%s" % (nic.mac) 702 ip = getattr(nic, "ip", None) 703 if ip is not None: 704 nic_str += ", ip=%s" % ip 705 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED: 706 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK] 707 vif_data.append("'%s'" % nic_str) 708 709 disk_data = cls._GetConfigFileDiskData(block_devices, 710 hvp[constants.HV_BLOCKDEV_PREFIX]) 711 712 config.write("vif = [%s]\n" % ",".join(vif_data)) 713 config.write("disk = [%s]\n" % ",".join(disk_data)) 714 715 if hvp[constants.HV_ROOT_PATH]: 716 config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH]) 717 config.write("on_poweroff = 'destroy'\n") 718 if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED: 719 config.write("on_reboot = 'restart'\n") 720 else: 721 config.write("on_reboot = 'destroy'\n") 722 config.write("on_crash = 'restart'\n") 723 config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS]) 724 cls._WriteConfigFileStatic(instance.name, config.getvalue()) 725 726 return True
727
728 729 -class XenHvmHypervisor(XenHypervisor):
730 """Xen HVM hypervisor interface""" 731 732 ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [ 733 pathutils.VNC_PASSWORD_FILE, 734 ] 735 ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [ 736 pathutils.VNC_PASSWORD_FILE, 737 ] 738 739 PARAMETERS = { 740 constants.HV_ACPI: hv_base.NO_CHECK, 741 constants.HV_BOOT_ORDER: (True, ) + 742 (lambda x: x and len(x.strip("acdn")) == 0, 743 "Invalid boot order specified, must be one or more of [acdn]", 744 None, None), 745 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK, 746 constants.HV_DISK_TYPE: 747 hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES), 748 constants.HV_NIC_TYPE: 749 hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES), 750 constants.HV_PAE: hv_base.NO_CHECK, 751 constants.HV_VNC_BIND_ADDRESS: 752 (False, netutils.IP4Address.IsValid, 753 "VNC bind address is not a valid IP address", None, None), 754 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK, 755 constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK, 756 constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK, 757 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK, 758 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK, 759 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK, 760 # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar). 761 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK, 762 # Add PCI passthrough 763 constants.HV_PASSTHROUGH: hv_base.NO_CHECK, 764 constants.HV_REBOOT_BEHAVIOR: 765 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS), 766 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK, 767 constants.HV_CPU_CAP: hv_base.NO_CHECK, 768 constants.HV_CPU_WEIGHT: 769 (False, lambda x: 0 < x < 65535, "invalid weight", None, None), 770 constants.HV_VIF_TYPE: 771 hv_base.ParamInSet(False, constants.HT_HVM_VALID_VIF_TYPES), 772 } 773 774 @classmethod
775 - def _WriteConfigFile(cls, instance, startup_memory, block_devices):
776 """Create a Xen 3.1 HVM config file. 777 778 """ 779 hvp = instance.hvparams 780 781 config = StringIO() 782 config.write("# this is autogenerated by Ganeti, please do not edit\n#\n") 783 784 # kernel handling 785 kpath = hvp[constants.HV_KERNEL_PATH] 786 config.write("kernel = '%s'\n" % kpath) 787 788 config.write("builder = 'hvm'\n") 789 config.write("memory = %d\n" % startup_memory) 790 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM]) 791 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS]) 792 cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK]) 793 if cpu_pinning: 794 config.write("%s\n" % cpu_pinning) 795 cpu_cap = hvp[constants.HV_CPU_CAP] 796 if cpu_cap: 797 config.write("cpu_cap=%d\n" % cpu_cap) 798 cpu_weight = hvp[constants.HV_CPU_WEIGHT] 799 if cpu_weight: 800 config.write("cpu_weight=%d\n" % cpu_weight) 801 802 config.write("name = '%s'\n" % instance.name) 803 if hvp[constants.HV_PAE]: 804 config.write("pae = 1\n") 805 else: 806 config.write("pae = 0\n") 807 if hvp[constants.HV_ACPI]: 808 config.write("acpi = 1\n") 809 else: 810 config.write("acpi = 0\n") 811 config.write("apic = 1\n") 812 config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL]) 813 config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER]) 814 config.write("sdl = 0\n") 815 config.write("usb = 1\n") 816 config.write("usbdevice = 'tablet'\n") 817 config.write("vnc = 1\n") 818 if hvp[constants.HV_VNC_BIND_ADDRESS] is None: 819 config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS) 820 else: 821 config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS]) 822 823 if instance.network_port > constants.VNC_BASE_PORT: 824 display = instance.network_port - constants.VNC_BASE_PORT 825 config.write("vncdisplay = %s\n" % display) 826 config.write("vncunused = 0\n") 827 else: 828 config.write("# vncdisplay = 1\n") 829 config.write("vncunused = 1\n") 830 831 vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE] 832 try: 833 password = utils.ReadFile(vnc_pwd_file) 834 except EnvironmentError, err: 835 raise errors.HypervisorError("Failed to open VNC password file %s: %s" % 836 (vnc_pwd_file, err)) 837 838 config.write("vncpasswd = '%s'\n" % password.rstrip()) 839 840 config.write("serial = 'pty'\n") 841 if hvp[constants.HV_USE_LOCALTIME]: 842 config.write("localtime = 1\n") 843 844 vif_data = [] 845 # Note: what is called 'nic_type' here, is used as value for the xen nic 846 # vif config parameter 'model'. For the xen nic vif parameter 'type', we use 847 # the 'vif_type' to avoid a clash of notation. 848 nic_type = hvp[constants.HV_NIC_TYPE] 849 850 if nic_type is None: 851 vif_type_str = "" 852 if hvp[constants.HV_VIF_TYPE]: 853 vif_type_str = ", type=%s" % hvp[constants.HV_VIF_TYPE] 854 # ensure old instances don't change 855 nic_type_str = vif_type_str 856 elif nic_type == constants.HT_NIC_PARAVIRTUAL: 857 nic_type_str = ", type=paravirtualized" 858 else: 859 # parameter 'model' is only valid with type 'ioemu' 860 nic_type_str = ", model=%s, type=%s" % \ 861 (nic_type, constants.HT_HVM_VIF_IOEMU) 862 for nic in instance.nics: 863 nic_str = "mac=%s%s" % (nic.mac, nic_type_str) 864 ip = getattr(nic, "ip", None) 865 if ip is not None: 866 nic_str += ", ip=%s" % ip 867 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED: 868 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK] 869 vif_data.append("'%s'" % nic_str) 870 871 config.write("vif = [%s]\n" % ",".join(vif_data)) 872 873 disk_data = cls._GetConfigFileDiskData(block_devices, 874 hvp[constants.HV_BLOCKDEV_PREFIX]) 875 876 iso_path = hvp[constants.HV_CDROM_IMAGE_PATH] 877 if iso_path: 878 iso = "'file:%s,hdc:cdrom,r'" % iso_path 879 disk_data.append(iso) 880 881 config.write("disk = [%s]\n" % (",".join(disk_data))) 882 # Add PCI passthrough 883 pci_pass_arr = [] 884 pci_pass = hvp[constants.HV_PASSTHROUGH] 885 if pci_pass: 886 pci_pass_arr = pci_pass.split(";") 887 config.write("pci = %s\n" % pci_pass_arr) 888 config.write("on_poweroff = 'destroy'\n") 889 if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED: 890 config.write("on_reboot = 'restart'\n") 891 else: 892 config.write("on_reboot = 'destroy'\n") 893 config.write("on_crash = 'restart'\n") 894 cls._WriteConfigFileStatic(instance.name, config.getvalue()) 895 896 return True
897