4 # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Google Inc.
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.
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.
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
27 from cStringIO import StringIO
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
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,
43 _DOM0_NAME = "Domain-0"
46 def _CreateConfigCpus(cpu_mask):
47 """Create a CPU config string for Xen's config file.
50 # Convert the string CPU mask to a list of list of int's
51 cpu_list = utils.ParseMultiCpuMask(cpu_mask)
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
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))
66 if vcpu[0] == constants.CPU_PINNING_ALL_VAL:
67 cpu_map = constants.CPU_PINNING_ALL_XEN
69 cpu_map = ",".join(map(str, vcpu))
70 return "\"%s\"" % cpu_map
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
75 return "cpus = [ %s ]" % ", ".join(map(_GetCPUMap, cpu_list))
78 class XenHypervisor(hv_base.BaseHypervisor):
79 """Xen generic hypervisor interface
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.
86 REBOOT_RETRY_COUNT = 60
87 REBOOT_RETRY_INTERVAL = 10
94 ANCILLARY_FILES_OPT = [
99 def _ConfigFileName(instance_name):
100 """Get the config file name for an instance.
102 @param instance_name: instance name
103 @type instance_name: str
104 @return: fully qualified path to instance config file
108 return utils.PathJoin(pathutils.XEN_CONFIG_DIR, instance_name)
111 def _WriteConfigFile(cls, instance, startup_memory, block_devices):
112 """Write the Xen config file for the instance.
115 raise NotImplementedError
118 def _WriteConfigFileStatic(instance_name, data):
119 """Write the Xen config file for the instance.
121 This version of the function just writes the config file from static data.
124 # just in case it exists
125 utils.RemoveFile(utils.PathJoin(pathutils.XEN_CONFIG_DIR, "auto",
128 cfg_file = XenHypervisor._ConfigFileName(instance_name)
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))
136 def _ReadConfigFile(instance_name):
137 """Returns the contents of the instance config file.
140 filename = XenHypervisor._ConfigFileName(instance_name)
143 file_content = utils.ReadFile(filename)
144 except EnvironmentError, err:
145 raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
150 def _RemoveConfigFile(instance_name):
151 """Remove the xen configuration file.
154 utils.RemoveFile(XenHypervisor._ConfigFileName(instance_name))
157 def _RunXmList(xmlist_errors):
158 """Helper function for L{_GetXMList} to run "xm list".
161 result = utils.RunCmd([constants.XEN_CMD, "list"])
163 logging.error("xm list failed (%s): %s", result.fail_reason,
165 xmlist_errors.append(result)
166 raise utils.RetryAgain()
168 # skip over the heading
169 return result.stdout.splitlines()[1:]
172 def _GetXMList(cls, include_node):
173 """Return the list of running instances.
175 If the include_node argument is True, then we return information
176 for dom0 also, otherwise we filter that from the return value.
178 @return: list of (name, id, memory, vcpus, state, time spent)
183 lines = utils.Retry(cls._RunXmList, 1, 5, args=(xmlist_errors, ))
184 except utils.RetryTimeout:
186 xmlist_result = xmlist_errors.pop()
188 errmsg = ("xm list failed, timeout exceeded (%s): %s" %
189 (xmlist_result.fail_reason, xmlist_result.output))
191 errmsg = "xm list failed"
193 raise errors.HypervisorError(errmsg)
197 # The format of lines is:
198 # Name ID Mem(MiB) VCPUs State Time(s)
199 # Domain-0 0 3418 4 r----- 266.2
202 raise errors.HypervisorError("Can't parse output of xm list,"
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))
213 # skip the Domain-0 (optional)
214 if include_node or data[0] != _DOM0_NAME:
219 def ListInstances(self):
220 """Get the list of running instances.
223 xm_list = self._GetXMList(False)
224 names = [info[0] for info in xm_list]
227 def GetInstanceInfo(self, instance_name):
228 """Get instance properties.
230 @param instance_name: the instance name
232 @return: tuple (name, id, memory, vcpus, stat, times)
235 xm_list = self._GetXMList(instance_name == _DOM0_NAME)
238 if data[0] == instance_name:
243 def GetAllInstancesInfo(self):
244 """Get properties of all instances.
246 @return: list of tuples (name, id, memory, vcpus, stat, times)
249 xm_list = self._GetXMList(False)
252 def StartInstance(self, instance, block_devices, startup_paused):
253 """Start an instance.
256 startup_memory = self._InstanceStartupMemory(instance)
257 self._WriteConfigFile(instance, startup_memory, block_devices)
258 cmd = [constants.XEN_CMD, "create"]
261 cmd.extend([self._ConfigFileName(instance.name)])
262 result = utils.RunCmd(cmd)
265 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
266 (instance.name, result.fail_reason,
269 def StopInstance(self, instance, force=False, retry=False, name=None):
277 command = [constants.XEN_CMD, "destroy", name]
279 command = [constants.XEN_CMD, "shutdown", name]
280 result = utils.RunCmd(command)
283 raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
284 (name, result.fail_reason, result.output))
286 # Remove configuration file if stopping/starting instance was successful
287 self._RemoveConfigFile(name)
289 def RebootInstance(self, instance):
290 """Reboot an instance.
293 ini_info = self.GetInstanceInfo(instance.name)
296 raise errors.HypervisorError("Failed to reboot instance %s,"
297 " not running" % instance.name)
299 result = utils.RunCmd([constants.XEN_CMD, "reboot", instance.name])
301 raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
302 (instance.name, result.fail_reason,
305 def _CheckInstance():
306 new_info = self.GetInstanceInfo(instance.name)
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])):
313 raise utils.RetryAgain()
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" %
323 def BalloonInstanceMemory(self, instance, mem):
324 """Balloon an instance memory to a certain value.
326 @type instance: L{objects.Instance}
327 @param instance: instance to be accepted
329 @param mem: actual memory size to use for instance runtime
332 cmd = [constants.XEN_CMD, "mem-set", instance.name, mem]
333 result = utils.RunCmd(cmd)
335 raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" %
336 (instance.name, result.fail_reason,
338 cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem]
339 cmd.append(XenHypervisor._ConfigFileName(instance.name))
340 result = utils.RunCmd(cmd)
342 raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" %
343 (instance.name, result.fail_reason,
346 def GetNodeInfo(self):
347 """Return information about the node.
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)
359 result = utils.RunCmd([constants.XEN_CMD, "info"])
361 logging.error("Can't run 'xm info' (%s): %s", result.fail_reason,
365 xmoutput = result.stdout.splitlines()
367 cores_per_socket = threads_per_core = nr_cpus = None
368 xen_major, xen_minor = None, None
372 for line in xmoutput:
373 splitfields = line.split(":", 1)
375 if len(splitfields) > 1:
376 key = splitfields[0].strip()
377 val = splitfields[1].strip()
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":
394 elif key == "xen_minor":
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)
401 for (name, _, mem, vcpus, _, _) in self._GetXMList(True):
402 if name == _DOM0_NAME:
403 result["memory_dom0"] = mem
404 result["dom0_cpus"] = vcpus
406 # Include Dom0 in total memory usage
409 if memory_free is not None:
410 result["memory_free"] = memory_free
412 if memory_total is not None:
413 result["memory_total"] = memory_total
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
419 if not (xen_major is None or xen_minor is None):
420 result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
425 def GetInstanceConsole(cls, instance, hvparams, beparams):
426 """Return a command for connecting to the console of an instance.
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])
437 """Verify the hypervisor.
439 For Xen, this verifies that the xend process is running.
441 @return: Problem description if something is wrong, C{None} otherwise
444 result = utils.RunCmd([constants.XEN_CMD, "info"])
446 return "'xm info' failed: %s, %s" % (result.fail_reason, result.output)
451 def _GetConfigFileDiskData(block_devices, blockdev_prefix):
452 """Get disk directive for xen config file.
454 This method builds the xen config disk directive according to the
455 given disk_template and block_devices.
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
463 @return: string containing disk directive for xen instance config file
467 constants.FD_LOOP: "file",
468 constants.FD_BLKTAP: "tap:aio",
471 if len(block_devices) > 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:
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)
484 line = "'phy:%s,%s,%s'" % (dev_path, sd_name, mode)
485 disk_data.append(line)
489 def MigrationInfo(self, instance):
490 """Get instance information to perform a migration.
492 @type instance: L{objects.Instance}
493 @param instance: instance to be migrated
495 @return: content of the xen config file
498 return self._ReadConfigFile(instance.name)
500 def AcceptInstance(self, instance, info, target):
501 """Prepare to accept an instance.
503 @type instance: L{objects.Instance}
504 @param instance: instance to be accepted
506 @param info: content of the xen config file on the source node
508 @param target: target host (usually ip), on this node
513 def FinalizeMigrationDst(self, instance, info, success):
514 """Finalize an instance migration.
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.
519 @type instance: L{objects.Instance}
520 @param instance: instance whose migration is being finalized
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
528 self._WriteConfigFileStatic(instance.name, info)
530 def MigrateInstance(self, instance, target, live):
531 """Migrate an instance to a target node.
533 The migration will not be attempted if the instance is not
536 @type instance: L{objects.Instance}
537 @param instance: the instance to be migrated
539 @param target: ip address of the target node
541 @param live: perform a live migration
544 if self.GetInstanceInfo(instance.name) is None:
545 raise errors.HypervisorError("Instance not running, cannot migrate")
547 port = instance.hvparams[constants.HV_MIGRATION_PORT]
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))
554 args = [constants.XEN_CMD, "migrate"]
555 if constants.XEN_CMD == constants.XEN_CMD_XM:
556 args.extend(["-p", "%d" % port])
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)])
564 raise errors.HypervisorError("Unsupported xen command: %s" %
567 args.extend([instance.name, target])
568 result = utils.RunCmd(args)
570 raise errors.HypervisorError("Failed to migrate instance %s: %s" %
571 (instance.name, result.output))
573 def FinalizeMigrationSource(self, instance, success, live):
574 """Finalize the instance migration on the source node.
576 @type instance: L{objects.Instance}
577 @param instance: the instance that was migrated
579 @param success: whether the migration succeeded or not
581 @param live: whether the user requested a live migration or not
584 # pylint: disable=W0613
586 # remove old xen file after migration succeeded
588 self._RemoveConfigFile(instance.name)
589 except EnvironmentError:
590 logging.exception("Failure while removing instance config file")
592 def GetMigrationStatus(self, instance):
593 """Get the migration status
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.
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
607 return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
610 def PowercycleNode(cls):
611 """Xen-specific powercycle.
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.
622 cls.LinuxPowercycle()
624 utils.RunCmd([constants.XEN_CMD, "debug", "R"])
627 class XenPvmHypervisor(XenHypervisor):
628 """Xen PVM hypervisor interface"""
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),
651 def _WriteConfigFile(cls, instance, startup_memory, block_devices):
652 """Write the Xen config file for the instance.
655 hvp = instance.hvparams
657 config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
659 # if bootloader is True, use bootloader instead of kernel and ramdisk
661 if hvp[constants.HV_USE_BOOTLOADER]:
662 # bootloader handling
663 bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
665 config.write("bootloader = '%s'\n" % bootloader_path)
667 raise errors.HypervisorError("Bootloader enabled, but missing"
670 bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
672 config.write("bootargs = '%s'\n" % bootloader_args)
675 kpath = hvp[constants.HV_KERNEL_PATH]
676 config.write("kernel = '%s'\n" % kpath)
679 initrd_path = hvp[constants.HV_INITRD_PATH]
681 config.write("ramdisk = '%s'\n" % initrd_path)
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])
689 config.write("%s\n" % cpu_pinning)
690 cpu_cap = hvp[constants.HV_CPU_CAP]
692 config.write("cpu_cap=%d\n" % cpu_cap)
693 cpu_weight = hvp[constants.HV_CPU_WEIGHT]
695 config.write("cpu_weight=%d\n" % cpu_weight)
697 config.write("name = '%s'\n" % instance.name)
700 for nic in instance.nics:
701 nic_str = "mac=%s" % (nic.mac)
702 ip = getattr(nic, "ip", 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)
709 disk_data = cls._GetConfigFileDiskData(block_devices,
710 hvp[constants.HV_BLOCKDEV_PREFIX])
712 config.write("vif = [%s]\n" % ",".join(vif_data))
713 config.write("disk = [%s]\n" % ",".join(disk_data))
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")
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())
729 class XenHvmHypervisor(XenHypervisor):
730 """Xen HVM hypervisor interface"""
732 ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
733 pathutils.VNC_PASSWORD_FILE,
735 ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [
736 pathutils.VNC_PASSWORD_FILE,
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]",
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),
773 def _WriteConfigFile(cls, instance, startup_memory, block_devices):
774 """Create a Xen 3.1 HVM config file.
777 hvp = instance.hvparams
780 config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
783 kpath = hvp[constants.HV_KERNEL_PATH]
784 config.write("kernel = '%s'\n" % kpath)
786 config.write("builder = 'hvm'\n")
787 config.write("memory = %d\n" % startup_memory)
788 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
789 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
790 cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
792 config.write("%s\n" % cpu_pinning)
793 cpu_cap = hvp[constants.HV_CPU_CAP]
795 config.write("cpu_cap=%d\n" % cpu_cap)
796 cpu_weight = hvp[constants.HV_CPU_WEIGHT]
798 config.write("cpu_weight=%d\n" % cpu_weight)
800 config.write("name = '%s'\n" % instance.name)
801 if hvp[constants.HV_PAE]:
802 config.write("pae = 1\n")
804 config.write("pae = 0\n")
805 if hvp[constants.HV_ACPI]:
806 config.write("acpi = 1\n")
808 config.write("acpi = 0\n")
809 config.write("apic = 1\n")
810 config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
811 config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
812 config.write("sdl = 0\n")
813 config.write("usb = 1\n")
814 config.write("usbdevice = 'tablet'\n")
815 config.write("vnc = 1\n")
816 if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
817 config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
819 config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
821 if instance.network_port > constants.VNC_BASE_PORT:
822 display = instance.network_port - constants.VNC_BASE_PORT
823 config.write("vncdisplay = %s\n" % display)
824 config.write("vncunused = 0\n")
826 config.write("# vncdisplay = 1\n")
827 config.write("vncunused = 1\n")
829 vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
831 password = utils.ReadFile(vnc_pwd_file)
832 except EnvironmentError, err:
833 raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
836 config.write("vncpasswd = '%s'\n" % password.rstrip())
838 config.write("serial = 'pty'\n")
839 if hvp[constants.HV_USE_LOCALTIME]:
840 config.write("localtime = 1\n")
843 nic_type = hvp[constants.HV_NIC_TYPE]
845 # ensure old instances don't change
846 nic_type_str = ", type=ioemu"
847 elif nic_type == constants.HT_NIC_PARAVIRTUAL:
848 nic_type_str = ", type=paravirtualized"
850 nic_type_str = ", model=%s, type=ioemu" % nic_type
851 for nic in instance.nics:
852 nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
853 ip = getattr(nic, "ip", None)
855 nic_str += ", ip=%s" % ip
856 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
857 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
858 vif_data.append("'%s'" % nic_str)
860 config.write("vif = [%s]\n" % ",".join(vif_data))
862 disk_data = cls._GetConfigFileDiskData(block_devices,
863 hvp[constants.HV_BLOCKDEV_PREFIX])
865 iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
867 iso = "'file:%s,hdc:cdrom,r'" % iso_path
868 disk_data.append(iso)
870 config.write("disk = [%s]\n" % (",".join(disk_data)))
871 # Add PCI passthrough
873 pci_pass = hvp[constants.HV_PASSTHROUGH]
875 pci_pass_arr = pci_pass.split(";")
876 config.write("pci = %s\n" % pci_pass_arr)
877 config.write("on_poweroff = 'destroy'\n")
878 if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
879 config.write("on_reboot = 'restart'\n")
881 config.write("on_reboot = 'destroy'\n")
882 config.write("on_crash = 'restart'\n")
883 cls._WriteConfigFileStatic(instance.name, config.getvalue())