4 # Copyright (C) 2006, 2007, 2008, 2009, 2010 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
37 XEND_CONFIG_FILE = "/etc/xen/xend-config.sxp"
38 XL_CONFIG_FILE = "/etc/xen/xl.conf"
39 VIF_BRIDGE_SCRIPT = "/etc/xen/scripts/vif-bridge"
40 _DOM0_NAME = "Domain-0"
43 class XenHypervisor(hv_base.BaseHypervisor):
44 """Xen generic hypervisor interface
46 This is the Xen base class used for both Xen PVM and HVM. It contains
47 all the functionality that is identical for both.
51 REBOOT_RETRY_COUNT = 60
52 REBOOT_RETRY_INTERVAL = 10
59 ANCILLARY_FILES_OPT = [
64 def _ConfigFileName(instance_name):
65 """Get the config file name for an instance.
67 @param instance_name: instance name
68 @type instance_name: str
69 @return: fully qualified path to instance config file
73 return "/etc/xen/%s" % instance_name
76 def _WriteConfigFile(cls, instance, block_devices):
77 """Write the Xen config file for the instance.
80 raise NotImplementedError
83 def _WriteConfigFileStatic(instance_name, data):
84 """Write the Xen config file for the instance.
86 This version of the function just writes the config file from static data.
89 utils.WriteFile(XenHypervisor._ConfigFileName(instance_name), data=data)
92 def _ReadConfigFile(instance_name):
93 """Returns the contents of the instance config file.
97 file_content = utils.ReadFile(
98 XenHypervisor._ConfigFileName(instance_name))
99 except EnvironmentError, err:
100 raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
104 def _RemoveConfigFile(instance_name):
105 """Remove the xen configuration file.
108 utils.RemoveFile(XenHypervisor._ConfigFileName(instance_name))
111 def _CreateConfigCpus(cls, cpu_mask):
112 """Create a CPU config string that's compatible with Xen's
116 # Convert the string CPU mask to a list of list of int's
117 cpu_list = utils.ParseMultiCpuMask(cpu_mask)
119 if len(cpu_list) == 1:
120 all_cpu_mapping = cpu_list[0]
121 if all_cpu_mapping == constants.CPU_PINNING_OFF:
122 # If CPU pinning has 1 entry that's "all", then remove the
123 # parameter from the config file
126 # If CPU pinning has one non-all entry, mapping all vCPUS (the entire
127 # VM) to one physical CPU, using format 'cpu = "C"'
128 return "cpu = \"%s\"" % ",".join(map(str, all_cpu_mapping))
130 def _GetCPUMap(vcpu):
131 if vcpu[0] == constants.CPU_PINNING_ALL_VAL:
132 cpu_map = constants.CPU_PINNING_ALL_XEN
134 cpu_map = ",".join(map(str, vcpu))
135 return "\"%s\"" % cpu_map
137 # build the result string in format 'cpus = [ "c", "c", "c" ]',
138 # where each c is a physical CPU number, a range, a list, or any
140 return "cpus = [ %s ]" % ", ".join(map(_GetCPUMap, cpu_list))
143 def _RunXmList(xmlist_errors):
144 """Helper function for L{_GetXMList} to run "xm list".
147 result = utils.RunCmd([constants.XEN_CMD, "list"])
149 logging.error("xm list failed (%s): %s", result.fail_reason,
151 xmlist_errors.append(result)
152 raise utils.RetryAgain()
154 # skip over the heading
155 return result.stdout.splitlines()[1:]
158 def _GetXMList(cls, include_node):
159 """Return the list of running instances.
161 If the include_node argument is True, then we return information
162 for dom0 also, otherwise we filter that from the return value.
164 @return: list of (name, id, memory, vcpus, state, time spent)
169 lines = utils.Retry(cls._RunXmList, 1, 5, args=(xmlist_errors, ))
170 except utils.RetryTimeout:
172 xmlist_result = xmlist_errors.pop()
174 errmsg = ("xm list failed, timeout exceeded (%s): %s" %
175 (xmlist_result.fail_reason, xmlist_result.output))
177 errmsg = "xm list failed"
179 raise errors.HypervisorError(errmsg)
183 # The format of lines is:
184 # Name ID Mem(MiB) VCPUs State Time(s)
185 # Domain-0 0 3418 4 r----- 266.2
188 raise errors.HypervisorError("Can't parse output of xm list,"
191 data[1] = int(data[1])
192 data[2] = int(data[2])
193 data[3] = int(data[3])
194 data[5] = float(data[5])
195 except (TypeError, ValueError), err:
196 raise errors.HypervisorError("Can't parse output of xm list,"
197 " line: %s, error: %s" % (line, err))
199 # skip the Domain-0 (optional)
200 if include_node or data[0] != _DOM0_NAME:
205 def ListInstances(self):
206 """Get the list of running instances.
209 xm_list = self._GetXMList(False)
210 names = [info[0] for info in xm_list]
213 def GetInstanceInfo(self, instance_name):
214 """Get instance properties.
216 @param instance_name: the instance name
218 @return: tuple (name, id, memory, vcpus, stat, times)
221 xm_list = self._GetXMList(instance_name == _DOM0_NAME)
224 if data[0] == instance_name:
229 def GetAllInstancesInfo(self):
230 """Get properties of all instances.
232 @return: list of tuples (name, id, memory, vcpus, stat, times)
235 xm_list = self._GetXMList(False)
238 def StartInstance(self, instance, block_devices, startup_paused):
239 """Start an instance.
242 self._WriteConfigFile(instance, block_devices)
243 cmd = [constants.XEN_CMD, "create"]
246 cmd.extend([self._ConfigFileName(instance.name)])
247 result = utils.RunCmd(cmd)
250 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
251 (instance.name, result.fail_reason,
254 def StopInstance(self, instance, force=False, retry=False, name=None):
260 self._RemoveConfigFile(name)
262 command = [constants.XEN_CMD, "destroy", name]
264 command = [constants.XEN_CMD, "shutdown", name]
265 result = utils.RunCmd(command)
268 raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
269 (name, result.fail_reason, result.output))
271 def RebootInstance(self, instance):
272 """Reboot an instance.
275 ini_info = self.GetInstanceInfo(instance.name)
278 raise errors.HypervisorError("Failed to reboot instance %s,"
279 " not running" % instance.name)
281 result = utils.RunCmd([constants.XEN_CMD, "reboot", instance.name])
283 raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
284 (instance.name, result.fail_reason,
287 def _CheckInstance():
288 new_info = self.GetInstanceInfo(instance.name)
290 # check if the domain ID has changed or the run time has decreased
291 if (new_info is not None and
292 (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])):
295 raise utils.RetryAgain()
298 utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
299 self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
300 except utils.RetryTimeout:
301 raise errors.HypervisorError("Failed to reboot instance %s: instance"
302 " did not reboot in the expected interval" %
305 def BalloonInstanceMemory(self, instance, mem):
306 """Balloon an instance memory to a certain value.
308 @type instance: L{objects.Instance}
309 @param instance: instance to be accepted
311 @param mem: actual memory size to use for instance runtime
314 cmd = [constants.XEN_CMD, "mem-set", instance.name, mem]
315 result = utils.RunCmd(cmd)
317 raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" %
318 (instance.name, result.fail_reason,
320 cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem]
321 cmd.append(XenHypervisor._ConfigFileName(instance.name))
322 result = utils.RunCmd(cmd)
324 raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" %
325 (instance.name, result.fail_reason,
328 def GetNodeInfo(self):
329 """Return information about the node.
331 @return: a dict with the following keys (memory values in MiB):
332 - memory_total: the total memory size on the node
333 - memory_free: the available memory on the node for instances
334 - memory_dom0: the memory used by the node itself, if available
335 - nr_cpus: total number of CPUs
336 - nr_nodes: in a NUMA system, the number of domains
337 - nr_sockets: the number of physical CPU sockets in the node
338 - hv_version: the hypervisor version in the form (major, minor)
341 result = utils.RunCmd([constants.XEN_CMD, "info"])
343 logging.error("Can't run 'xm info' (%s): %s", result.fail_reason,
347 xmoutput = result.stdout.splitlines()
349 cores_per_socket = threads_per_core = nr_cpus = None
350 xen_major, xen_minor = None, None
354 for line in xmoutput:
355 splitfields = line.split(":", 1)
357 if len(splitfields) > 1:
358 key = splitfields[0].strip()
359 val = splitfields[1].strip()
361 # note: in xen 3, memory has changed to total_memory
362 if key == "memory" or key == "total_memory":
363 memory_total = int(val)
364 elif key == "free_memory":
365 memory_free = int(val)
366 elif key == "nr_cpus":
367 nr_cpus = result["cpu_total"] = int(val)
368 elif key == "nr_nodes":
369 result["cpu_nodes"] = int(val)
370 elif key == "cores_per_socket":
371 cores_per_socket = int(val)
372 elif key == "threads_per_core":
373 threads_per_core = int(val)
374 elif key == "xen_major":
376 elif key == "xen_minor":
379 if None not in [cores_per_socket, threads_per_core, nr_cpus]:
380 result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
383 for (name, _, mem, vcpus, _, _) in self._GetXMList(True):
384 if name == _DOM0_NAME:
385 result["memory_dom0"] = mem
386 result["dom0_cpus"] = vcpus
388 # Include Dom0 in total memory usage
391 if memory_free is not None:
392 result["memory_free"] = memory_free
394 if memory_total is not None:
395 result["memory_total"] = memory_total
397 # Calculate memory used by hypervisor
398 if None not in [memory_total, memory_free, total_instmem]:
399 result["memory_hv"] = memory_total - memory_free - total_instmem
401 if not (xen_major is None or xen_minor is None):
402 result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
407 def GetInstanceConsole(cls, instance, hvparams, beparams):
408 """Return a command for connecting to the console of an instance.
411 return objects.InstanceConsole(instance=instance.name,
412 kind=constants.CONS_SSH,
413 host=instance.primary_node,
414 user=constants.GANETI_RUNAS,
415 command=[constants.XM_CONSOLE_WRAPPER,
419 """Verify the hypervisor.
421 For Xen, this verifies that the xend process is running.
424 result = utils.RunCmd([constants.XEN_CMD, "info"])
426 return "'xm info' failed: %s, %s" % (result.fail_reason, result.output)
429 def _GetConfigFileDiskData(block_devices, blockdev_prefix):
430 """Get disk directive for xen config file.
432 This method builds the xen config disk directive according to the
433 given disk_template and block_devices.
435 @param block_devices: list of tuples (cfdev, rldev):
436 - cfdev: dict containing ganeti config disk part
437 - rldev: ganeti.bdev.BlockDev object
438 @param blockdev_prefix: a string containing blockdevice prefix,
439 e.g. "sd" for /dev/sda
441 @return: string containing disk directive for xen instance config file
445 constants.FD_LOOP: "file",
446 constants.FD_BLKTAP: "tap:aio",
449 if len(block_devices) > 24:
451 raise errors.HypervisorError("Too many disks")
452 namespace = [blockdev_prefix + chr(i + ord("a")) for i in range(24)]
453 for sd_name, (cfdev, dev_path) in zip(namespace, block_devices):
454 if cfdev.mode == constants.DISK_RDWR:
458 if cfdev.dev_type == constants.LD_FILE:
459 line = "'%s:%s,%s,%s'" % (FILE_DRIVER_MAP[cfdev.physical_id[0]],
460 dev_path, sd_name, mode)
462 line = "'phy:%s,%s,%s'" % (dev_path, sd_name, mode)
463 disk_data.append(line)
467 def MigrationInfo(self, instance):
468 """Get instance information to perform a migration.
470 @type instance: L{objects.Instance}
471 @param instance: instance to be migrated
473 @return: content of the xen config file
476 return self._ReadConfigFile(instance.name)
478 def AcceptInstance(self, instance, info, target):
479 """Prepare to accept an instance.
481 @type instance: L{objects.Instance}
482 @param instance: instance to be accepted
484 @param info: content of the xen config file on the source node
486 @param target: target host (usually ip), on this node
491 def FinalizeMigrationDst(self, instance, info, success):
492 """Finalize an instance migration.
494 After a successful migration we write the xen config file.
495 We do nothing on a failure, as we did not change anything at accept time.
497 @type instance: L{objects.Instance}
498 @param instance: instance whose migration is being finalized
500 @param info: content of the xen config file on the source node
501 @type success: boolean
502 @param success: whether the migration was a success or a failure
506 self._WriteConfigFileStatic(instance.name, info)
508 def MigrateInstance(self, instance, target, live):
509 """Migrate an instance to a target node.
511 The migration will not be attempted if the instance is not
514 @type instance: L{objects.Instance}
515 @param instance: the instance to be migrated
517 @param target: ip address of the target node
519 @param live: perform a live migration
522 if self.GetInstanceInfo(instance.name) is None:
523 raise errors.HypervisorError("Instance not running, cannot migrate")
525 port = instance.hvparams[constants.HV_MIGRATION_PORT]
527 if not netutils.TcpPing(target, port, live_port_needed=True):
528 raise errors.HypervisorError("Remote host %s not listening on port"
529 " %s, cannot migrate" % (target, port))
531 # FIXME: migrate must be upgraded for transitioning to "xl" (xen 4.1).
532 # -l doesn't exist anymore
533 # -p doesn't exist anymore
534 # -C config_file must be passed
535 # ssh must recognize the key of the target host for the migration
536 args = [constants.XEN_CMD, "migrate", "-p", "%d" % port]
539 args.extend([instance.name, target])
540 result = utils.RunCmd(args)
542 raise errors.HypervisorError("Failed to migrate instance %s: %s" %
543 (instance.name, result.output))
545 def FinalizeMigrationSource(self, instance, success, live):
546 """Finalize the instance migration on the source node.
548 @type instance: L{objects.Instance}
549 @param instance: the instance that was migrated
551 @param success: whether the migration succeeded or not
553 @param live: whether the user requested a live migration or not
556 # pylint: disable=W0613
558 # remove old xen file after migration succeeded
560 self._RemoveConfigFile(instance.name)
561 except EnvironmentError:
562 logging.exception("Failure while removing instance config file")
564 def GetMigrationStatus(self, instance):
565 """Get the migration status
567 As MigrateInstance for Xen is still blocking, if this method is called it
568 means that MigrateInstance has completed successfully. So we can safely
569 assume that the migration was successful and notify this fact to the client.
571 @type instance: L{objects.Instance}
572 @param instance: the instance that is being migrated
573 @rtype: L{objects.MigrationStatus}
574 @return: the status of the current migration (one of
575 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
576 progress info that can be retrieved from the hypervisor
579 return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
582 def PowercycleNode(cls):
583 """Xen-specific powercycle.
585 This first does a Linux reboot (which triggers automatically a Xen
586 reboot), and if that fails it tries to do a Xen reboot. The reason
587 we don't try a Xen reboot first is that the xen reboot launches an
588 external command which connects to the Xen hypervisor, and that
589 won't work in case the root filesystem is broken and/or the xend
590 daemon is not working.
594 cls.LinuxPowercycle()
596 utils.RunCmd([constants.XEN_CMD, "debug", "R"])
599 class XenPvmHypervisor(XenHypervisor):
600 """Xen PVM hypervisor interface"""
603 constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
604 constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
605 constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
606 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
607 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
608 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
609 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
610 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
611 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
612 # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
613 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
614 constants.HV_REBOOT_BEHAVIOR:
615 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
616 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
620 def _WriteConfigFile(cls, instance, block_devices):
621 """Write the Xen config file for the instance.
624 hvp = instance.hvparams
626 config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
628 # if bootloader is True, use bootloader instead of kernel and ramdisk
630 if hvp[constants.HV_USE_BOOTLOADER]:
631 # bootloader handling
632 bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
634 config.write("bootloader = '%s'\n" % bootloader_path)
636 raise errors.HypervisorError("Bootloader enabled, but missing"
639 bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
641 config.write("bootargs = '%s'\n" % bootloader_args)
644 kpath = hvp[constants.HV_KERNEL_PATH]
645 config.write("kernel = '%s'\n" % kpath)
648 initrd_path = hvp[constants.HV_INITRD_PATH]
650 config.write("ramdisk = '%s'\n" % initrd_path)
652 # rest of the settings
653 # TODO(dynmem): use actual chosen memory for instance startup
654 config.write("memory = %d\n" % instance.beparams[constants.BE_MAXMEM])
655 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
656 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
657 cpu_pinning = cls._CreateConfigCpus(hvp[constants.HV_CPU_MASK])
659 config.write("%s\n" % cpu_pinning)
661 config.write("name = '%s'\n" % instance.name)
664 for nic in instance.nics:
665 nic_str = "mac=%s" % (nic.mac)
666 ip = getattr(nic, "ip", None)
668 nic_str += ", ip=%s" % ip
669 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
670 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
671 vif_data.append("'%s'" % nic_str)
673 disk_data = cls._GetConfigFileDiskData(block_devices,
674 hvp[constants.HV_BLOCKDEV_PREFIX])
676 config.write("vif = [%s]\n" % ",".join(vif_data))
677 config.write("disk = [%s]\n" % ",".join(disk_data))
679 if hvp[constants.HV_ROOT_PATH]:
680 config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
681 config.write("on_poweroff = 'destroy'\n")
682 if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
683 config.write("on_reboot = 'restart'\n")
685 config.write("on_reboot = 'destroy'\n")
686 config.write("on_crash = 'restart'\n")
687 config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
688 # just in case it exists
689 utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
691 utils.WriteFile(cls._ConfigFileName(instance.name),
692 data=config.getvalue())
693 except EnvironmentError, err:
694 raise errors.HypervisorError("Cannot write Xen instance confile"
696 (cls._ConfigFileName(instance.name), err))
701 class XenHvmHypervisor(XenHypervisor):
702 """Xen HVM hypervisor interface"""
704 ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
705 constants.VNC_PASSWORD_FILE,
707 ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [
708 constants.VNC_PASSWORD_FILE,
712 constants.HV_ACPI: hv_base.NO_CHECK,
713 constants.HV_BOOT_ORDER: (True, ) +
714 (lambda x: x and len(x.strip("acdn")) == 0,
715 "Invalid boot order specified, must be one or more of [acdn]",
717 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
718 constants.HV_DISK_TYPE:
719 hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
720 constants.HV_NIC_TYPE:
721 hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
722 constants.HV_PAE: hv_base.NO_CHECK,
723 constants.HV_VNC_BIND_ADDRESS:
724 (False, netutils.IP4Address.IsValid,
725 "VNC bind address is not a valid IP address", None, None),
726 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
727 constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
728 constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
729 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
730 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
731 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
732 # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
733 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
734 constants.HV_REBOOT_BEHAVIOR:
735 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
736 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
740 def _WriteConfigFile(cls, instance, block_devices):
741 """Create a Xen 3.1 HVM config file.
744 hvp = instance.hvparams
747 config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
750 kpath = hvp[constants.HV_KERNEL_PATH]
751 config.write("kernel = '%s'\n" % kpath)
753 config.write("builder = 'hvm'\n")
754 # TODO(dynmem): use actual chosen memory for instance startup
755 config.write("memory = %d\n" % instance.beparams[constants.BE_MAXMEM])
756 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
757 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
758 cpu_pinning = cls._CreateConfigCpus(hvp[constants.HV_CPU_MASK])
760 config.write("%s\n" % cpu_pinning)
762 config.write("name = '%s'\n" % instance.name)
763 if hvp[constants.HV_PAE]:
764 config.write("pae = 1\n")
766 config.write("pae = 0\n")
767 if hvp[constants.HV_ACPI]:
768 config.write("acpi = 1\n")
770 config.write("acpi = 0\n")
771 config.write("apic = 1\n")
772 config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
773 config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
774 config.write("sdl = 0\n")
775 config.write("usb = 1\n")
776 config.write("usbdevice = 'tablet'\n")
777 config.write("vnc = 1\n")
778 if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
779 config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
781 config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
783 if instance.network_port > constants.VNC_BASE_PORT:
784 display = instance.network_port - constants.VNC_BASE_PORT
785 config.write("vncdisplay = %s\n" % display)
786 config.write("vncunused = 0\n")
788 config.write("# vncdisplay = 1\n")
789 config.write("vncunused = 1\n")
791 vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
793 password = utils.ReadFile(vnc_pwd_file)
794 except EnvironmentError, err:
795 raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
798 config.write("vncpasswd = '%s'\n" % password.rstrip())
800 config.write("serial = 'pty'\n")
801 if hvp[constants.HV_USE_LOCALTIME]:
802 config.write("localtime = 1\n")
805 nic_type = hvp[constants.HV_NIC_TYPE]
807 # ensure old instances don't change
808 nic_type_str = ", type=ioemu"
809 elif nic_type == constants.HT_NIC_PARAVIRTUAL:
810 nic_type_str = ", type=paravirtualized"
812 nic_type_str = ", model=%s, type=ioemu" % nic_type
813 for nic in instance.nics:
814 nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
815 ip = getattr(nic, "ip", None)
817 nic_str += ", ip=%s" % ip
818 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
819 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
820 vif_data.append("'%s'" % nic_str)
822 config.write("vif = [%s]\n" % ",".join(vif_data))
824 disk_data = cls._GetConfigFileDiskData(block_devices,
825 hvp[constants.HV_BLOCKDEV_PREFIX])
827 iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
829 iso = "'file:%s,hdc:cdrom,r'" % iso_path
830 disk_data.append(iso)
832 config.write("disk = [%s]\n" % (",".join(disk_data)))
834 config.write("on_poweroff = 'destroy'\n")
835 if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
836 config.write("on_reboot = 'restart'\n")
838 config.write("on_reboot = 'destroy'\n")
839 config.write("on_crash = 'restart'\n")
840 # just in case it exists
841 utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
843 utils.WriteFile(cls._ConfigFileName(instance.name),
844 data=config.getvalue())
845 except EnvironmentError, err:
846 raise errors.HypervisorError("Cannot write Xen instance confile"
848 (cls._ConfigFileName(instance.name), err))