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))
140 def _GetCPUMap(vcpu):
141 if vcpu[0] == constants.CPU_PINNING_ALL_VAL:
142 cpu_map = constants.CPU_PINNING_ALL_XEN
144 cpu_map = ",".join(map(str, vcpu))
145 return "\"%s\"" % cpu_map
147 # build the result string in format 'cpus = [ "c", "c", "c" ]',
148 # where each c is a physical CPU number, a range, a list, or any
150 return "cpus = [ %s ]" % ", ".join(map(_GetCPUMap, cpu_list))
153 def _RunXmList(xmlist_errors):
154 """Helper function for L{_GetXMList} to run "xm list".
157 result = utils.RunCmd([constants.XEN_CMD, "list"])
159 logging.error("xm list failed (%s): %s", result.fail_reason,
161 xmlist_errors.append(result)
162 raise utils.RetryAgain()
164 # skip over the heading
165 return result.stdout.splitlines()[1:]
168 def _GetXMList(cls, include_node):
169 """Return the list of running instances.
171 If the include_node argument is True, then we return information
172 for dom0 also, otherwise we filter that from the return value.
174 @return: list of (name, id, memory, vcpus, state, time spent)
179 lines = utils.Retry(cls._RunXmList, 1, 5, args=(xmlist_errors, ))
180 except utils.RetryTimeout:
182 xmlist_result = xmlist_errors.pop()
184 errmsg = ("xm list failed, timeout exceeded (%s): %s" %
185 (xmlist_result.fail_reason, xmlist_result.output))
187 errmsg = "xm list failed"
189 raise errors.HypervisorError(errmsg)
193 # The format of lines is:
194 # Name ID Mem(MiB) VCPUs State Time(s)
195 # Domain-0 0 3418 4 r----- 266.2
198 raise errors.HypervisorError("Can't parse output of xm list,"
201 data[1] = int(data[1])
202 data[2] = int(data[2])
203 data[3] = int(data[3])
204 data[5] = float(data[5])
205 except (TypeError, ValueError), err:
206 raise errors.HypervisorError("Can't parse output of xm list,"
207 " line: %s, error: %s" % (line, err))
209 # skip the Domain-0 (optional)
210 if include_node or data[0] != _DOM0_NAME:
215 def ListInstances(self):
216 """Get the list of running instances.
219 xm_list = self._GetXMList(False)
220 names = [info[0] for info in xm_list]
223 def GetInstanceInfo(self, instance_name):
224 """Get instance properties.
226 @param instance_name: the instance name
228 @return: tuple (name, id, memory, vcpus, stat, times)
231 xm_list = self._GetXMList(instance_name == _DOM0_NAME)
234 if data[0] == instance_name:
239 def GetAllInstancesInfo(self):
240 """Get properties of all instances.
242 @return: list of tuples (name, id, memory, vcpus, stat, times)
245 xm_list = self._GetXMList(False)
248 def StartInstance(self, instance, block_devices, startup_paused):
249 """Start an instance.
252 startup_memory = self._InstanceStartupMemory(instance)
253 self._WriteConfigFile(instance, startup_memory, block_devices)
254 cmd = [constants.XEN_CMD, "create"]
257 cmd.extend([self._ConfigFileName(instance.name)])
258 result = utils.RunCmd(cmd)
261 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
262 (instance.name, result.fail_reason,
265 def StopInstance(self, instance, force=False, retry=False, name=None):
271 self._RemoveConfigFile(name)
273 command = [constants.XEN_CMD, "destroy", name]
275 command = [constants.XEN_CMD, "shutdown", name]
276 result = utils.RunCmd(command)
279 raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
280 (name, result.fail_reason, result.output))
282 def RebootInstance(self, instance):
283 """Reboot an instance.
286 ini_info = self.GetInstanceInfo(instance.name)
289 raise errors.HypervisorError("Failed to reboot instance %s,"
290 " not running" % instance.name)
292 result = utils.RunCmd([constants.XEN_CMD, "reboot", instance.name])
294 raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
295 (instance.name, result.fail_reason,
298 def _CheckInstance():
299 new_info = self.GetInstanceInfo(instance.name)
301 # check if the domain ID has changed or the run time has decreased
302 if (new_info is not None and
303 (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])):
306 raise utils.RetryAgain()
309 utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
310 self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
311 except utils.RetryTimeout:
312 raise errors.HypervisorError("Failed to reboot instance %s: instance"
313 " did not reboot in the expected interval" %
316 def BalloonInstanceMemory(self, instance, mem):
317 """Balloon an instance memory to a certain value.
319 @type instance: L{objects.Instance}
320 @param instance: instance to be accepted
322 @param mem: actual memory size to use for instance runtime
325 cmd = [constants.XEN_CMD, "mem-set", instance.name, mem]
326 result = utils.RunCmd(cmd)
328 raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" %
329 (instance.name, result.fail_reason,
331 cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem]
332 cmd.append(XenHypervisor._ConfigFileName(instance.name))
333 result = utils.RunCmd(cmd)
335 raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" %
336 (instance.name, result.fail_reason,
339 def GetNodeInfo(self):
340 """Return information about the node.
342 @return: a dict with the following keys (memory values in MiB):
343 - memory_total: the total memory size on the node
344 - memory_free: the available memory on the node for instances
345 - memory_dom0: the memory used by the node itself, if available
346 - nr_cpus: total number of CPUs
347 - nr_nodes: in a NUMA system, the number of domains
348 - nr_sockets: the number of physical CPU sockets in the node
349 - hv_version: the hypervisor version in the form (major, minor)
352 result = utils.RunCmd([constants.XEN_CMD, "info"])
354 logging.error("Can't run 'xm info' (%s): %s", result.fail_reason,
358 xmoutput = result.stdout.splitlines()
360 cores_per_socket = threads_per_core = nr_cpus = None
361 xen_major, xen_minor = None, None
365 for line in xmoutput:
366 splitfields = line.split(":", 1)
368 if len(splitfields) > 1:
369 key = splitfields[0].strip()
370 val = splitfields[1].strip()
372 # note: in xen 3, memory has changed to total_memory
373 if key == "memory" or key == "total_memory":
374 memory_total = int(val)
375 elif key == "free_memory":
376 memory_free = int(val)
377 elif key == "nr_cpus":
378 nr_cpus = result["cpu_total"] = int(val)
379 elif key == "nr_nodes":
380 result["cpu_nodes"] = int(val)
381 elif key == "cores_per_socket":
382 cores_per_socket = int(val)
383 elif key == "threads_per_core":
384 threads_per_core = int(val)
385 elif key == "xen_major":
387 elif key == "xen_minor":
390 if None not in [cores_per_socket, threads_per_core, nr_cpus]:
391 result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
394 for (name, _, mem, vcpus, _, _) in self._GetXMList(True):
395 if name == _DOM0_NAME:
396 result["memory_dom0"] = mem
397 result["dom0_cpus"] = vcpus
399 # Include Dom0 in total memory usage
402 if memory_free is not None:
403 result["memory_free"] = memory_free
405 if memory_total is not None:
406 result["memory_total"] = memory_total
408 # Calculate memory used by hypervisor
409 if None not in [memory_total, memory_free, total_instmem]:
410 result["memory_hv"] = memory_total - memory_free - total_instmem
412 if not (xen_major is None or xen_minor is None):
413 result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
418 def GetInstanceConsole(cls, instance, hvparams, beparams):
419 """Return a command for connecting to the console of an instance.
422 return objects.InstanceConsole(instance=instance.name,
423 kind=constants.CONS_SSH,
424 host=instance.primary_node,
425 user=constants.SSH_CONSOLE_USER,
426 command=[pathutils.XEN_CONSOLE_WRAPPER,
427 constants.XEN_CMD, instance.name])
430 """Verify the hypervisor.
432 For Xen, this verifies that the xend process is running.
435 result = utils.RunCmd([constants.XEN_CMD, "info"])
437 return "'xm info' failed: %s, %s" % (result.fail_reason, result.output)
440 def _GetConfigFileDiskData(block_devices, blockdev_prefix):
441 """Get disk directive for xen config file.
443 This method builds the xen config disk directive according to the
444 given disk_template and block_devices.
446 @param block_devices: list of tuples (cfdev, rldev):
447 - cfdev: dict containing ganeti config disk part
448 - rldev: ganeti.bdev.BlockDev object
449 @param blockdev_prefix: a string containing blockdevice prefix,
450 e.g. "sd" for /dev/sda
452 @return: string containing disk directive for xen instance config file
456 constants.FD_LOOP: "file",
457 constants.FD_BLKTAP: "tap:aio",
460 if len(block_devices) > 24:
462 raise errors.HypervisorError("Too many disks")
463 namespace = [blockdev_prefix + chr(i + ord("a")) for i in range(24)]
464 for sd_name, (cfdev, dev_path) in zip(namespace, block_devices):
465 if cfdev.mode == constants.DISK_RDWR:
469 if cfdev.dev_type == constants.LD_FILE:
470 line = "'%s:%s,%s,%s'" % (FILE_DRIVER_MAP[cfdev.physical_id[0]],
471 dev_path, sd_name, mode)
473 line = "'phy:%s,%s,%s'" % (dev_path, sd_name, mode)
474 disk_data.append(line)
478 def MigrationInfo(self, instance):
479 """Get instance information to perform a migration.
481 @type instance: L{objects.Instance}
482 @param instance: instance to be migrated
484 @return: content of the xen config file
487 return self._ReadConfigFile(instance.name)
489 def AcceptInstance(self, instance, info, target):
490 """Prepare to accept an instance.
492 @type instance: L{objects.Instance}
493 @param instance: instance to be accepted
495 @param info: content of the xen config file on the source node
497 @param target: target host (usually ip), on this node
502 def FinalizeMigrationDst(self, instance, info, success):
503 """Finalize an instance migration.
505 After a successful migration we write the xen config file.
506 We do nothing on a failure, as we did not change anything at accept time.
508 @type instance: L{objects.Instance}
509 @param instance: instance whose migration is being finalized
511 @param info: content of the xen config file on the source node
512 @type success: boolean
513 @param success: whether the migration was a success or a failure
517 self._WriteConfigFileStatic(instance.name, info)
519 def MigrateInstance(self, instance, target, live):
520 """Migrate an instance to a target node.
522 The migration will not be attempted if the instance is not
525 @type instance: L{objects.Instance}
526 @param instance: the instance to be migrated
528 @param target: ip address of the target node
530 @param live: perform a live migration
533 if self.GetInstanceInfo(instance.name) is None:
534 raise errors.HypervisorError("Instance not running, cannot migrate")
536 port = instance.hvparams[constants.HV_MIGRATION_PORT]
538 if (constants.XEN_CMD == constants.XEN_CMD_XM and
539 not netutils.TcpPing(target, port, live_port_needed=True)):
540 raise errors.HypervisorError("Remote host %s not listening on port"
541 " %s, cannot migrate" % (target, port))
543 # FIXME: migrate must be upgraded for transitioning to "xl" (xen 4.1).
544 # This should be reworked in Ganeti 2.7
545 # ssh must recognize the key of the target host for the migration
546 args = [constants.XEN_CMD, "migrate"]
547 if constants.XEN_CMD == constants.XEN_CMD_XM:
548 args.extend(["-p", "%d" % port])
551 elif constants.XEN_CMD == constants.XEN_CMD_XL:
552 cluster_name = ssconf.SimpleStore().GetClusterName()
553 args.extend(["-s", constants.XL_SSH_CMD % cluster_name])
554 args.extend(["-C", self._ConfigFileName(instance.name)])
556 raise errors.HypervisorError("Unsupported xen command: %s" %
559 args.extend([instance.name, target])
560 result = utils.RunCmd(args)
562 raise errors.HypervisorError("Failed to migrate instance %s: %s" %
563 (instance.name, result.output))
565 def FinalizeMigrationSource(self, instance, success, live):
566 """Finalize the instance migration on the source node.
568 @type instance: L{objects.Instance}
569 @param instance: the instance that was migrated
571 @param success: whether the migration succeeded or not
573 @param live: whether the user requested a live migration or not
576 # pylint: disable=W0613
578 # remove old xen file after migration succeeded
580 self._RemoveConfigFile(instance.name)
581 except EnvironmentError:
582 logging.exception("Failure while removing instance config file")
584 def GetMigrationStatus(self, instance):
585 """Get the migration status
587 As MigrateInstance for Xen is still blocking, if this method is called it
588 means that MigrateInstance has completed successfully. So we can safely
589 assume that the migration was successful and notify this fact to the client.
591 @type instance: L{objects.Instance}
592 @param instance: the instance that is being migrated
593 @rtype: L{objects.MigrationStatus}
594 @return: the status of the current migration (one of
595 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
596 progress info that can be retrieved from the hypervisor
599 return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
602 def PowercycleNode(cls):
603 """Xen-specific powercycle.
605 This first does a Linux reboot (which triggers automatically a Xen
606 reboot), and if that fails it tries to do a Xen reboot. The reason
607 we don't try a Xen reboot first is that the xen reboot launches an
608 external command which connects to the Xen hypervisor, and that
609 won't work in case the root filesystem is broken and/or the xend
610 daemon is not working.
614 cls.LinuxPowercycle()
616 utils.RunCmd([constants.XEN_CMD, "debug", "R"])
619 class XenPvmHypervisor(XenHypervisor):
620 """Xen PVM hypervisor interface"""
623 constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
624 constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
625 constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
626 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
627 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
628 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
629 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
630 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
631 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
632 # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
633 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
634 constants.HV_REBOOT_BEHAVIOR:
635 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
636 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
637 constants.HV_CPU_CAP: hv_base.NO_CHECK,
638 constants.HV_CPU_WEIGHT:
639 (False, lambda x: 0 < x < 65536, "invalid weight", None, None),
643 def _WriteConfigFile(cls, instance, startup_memory, block_devices):
644 """Write the Xen config file for the instance.
647 hvp = instance.hvparams
649 config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
651 # if bootloader is True, use bootloader instead of kernel and ramdisk
653 if hvp[constants.HV_USE_BOOTLOADER]:
654 # bootloader handling
655 bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
657 config.write("bootloader = '%s'\n" % bootloader_path)
659 raise errors.HypervisorError("Bootloader enabled, but missing"
662 bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
664 config.write("bootargs = '%s'\n" % bootloader_args)
667 kpath = hvp[constants.HV_KERNEL_PATH]
668 config.write("kernel = '%s'\n" % kpath)
671 initrd_path = hvp[constants.HV_INITRD_PATH]
673 config.write("ramdisk = '%s'\n" % initrd_path)
675 # rest of the settings
676 config.write("memory = %d\n" % startup_memory)
677 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
678 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
679 cpu_pinning = cls._CreateConfigCpus(hvp[constants.HV_CPU_MASK])
681 config.write("%s\n" % cpu_pinning)
682 cpu_cap = hvp[constants.HV_CPU_CAP]
684 config.write("cpu_cap=%d\n" % cpu_cap)
685 cpu_weight = hvp[constants.HV_CPU_WEIGHT]
687 config.write("cpu_weight=%d\n" % cpu_weight)
689 config.write("name = '%s'\n" % instance.name)
692 for nic in instance.nics:
693 nic_str = "mac=%s" % (nic.mac)
694 ip = getattr(nic, "ip", None)
696 nic_str += ", ip=%s" % ip
697 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
698 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
699 vif_data.append("'%s'" % nic_str)
701 disk_data = cls._GetConfigFileDiskData(block_devices,
702 hvp[constants.HV_BLOCKDEV_PREFIX])
704 config.write("vif = [%s]\n" % ",".join(vif_data))
705 config.write("disk = [%s]\n" % ",".join(disk_data))
707 if hvp[constants.HV_ROOT_PATH]:
708 config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
709 config.write("on_poweroff = 'destroy'\n")
710 if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
711 config.write("on_reboot = 'restart'\n")
713 config.write("on_reboot = 'destroy'\n")
714 config.write("on_crash = 'restart'\n")
715 config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
716 cls._WriteConfigFileStatic(instance.name, config.getvalue())
721 class XenHvmHypervisor(XenHypervisor):
722 """Xen HVM hypervisor interface"""
724 ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
725 pathutils.VNC_PASSWORD_FILE,
727 ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [
728 pathutils.VNC_PASSWORD_FILE,
732 constants.HV_ACPI: hv_base.NO_CHECK,
733 constants.HV_BOOT_ORDER: (True, ) +
734 (lambda x: x and len(x.strip("acdn")) == 0,
735 "Invalid boot order specified, must be one or more of [acdn]",
737 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
738 constants.HV_DISK_TYPE:
739 hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
740 constants.HV_NIC_TYPE:
741 hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
742 constants.HV_PAE: hv_base.NO_CHECK,
743 constants.HV_VNC_BIND_ADDRESS:
744 (False, netutils.IP4Address.IsValid,
745 "VNC bind address is not a valid IP address", None, None),
746 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
747 constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
748 constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
749 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
750 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
751 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
752 # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
753 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
754 # Add PCI passthrough
755 constants.HV_PASSTHROUGH: hv_base.NO_CHECK,
756 constants.HV_REBOOT_BEHAVIOR:
757 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
758 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
759 constants.HV_CPU_CAP: hv_base.NO_CHECK,
760 constants.HV_CPU_WEIGHT:
761 (False, lambda x: 0 < x < 65535, "invalid weight", None, None),
765 def _WriteConfigFile(cls, instance, startup_memory, block_devices):
766 """Create a Xen 3.1 HVM config file.
769 hvp = instance.hvparams
772 config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
775 kpath = hvp[constants.HV_KERNEL_PATH]
776 config.write("kernel = '%s'\n" % kpath)
778 config.write("builder = 'hvm'\n")
779 config.write("memory = %d\n" % startup_memory)
780 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
781 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
782 cpu_pinning = cls._CreateConfigCpus(hvp[constants.HV_CPU_MASK])
784 config.write("%s\n" % cpu_pinning)
785 cpu_cap = hvp[constants.HV_CPU_CAP]
787 config.write("cpu_cap=%d\n" % cpu_cap)
788 cpu_weight = hvp[constants.HV_CPU_WEIGHT]
790 config.write("cpu_weight=%d\n" % cpu_weight)
792 config.write("name = '%s'\n" % instance.name)
793 if hvp[constants.HV_PAE]:
794 config.write("pae = 1\n")
796 config.write("pae = 0\n")
797 if hvp[constants.HV_ACPI]:
798 config.write("acpi = 1\n")
800 config.write("acpi = 0\n")
801 config.write("apic = 1\n")
802 config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
803 config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
804 config.write("sdl = 0\n")
805 config.write("usb = 1\n")
806 config.write("usbdevice = 'tablet'\n")
807 config.write("vnc = 1\n")
808 if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
809 config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
811 config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
813 if instance.network_port > constants.VNC_BASE_PORT:
814 display = instance.network_port - constants.VNC_BASE_PORT
815 config.write("vncdisplay = %s\n" % display)
816 config.write("vncunused = 0\n")
818 config.write("# vncdisplay = 1\n")
819 config.write("vncunused = 1\n")
821 vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
823 password = utils.ReadFile(vnc_pwd_file)
824 except EnvironmentError, err:
825 raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
828 config.write("vncpasswd = '%s'\n" % password.rstrip())
830 config.write("serial = 'pty'\n")
831 if hvp[constants.HV_USE_LOCALTIME]:
832 config.write("localtime = 1\n")
835 nic_type = hvp[constants.HV_NIC_TYPE]
837 # ensure old instances don't change
838 nic_type_str = ", type=ioemu"
839 elif nic_type == constants.HT_NIC_PARAVIRTUAL:
840 nic_type_str = ", type=paravirtualized"
842 nic_type_str = ", model=%s, type=ioemu" % nic_type
843 for nic in instance.nics:
844 nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
845 ip = getattr(nic, "ip", None)
847 nic_str += ", ip=%s" % ip
848 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
849 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
850 vif_data.append("'%s'" % nic_str)
852 config.write("vif = [%s]\n" % ",".join(vif_data))
854 disk_data = cls._GetConfigFileDiskData(block_devices,
855 hvp[constants.HV_BLOCKDEV_PREFIX])
857 iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
859 iso = "'file:%s,hdc:cdrom,r'" % iso_path
860 disk_data.append(iso)
862 config.write("disk = [%s]\n" % (",".join(disk_data)))
863 # Add PCI passthrough
865 pci_pass = hvp[constants.HV_PASSTHROUGH]
867 pci_pass_arr = pci_pass.split(";")
868 config.write("pci = %s\n" % pci_pass_arr)
869 config.write("on_poweroff = 'destroy'\n")
870 if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
871 config.write("on_reboot = 'restart'\n")
873 config.write("on_reboot = 'destroy'\n")
874 config.write("on_crash = 'restart'\n")
875 cls._WriteConfigFileStatic(instance.name, config.getvalue())