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 vcluster
37 from ganeti import ssconf
40 XEND_CONFIG_FILE = vcluster.AddNodePrefix("/etc/xen/xend-config.sxp")
41 XL_CONFIG_FILE = vcluster.AddNodePrefix("/etc/xen/xl.conf")
42 VIF_BRIDGE_SCRIPT = vcluster.AddNodePrefix("/etc/xen/scripts/vif-bridge")
43 _DOM0_NAME = "Domain-0"
46 class XenHypervisor(hv_base.BaseHypervisor):
47 """Xen generic hypervisor interface
49 This is the Xen base class used for both Xen PVM and HVM. It contains
50 all the functionality that is identical for both.
54 REBOOT_RETRY_COUNT = 60
55 REBOOT_RETRY_INTERVAL = 10
62 ANCILLARY_FILES_OPT = [
67 def _ConfigFileName(instance_name):
68 """Get the config file name for an instance.
70 @param instance_name: instance name
71 @type instance_name: str
72 @return: fully qualified path to instance config file
76 return "/etc/xen/%s" % instance_name
79 def _WriteConfigFile(cls, instance, startup_memory, block_devices):
80 """Write the Xen config file for the instance.
83 raise NotImplementedError
86 def _WriteConfigFileStatic(instance_name, data):
87 """Write the Xen config file for the instance.
89 This version of the function just writes the config file from static data.
92 # just in case it exists
93 utils.RemoveFile("/etc/xen/auto/%s" % instance_name)
94 cfg_file = XenHypervisor._ConfigFileName(instance_name)
96 utils.WriteFile(cfg_file, data=data)
97 except EnvironmentError, err:
98 raise errors.HypervisorError("Cannot write Xen instance configuration"
99 " file %s: %s" % (cfg_file, err))
102 def _ReadConfigFile(instance_name):
103 """Returns the contents of the instance config file.
107 file_content = utils.ReadFile(
108 XenHypervisor._ConfigFileName(instance_name))
109 except EnvironmentError, err:
110 raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
114 def _RemoveConfigFile(instance_name):
115 """Remove the xen configuration file.
118 utils.RemoveFile(XenHypervisor._ConfigFileName(instance_name))
121 def _CreateConfigCpus(cls, cpu_mask):
122 """Create a CPU config string that's compatible with Xen's
126 # Convert the string CPU mask to a list of list of int's
127 cpu_list = utils.ParseMultiCpuMask(cpu_mask)
129 if len(cpu_list) == 1:
130 all_cpu_mapping = cpu_list[0]
131 if all_cpu_mapping == constants.CPU_PINNING_OFF:
132 # If CPU pinning has 1 entry that's "all", then remove the
133 # parameter from the config file
136 # If CPU pinning has one non-all entry, mapping all vCPUS (the entire
137 # VM) to one physical CPU, using format 'cpu = "C"'
138 return "cpu = \"%s\"" % ",".join(map(str, all_cpu_mapping))
141 def _GetCPUMap(vcpu):
142 if vcpu[0] == constants.CPU_PINNING_ALL_VAL:
143 cpu_map = constants.CPU_PINNING_ALL_XEN
145 cpu_map = ",".join(map(str, vcpu))
146 return "\"%s\"" % cpu_map
148 # build the result string in format 'cpus = [ "c", "c", "c" ]',
149 # where each c is a physical CPU number, a range, a list, or any
151 return "cpus = [ %s ]" % ", ".join(map(_GetCPUMap, cpu_list))
154 def _RunXmList(xmlist_errors):
155 """Helper function for L{_GetXMList} to run "xm list".
158 result = utils.RunCmd([constants.XEN_CMD, "list"])
160 logging.error("xm list failed (%s): %s", result.fail_reason,
162 xmlist_errors.append(result)
163 raise utils.RetryAgain()
165 # skip over the heading
166 return result.stdout.splitlines()[1:]
169 def _GetXMList(cls, include_node):
170 """Return the list of running instances.
172 If the include_node argument is True, then we return information
173 for dom0 also, otherwise we filter that from the return value.
175 @return: list of (name, id, memory, vcpus, state, time spent)
180 lines = utils.Retry(cls._RunXmList, 1, 5, args=(xmlist_errors, ))
181 except utils.RetryTimeout:
183 xmlist_result = xmlist_errors.pop()
185 errmsg = ("xm list failed, timeout exceeded (%s): %s" %
186 (xmlist_result.fail_reason, xmlist_result.output))
188 errmsg = "xm list failed"
190 raise errors.HypervisorError(errmsg)
194 # The format of lines is:
195 # Name ID Mem(MiB) VCPUs State Time(s)
196 # Domain-0 0 3418 4 r----- 266.2
199 raise errors.HypervisorError("Can't parse output of xm list,"
202 data[1] = int(data[1])
203 data[2] = int(data[2])
204 data[3] = int(data[3])
205 data[5] = float(data[5])
206 except (TypeError, ValueError), err:
207 raise errors.HypervisorError("Can't parse output of xm list,"
208 " line: %s, error: %s" % (line, err))
210 # skip the Domain-0 (optional)
211 if include_node or data[0] != _DOM0_NAME:
216 def ListInstances(self):
217 """Get the list of running instances.
220 xm_list = self._GetXMList(False)
221 names = [info[0] for info in xm_list]
224 def GetInstanceInfo(self, instance_name):
225 """Get instance properties.
227 @param instance_name: the instance name
229 @return: tuple (name, id, memory, vcpus, stat, times)
232 xm_list = self._GetXMList(instance_name == _DOM0_NAME)
235 if data[0] == instance_name:
240 def GetAllInstancesInfo(self):
241 """Get properties of all instances.
243 @return: list of tuples (name, id, memory, vcpus, stat, times)
246 xm_list = self._GetXMList(False)
249 def StartInstance(self, instance, block_devices, startup_paused):
250 """Start an instance.
253 startup_memory = self._InstanceStartupMemory(instance)
254 self._WriteConfigFile(instance, startup_memory, block_devices)
255 cmd = [constants.XEN_CMD, "create"]
258 cmd.extend([self._ConfigFileName(instance.name)])
259 result = utils.RunCmd(cmd)
262 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
263 (instance.name, result.fail_reason,
266 def StopInstance(self, instance, force=False, retry=False, name=None):
272 self._RemoveConfigFile(name)
274 command = [constants.XEN_CMD, "destroy", name]
276 command = [constants.XEN_CMD, "shutdown", name]
277 result = utils.RunCmd(command)
280 raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
281 (name, result.fail_reason, result.output))
283 def RebootInstance(self, instance):
284 """Reboot an instance.
287 ini_info = self.GetInstanceInfo(instance.name)
290 raise errors.HypervisorError("Failed to reboot instance %s,"
291 " not running" % instance.name)
293 result = utils.RunCmd([constants.XEN_CMD, "reboot", instance.name])
295 raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
296 (instance.name, result.fail_reason,
299 def _CheckInstance():
300 new_info = self.GetInstanceInfo(instance.name)
302 # check if the domain ID has changed or the run time has decreased
303 if (new_info is not None and
304 (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])):
307 raise utils.RetryAgain()
310 utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
311 self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
312 except utils.RetryTimeout:
313 raise errors.HypervisorError("Failed to reboot instance %s: instance"
314 " did not reboot in the expected interval" %
317 def BalloonInstanceMemory(self, instance, mem):
318 """Balloon an instance memory to a certain value.
320 @type instance: L{objects.Instance}
321 @param instance: instance to be accepted
323 @param mem: actual memory size to use for instance runtime
326 cmd = [constants.XEN_CMD, "mem-set", instance.name, mem]
327 result = utils.RunCmd(cmd)
329 raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" %
330 (instance.name, result.fail_reason,
332 cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem]
333 cmd.append(XenHypervisor._ConfigFileName(instance.name))
334 result = utils.RunCmd(cmd)
336 raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" %
337 (instance.name, result.fail_reason,
340 def GetNodeInfo(self):
341 """Return information about the node.
343 @return: a dict with the following keys (memory values in MiB):
344 - memory_total: the total memory size on the node
345 - memory_free: the available memory on the node for instances
346 - memory_dom0: the memory used by the node itself, if available
347 - nr_cpus: total number of CPUs
348 - nr_nodes: in a NUMA system, the number of domains
349 - nr_sockets: the number of physical CPU sockets in the node
350 - hv_version: the hypervisor version in the form (major, minor)
353 result = utils.RunCmd([constants.XEN_CMD, "info"])
355 logging.error("Can't run 'xm info' (%s): %s", result.fail_reason,
359 xmoutput = result.stdout.splitlines()
361 cores_per_socket = threads_per_core = nr_cpus = None
362 xen_major, xen_minor = None, None
366 for line in xmoutput:
367 splitfields = line.split(":", 1)
369 if len(splitfields) > 1:
370 key = splitfields[0].strip()
371 val = splitfields[1].strip()
373 # note: in xen 3, memory has changed to total_memory
374 if key == "memory" or key == "total_memory":
375 memory_total = int(val)
376 elif key == "free_memory":
377 memory_free = int(val)
378 elif key == "nr_cpus":
379 nr_cpus = result["cpu_total"] = int(val)
380 elif key == "nr_nodes":
381 result["cpu_nodes"] = int(val)
382 elif key == "cores_per_socket":
383 cores_per_socket = int(val)
384 elif key == "threads_per_core":
385 threads_per_core = int(val)
386 elif key == "xen_major":
388 elif key == "xen_minor":
391 if None not in [cores_per_socket, threads_per_core, nr_cpus]:
392 result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
395 for (name, _, mem, vcpus, _, _) in self._GetXMList(True):
396 if name == _DOM0_NAME:
397 result["memory_dom0"] = mem
398 result["dom0_cpus"] = vcpus
400 # Include Dom0 in total memory usage
403 if memory_free is not None:
404 result["memory_free"] = memory_free
406 if memory_total is not None:
407 result["memory_total"] = memory_total
409 # Calculate memory used by hypervisor
410 if None not in [memory_total, memory_free, total_instmem]:
411 result["memory_hv"] = memory_total - memory_free - total_instmem
413 if not (xen_major is None or xen_minor is None):
414 result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
419 def GetInstanceConsole(cls, instance, hvparams, beparams):
420 """Return a command for connecting to the console of an instance.
423 return objects.InstanceConsole(instance=instance.name,
424 kind=constants.CONS_SSH,
425 host=instance.primary_node,
426 user=constants.SSH_CONSOLE_USER,
427 command=[pathutils.XEN_CONSOLE_WRAPPER,
428 constants.XEN_CMD, instance.name])
431 """Verify the hypervisor.
433 For Xen, this verifies that the xend process is running.
436 result = utils.RunCmd([constants.XEN_CMD, "info"])
438 return "'xm info' failed: %s, %s" % (result.fail_reason, result.output)
441 def _GetConfigFileDiskData(block_devices, blockdev_prefix):
442 """Get disk directive for xen config file.
444 This method builds the xen config disk directive according to the
445 given disk_template and block_devices.
447 @param block_devices: list of tuples (cfdev, rldev):
448 - cfdev: dict containing ganeti config disk part
449 - rldev: ganeti.bdev.BlockDev object
450 @param blockdev_prefix: a string containing blockdevice prefix,
451 e.g. "sd" for /dev/sda
453 @return: string containing disk directive for xen instance config file
457 constants.FD_LOOP: "file",
458 constants.FD_BLKTAP: "tap:aio",
461 if len(block_devices) > 24:
463 raise errors.HypervisorError("Too many disks")
464 namespace = [blockdev_prefix + chr(i + ord("a")) for i in range(24)]
465 for sd_name, (cfdev, dev_path) in zip(namespace, block_devices):
466 if cfdev.mode == constants.DISK_RDWR:
470 if cfdev.dev_type == constants.LD_FILE:
471 line = "'%s:%s,%s,%s'" % (FILE_DRIVER_MAP[cfdev.physical_id[0]],
472 dev_path, sd_name, mode)
474 line = "'phy:%s,%s,%s'" % (dev_path, sd_name, mode)
475 disk_data.append(line)
479 def MigrationInfo(self, instance):
480 """Get instance information to perform a migration.
482 @type instance: L{objects.Instance}
483 @param instance: instance to be migrated
485 @return: content of the xen config file
488 return self._ReadConfigFile(instance.name)
490 def AcceptInstance(self, instance, info, target):
491 """Prepare to accept an instance.
493 @type instance: L{objects.Instance}
494 @param instance: instance to be accepted
496 @param info: content of the xen config file on the source node
498 @param target: target host (usually ip), on this node
503 def FinalizeMigrationDst(self, instance, info, success):
504 """Finalize an instance migration.
506 After a successful migration we write the xen config file.
507 We do nothing on a failure, as we did not change anything at accept time.
509 @type instance: L{objects.Instance}
510 @param instance: instance whose migration is being finalized
512 @param info: content of the xen config file on the source node
513 @type success: boolean
514 @param success: whether the migration was a success or a failure
518 self._WriteConfigFileStatic(instance.name, info)
520 def MigrateInstance(self, instance, target, live):
521 """Migrate an instance to a target node.
523 The migration will not be attempted if the instance is not
526 @type instance: L{objects.Instance}
527 @param instance: the instance to be migrated
529 @param target: ip address of the target node
531 @param live: perform a live migration
534 if self.GetInstanceInfo(instance.name) is None:
535 raise errors.HypervisorError("Instance not running, cannot migrate")
537 port = instance.hvparams[constants.HV_MIGRATION_PORT]
539 if (constants.XEN_CMD == constants.XEN_CMD_XM and
540 not netutils.TcpPing(target, port, live_port_needed=True)):
541 raise errors.HypervisorError("Remote host %s not listening on port"
542 " %s, cannot migrate" % (target, port))
544 # FIXME: migrate must be upgraded for transitioning to "xl" (xen 4.1).
545 # This should be reworked in Ganeti 2.7
546 # ssh must recognize the key of the target host for the migration
547 args = [constants.XEN_CMD, "migrate"]
548 if constants.XEN_CMD == constants.XEN_CMD_XM:
549 args.extend(["-p", "%d" % port])
552 elif constants.XEN_CMD == constants.XEN_CMD_XL:
553 cluster_name = ssconf.SimpleStore().GetClusterName()
554 args.extend(["-s", constants.XL_SSH_CMD % cluster_name])
555 args.extend(["-C", self._ConfigFileName(instance.name)])
557 raise errors.HypervisorError("Unsupported xen command: %s" %
560 args.extend([instance.name, target])
561 result = utils.RunCmd(args)
563 raise errors.HypervisorError("Failed to migrate instance %s: %s" %
564 (instance.name, result.output))
566 def FinalizeMigrationSource(self, instance, success, live):
567 """Finalize the instance migration on the source node.
569 @type instance: L{objects.Instance}
570 @param instance: the instance that was migrated
572 @param success: whether the migration succeeded or not
574 @param live: whether the user requested a live migration or not
577 # pylint: disable=W0613
579 # remove old xen file after migration succeeded
581 self._RemoveConfigFile(instance.name)
582 except EnvironmentError:
583 logging.exception("Failure while removing instance config file")
585 def GetMigrationStatus(self, instance):
586 """Get the migration status
588 As MigrateInstance for Xen is still blocking, if this method is called it
589 means that MigrateInstance has completed successfully. So we can safely
590 assume that the migration was successful and notify this fact to the client.
592 @type instance: L{objects.Instance}
593 @param instance: the instance that is being migrated
594 @rtype: L{objects.MigrationStatus}
595 @return: the status of the current migration (one of
596 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
597 progress info that can be retrieved from the hypervisor
600 return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
603 def PowercycleNode(cls):
604 """Xen-specific powercycle.
606 This first does a Linux reboot (which triggers automatically a Xen
607 reboot), and if that fails it tries to do a Xen reboot. The reason
608 we don't try a Xen reboot first is that the xen reboot launches an
609 external command which connects to the Xen hypervisor, and that
610 won't work in case the root filesystem is broken and/or the xend
611 daemon is not working.
615 cls.LinuxPowercycle()
617 utils.RunCmd([constants.XEN_CMD, "debug", "R"])
620 class XenPvmHypervisor(XenHypervisor):
621 """Xen PVM hypervisor interface"""
624 constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
625 constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
626 constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
627 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
628 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
629 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
630 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
631 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
632 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
633 # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
634 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
635 constants.HV_REBOOT_BEHAVIOR:
636 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
637 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
638 constants.HV_CPU_CAP: hv_base.NO_CHECK,
639 constants.HV_CPU_WEIGHT:
640 (False, lambda x: 0 < x < 65536, "invalid weight", None, None),
644 def _WriteConfigFile(cls, instance, startup_memory, block_devices):
645 """Write the Xen config file for the instance.
648 hvp = instance.hvparams
650 config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
652 # if bootloader is True, use bootloader instead of kernel and ramdisk
654 if hvp[constants.HV_USE_BOOTLOADER]:
655 # bootloader handling
656 bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
658 config.write("bootloader = '%s'\n" % bootloader_path)
660 raise errors.HypervisorError("Bootloader enabled, but missing"
663 bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
665 config.write("bootargs = '%s'\n" % bootloader_args)
668 kpath = hvp[constants.HV_KERNEL_PATH]
669 config.write("kernel = '%s'\n" % kpath)
672 initrd_path = hvp[constants.HV_INITRD_PATH]
674 config.write("ramdisk = '%s'\n" % initrd_path)
676 # rest of the settings
677 config.write("memory = %d\n" % startup_memory)
678 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
679 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
680 cpu_pinning = cls._CreateConfigCpus(hvp[constants.HV_CPU_MASK])
682 config.write("%s\n" % cpu_pinning)
683 cpu_cap = hvp[constants.HV_CPU_CAP]
685 config.write("cpu_cap=%d\n" % cpu_cap)
686 cpu_weight = hvp[constants.HV_CPU_WEIGHT]
688 config.write("cpu_weight=%d\n" % cpu_weight)
690 config.write("name = '%s'\n" % instance.name)
693 for nic in instance.nics:
694 nic_str = "mac=%s" % (nic.mac)
695 ip = getattr(nic, "ip", None)
697 nic_str += ", ip=%s" % ip
698 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
699 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
700 vif_data.append("'%s'" % nic_str)
702 disk_data = cls._GetConfigFileDiskData(block_devices,
703 hvp[constants.HV_BLOCKDEV_PREFIX])
705 config.write("vif = [%s]\n" % ",".join(vif_data))
706 config.write("disk = [%s]\n" % ",".join(disk_data))
708 if hvp[constants.HV_ROOT_PATH]:
709 config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
710 config.write("on_poweroff = 'destroy'\n")
711 if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
712 config.write("on_reboot = 'restart'\n")
714 config.write("on_reboot = 'destroy'\n")
715 config.write("on_crash = 'restart'\n")
716 config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
717 cls._WriteConfigFileStatic(instance.name, config.getvalue())
722 class XenHvmHypervisor(XenHypervisor):
723 """Xen HVM hypervisor interface"""
725 ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
726 pathutils.VNC_PASSWORD_FILE,
728 ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [
729 pathutils.VNC_PASSWORD_FILE,
733 constants.HV_ACPI: hv_base.NO_CHECK,
734 constants.HV_BOOT_ORDER: (True, ) +
735 (lambda x: x and len(x.strip("acdn")) == 0,
736 "Invalid boot order specified, must be one or more of [acdn]",
738 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
739 constants.HV_DISK_TYPE:
740 hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
741 constants.HV_NIC_TYPE:
742 hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
743 constants.HV_PAE: hv_base.NO_CHECK,
744 constants.HV_VNC_BIND_ADDRESS:
745 (False, netutils.IP4Address.IsValid,
746 "VNC bind address is not a valid IP address", None, None),
747 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
748 constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
749 constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
750 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
751 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
752 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
753 # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
754 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
755 # Add PCI passthrough
756 constants.HV_PASSTHROUGH: hv_base.NO_CHECK,
757 constants.HV_REBOOT_BEHAVIOR:
758 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
759 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
760 constants.HV_CPU_CAP: hv_base.NO_CHECK,
761 constants.HV_CPU_WEIGHT:
762 (False, lambda x: 0 < x < 65535, "invalid weight", None, None),
766 def _WriteConfigFile(cls, instance, startup_memory, block_devices):
767 """Create a Xen 3.1 HVM config file.
770 hvp = instance.hvparams
773 config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
776 kpath = hvp[constants.HV_KERNEL_PATH]
777 config.write("kernel = '%s'\n" % kpath)
779 config.write("builder = 'hvm'\n")
780 config.write("memory = %d\n" % startup_memory)
781 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
782 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
783 cpu_pinning = cls._CreateConfigCpus(hvp[constants.HV_CPU_MASK])
785 config.write("%s\n" % cpu_pinning)
786 cpu_cap = hvp[constants.HV_CPU_CAP]
788 config.write("cpu_cap=%d\n" % cpu_cap)
789 cpu_weight = hvp[constants.HV_CPU_WEIGHT]
791 config.write("cpu_weight=%d\n" % cpu_weight)
793 config.write("name = '%s'\n" % instance.name)
794 if hvp[constants.HV_PAE]:
795 config.write("pae = 1\n")
797 config.write("pae = 0\n")
798 if hvp[constants.HV_ACPI]:
799 config.write("acpi = 1\n")
801 config.write("acpi = 0\n")
802 config.write("apic = 1\n")
803 config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
804 config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
805 config.write("sdl = 0\n")
806 config.write("usb = 1\n")
807 config.write("usbdevice = 'tablet'\n")
808 config.write("vnc = 1\n")
809 if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
810 config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
812 config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
814 if instance.network_port > constants.VNC_BASE_PORT:
815 display = instance.network_port - constants.VNC_BASE_PORT
816 config.write("vncdisplay = %s\n" % display)
817 config.write("vncunused = 0\n")
819 config.write("# vncdisplay = 1\n")
820 config.write("vncunused = 1\n")
822 vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
824 password = utils.ReadFile(vnc_pwd_file)
825 except EnvironmentError, err:
826 raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
829 config.write("vncpasswd = '%s'\n" % password.rstrip())
831 config.write("serial = 'pty'\n")
832 if hvp[constants.HV_USE_LOCALTIME]:
833 config.write("localtime = 1\n")
836 nic_type = hvp[constants.HV_NIC_TYPE]
838 # ensure old instances don't change
839 nic_type_str = ", type=ioemu"
840 elif nic_type == constants.HT_NIC_PARAVIRTUAL:
841 nic_type_str = ", type=paravirtualized"
843 nic_type_str = ", model=%s, type=ioemu" % nic_type
844 for nic in instance.nics:
845 nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
846 ip = getattr(nic, "ip", None)
848 nic_str += ", ip=%s" % ip
849 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
850 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
851 vif_data.append("'%s'" % nic_str)
853 config.write("vif = [%s]\n" % ",".join(vif_data))
855 disk_data = cls._GetConfigFileDiskData(block_devices,
856 hvp[constants.HV_BLOCKDEV_PREFIX])
858 iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
860 iso = "'file:%s,hdc:cdrom,r'" % iso_path
861 disk_data.append(iso)
863 config.write("disk = [%s]\n" % (",".join(disk_data)))
864 # Add PCI passthrough
866 pci_pass = hvp[constants.HV_PASSTHROUGH]
868 pci_pass_arr = pci_pass.split(";")
869 config.write("pci = %s\n" % pci_pass_arr)
870 config.write("on_poweroff = 'destroy'\n")
871 if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
872 config.write("on_reboot = 'restart'\n")
874 config.write("on_reboot = 'destroy'\n")
875 config.write("on_crash = 'restart'\n")
876 cls._WriteConfigFileStatic(instance.name, config.getvalue())