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, startup_memory, 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 startup_memory = self._InstanceStartupMemory(instance)
243 self._WriteConfigFile(instance, startup_memory, block_devices)
244 cmd = [constants.XEN_CMD, "create"]
247 cmd.extend([self._ConfigFileName(instance.name)])
248 result = utils.RunCmd(cmd)
251 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
252 (instance.name, result.fail_reason,
255 def StopInstance(self, instance, force=False, retry=False, name=None):
261 self._RemoveConfigFile(name)
263 command = [constants.XEN_CMD, "destroy", name]
265 command = [constants.XEN_CMD, "shutdown", name]
266 result = utils.RunCmd(command)
269 raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
270 (name, result.fail_reason, result.output))
272 def RebootInstance(self, instance):
273 """Reboot an instance.
276 ini_info = self.GetInstanceInfo(instance.name)
279 raise errors.HypervisorError("Failed to reboot instance %s,"
280 " not running" % instance.name)
282 result = utils.RunCmd([constants.XEN_CMD, "reboot", instance.name])
284 raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
285 (instance.name, result.fail_reason,
288 def _CheckInstance():
289 new_info = self.GetInstanceInfo(instance.name)
291 # check if the domain ID has changed or the run time has decreased
292 if (new_info is not None and
293 (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])):
296 raise utils.RetryAgain()
299 utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
300 self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
301 except utils.RetryTimeout:
302 raise errors.HypervisorError("Failed to reboot instance %s: instance"
303 " did not reboot in the expected interval" %
306 def BalloonInstanceMemory(self, instance, mem):
307 """Balloon an instance memory to a certain value.
309 @type instance: L{objects.Instance}
310 @param instance: instance to be accepted
312 @param mem: actual memory size to use for instance runtime
315 cmd = [constants.XEN_CMD, "mem-set", instance.name, mem]
316 result = utils.RunCmd(cmd)
318 raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" %
319 (instance.name, result.fail_reason,
321 cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem]
322 cmd.append(XenHypervisor._ConfigFileName(instance.name))
323 result = utils.RunCmd(cmd)
325 raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" %
326 (instance.name, result.fail_reason,
329 def GetNodeInfo(self):
330 """Return information about the node.
332 @return: a dict with the following keys (memory values in MiB):
333 - memory_total: the total memory size on the node
334 - memory_free: the available memory on the node for instances
335 - memory_dom0: the memory used by the node itself, if available
336 - nr_cpus: total number of CPUs
337 - nr_nodes: in a NUMA system, the number of domains
338 - nr_sockets: the number of physical CPU sockets in the node
339 - hv_version: the hypervisor version in the form (major, minor)
342 result = utils.RunCmd([constants.XEN_CMD, "info"])
344 logging.error("Can't run 'xm info' (%s): %s", result.fail_reason,
348 xmoutput = result.stdout.splitlines()
350 cores_per_socket = threads_per_core = nr_cpus = None
351 xen_major, xen_minor = None, None
355 for line in xmoutput:
356 splitfields = line.split(":", 1)
358 if len(splitfields) > 1:
359 key = splitfields[0].strip()
360 val = splitfields[1].strip()
362 # note: in xen 3, memory has changed to total_memory
363 if key == "memory" or key == "total_memory":
364 memory_total = int(val)
365 elif key == "free_memory":
366 memory_free = int(val)
367 elif key == "nr_cpus":
368 nr_cpus = result["cpu_total"] = int(val)
369 elif key == "nr_nodes":
370 result["cpu_nodes"] = int(val)
371 elif key == "cores_per_socket":
372 cores_per_socket = int(val)
373 elif key == "threads_per_core":
374 threads_per_core = int(val)
375 elif key == "xen_major":
377 elif key == "xen_minor":
380 if None not in [cores_per_socket, threads_per_core, nr_cpus]:
381 result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
384 for (name, _, mem, vcpus, _, _) in self._GetXMList(True):
385 if name == _DOM0_NAME:
386 result["memory_dom0"] = mem
387 result["dom0_cpus"] = vcpus
389 # Include Dom0 in total memory usage
392 if memory_free is not None:
393 result["memory_free"] = memory_free
395 if memory_total is not None:
396 result["memory_total"] = memory_total
398 # Calculate memory used by hypervisor
399 if None not in [memory_total, memory_free, total_instmem]:
400 result["memory_hv"] = memory_total - memory_free - total_instmem
402 if not (xen_major is None or xen_minor is None):
403 result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
408 def GetInstanceConsole(cls, instance, hvparams, beparams):
409 """Return a command for connecting to the console of an instance.
412 return objects.InstanceConsole(instance=instance.name,
413 kind=constants.CONS_SSH,
414 host=instance.primary_node,
415 user=constants.GANETI_RUNAS,
416 command=[constants.XM_CONSOLE_WRAPPER,
420 """Verify the hypervisor.
422 For Xen, this verifies that the xend process is running.
425 result = utils.RunCmd([constants.XEN_CMD, "info"])
427 return "'xm info' failed: %s, %s" % (result.fail_reason, result.output)
430 def _GetConfigFileDiskData(block_devices, blockdev_prefix):
431 """Get disk directive for xen config file.
433 This method builds the xen config disk directive according to the
434 given disk_template and block_devices.
436 @param block_devices: list of tuples (cfdev, rldev):
437 - cfdev: dict containing ganeti config disk part
438 - rldev: ganeti.bdev.BlockDev object
439 @param blockdev_prefix: a string containing blockdevice prefix,
440 e.g. "sd" for /dev/sda
442 @return: string containing disk directive for xen instance config file
446 constants.FD_LOOP: "file",
447 constants.FD_BLKTAP: "tap:aio",
450 if len(block_devices) > 24:
452 raise errors.HypervisorError("Too many disks")
453 namespace = [blockdev_prefix + chr(i + ord("a")) for i in range(24)]
454 for sd_name, (cfdev, dev_path) in zip(namespace, block_devices):
455 if cfdev.mode == constants.DISK_RDWR:
459 if cfdev.dev_type == constants.LD_FILE:
460 line = "'%s:%s,%s,%s'" % (FILE_DRIVER_MAP[cfdev.physical_id[0]],
461 dev_path, sd_name, mode)
463 line = "'phy:%s,%s,%s'" % (dev_path, sd_name, mode)
464 disk_data.append(line)
468 def MigrationInfo(self, instance):
469 """Get instance information to perform a migration.
471 @type instance: L{objects.Instance}
472 @param instance: instance to be migrated
474 @return: content of the xen config file
477 return self._ReadConfigFile(instance.name)
479 def AcceptInstance(self, instance, info, target):
480 """Prepare to accept an instance.
482 @type instance: L{objects.Instance}
483 @param instance: instance to be accepted
485 @param info: content of the xen config file on the source node
487 @param target: target host (usually ip), on this node
492 def FinalizeMigrationDst(self, instance, info, success):
493 """Finalize an instance migration.
495 After a successful migration we write the xen config file.
496 We do nothing on a failure, as we did not change anything at accept time.
498 @type instance: L{objects.Instance}
499 @param instance: instance whose migration is being finalized
501 @param info: content of the xen config file on the source node
502 @type success: boolean
503 @param success: whether the migration was a success or a failure
507 self._WriteConfigFileStatic(instance.name, info)
509 def MigrateInstance(self, instance, target, live):
510 """Migrate an instance to a target node.
512 The migration will not be attempted if the instance is not
515 @type instance: L{objects.Instance}
516 @param instance: the instance to be migrated
518 @param target: ip address of the target node
520 @param live: perform a live migration
523 if self.GetInstanceInfo(instance.name) is None:
524 raise errors.HypervisorError("Instance not running, cannot migrate")
526 port = instance.hvparams[constants.HV_MIGRATION_PORT]
528 if not netutils.TcpPing(target, port, live_port_needed=True):
529 raise errors.HypervisorError("Remote host %s not listening on port"
530 " %s, cannot migrate" % (target, port))
532 # FIXME: migrate must be upgraded for transitioning to "xl" (xen 4.1).
533 # -l doesn't exist anymore
534 # -p doesn't exist anymore
535 # -C config_file must be passed
536 # ssh must recognize the key of the target host for the migration
537 args = [constants.XEN_CMD, "migrate", "-p", "%d" % port]
540 args.extend([instance.name, target])
541 result = utils.RunCmd(args)
543 raise errors.HypervisorError("Failed to migrate instance %s: %s" %
544 (instance.name, result.output))
546 def FinalizeMigrationSource(self, instance, success, live):
547 """Finalize the instance migration on the source node.
549 @type instance: L{objects.Instance}
550 @param instance: the instance that was migrated
552 @param success: whether the migration succeeded or not
554 @param live: whether the user requested a live migration or not
557 # pylint: disable=W0613
559 # remove old xen file after migration succeeded
561 self._RemoveConfigFile(instance.name)
562 except EnvironmentError:
563 logging.exception("Failure while removing instance config file")
565 def GetMigrationStatus(self, instance):
566 """Get the migration status
568 As MigrateInstance for Xen is still blocking, if this method is called it
569 means that MigrateInstance has completed successfully. So we can safely
570 assume that the migration was successful and notify this fact to the client.
572 @type instance: L{objects.Instance}
573 @param instance: the instance that is being migrated
574 @rtype: L{objects.MigrationStatus}
575 @return: the status of the current migration (one of
576 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
577 progress info that can be retrieved from the hypervisor
580 return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
583 def PowercycleNode(cls):
584 """Xen-specific powercycle.
586 This first does a Linux reboot (which triggers automatically a Xen
587 reboot), and if that fails it tries to do a Xen reboot. The reason
588 we don't try a Xen reboot first is that the xen reboot launches an
589 external command which connects to the Xen hypervisor, and that
590 won't work in case the root filesystem is broken and/or the xend
591 daemon is not working.
595 cls.LinuxPowercycle()
597 utils.RunCmd([constants.XEN_CMD, "debug", "R"])
600 class XenPvmHypervisor(XenHypervisor):
601 """Xen PVM hypervisor interface"""
604 constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
605 constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
606 constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
607 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
608 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
609 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
610 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
611 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
612 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
613 # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
614 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
615 constants.HV_REBOOT_BEHAVIOR:
616 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
617 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
621 def _WriteConfigFile(cls, instance, startup_memory, block_devices):
622 """Write the Xen config file for the instance.
625 hvp = instance.hvparams
627 config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
629 # if bootloader is True, use bootloader instead of kernel and ramdisk
631 if hvp[constants.HV_USE_BOOTLOADER]:
632 # bootloader handling
633 bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
635 config.write("bootloader = '%s'\n" % bootloader_path)
637 raise errors.HypervisorError("Bootloader enabled, but missing"
640 bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
642 config.write("bootargs = '%s'\n" % bootloader_args)
645 kpath = hvp[constants.HV_KERNEL_PATH]
646 config.write("kernel = '%s'\n" % kpath)
649 initrd_path = hvp[constants.HV_INITRD_PATH]
651 config.write("ramdisk = '%s'\n" % initrd_path)
653 # rest of the settings
654 config.write("memory = %d\n" % startup_memory)
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, startup_memory, 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 config.write("memory = %d\n" % startup_memory)
755 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
756 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
757 cpu_pinning = cls._CreateConfigCpus(hvp[constants.HV_CPU_MASK])
759 config.write("%s\n" % cpu_pinning)
761 config.write("name = '%s'\n" % instance.name)
762 if hvp[constants.HV_PAE]:
763 config.write("pae = 1\n")
765 config.write("pae = 0\n")
766 if hvp[constants.HV_ACPI]:
767 config.write("acpi = 1\n")
769 config.write("acpi = 0\n")
770 config.write("apic = 1\n")
771 config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
772 config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
773 config.write("sdl = 0\n")
774 config.write("usb = 1\n")
775 config.write("usbdevice = 'tablet'\n")
776 config.write("vnc = 1\n")
777 if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
778 config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
780 config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
782 if instance.network_port > constants.VNC_BASE_PORT:
783 display = instance.network_port - constants.VNC_BASE_PORT
784 config.write("vncdisplay = %s\n" % display)
785 config.write("vncunused = 0\n")
787 config.write("# vncdisplay = 1\n")
788 config.write("vncunused = 1\n")
790 vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
792 password = utils.ReadFile(vnc_pwd_file)
793 except EnvironmentError, err:
794 raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
797 config.write("vncpasswd = '%s'\n" % password.rstrip())
799 config.write("serial = 'pty'\n")
800 if hvp[constants.HV_USE_LOCALTIME]:
801 config.write("localtime = 1\n")
804 nic_type = hvp[constants.HV_NIC_TYPE]
806 # ensure old instances don't change
807 nic_type_str = ", type=ioemu"
808 elif nic_type == constants.HT_NIC_PARAVIRTUAL:
809 nic_type_str = ", type=paravirtualized"
811 nic_type_str = ", model=%s, type=ioemu" % nic_type
812 for nic in instance.nics:
813 nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
814 ip = getattr(nic, "ip", None)
816 nic_str += ", ip=%s" % ip
817 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
818 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
819 vif_data.append("'%s'" % nic_str)
821 config.write("vif = [%s]\n" % ",".join(vif_data))
823 disk_data = cls._GetConfigFileDiskData(block_devices,
824 hvp[constants.HV_BLOCKDEV_PREFIX])
826 iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
828 iso = "'file:%s,hdc:cdrom,r'" % iso_path
829 disk_data.append(iso)
831 config.write("disk = [%s]\n" % (",".join(disk_data)))
833 config.write("on_poweroff = 'destroy'\n")
834 if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
835 config.write("on_reboot = 'restart'\n")
837 config.write("on_reboot = 'destroy'\n")
838 config.write("on_crash = 'restart'\n")
839 # just in case it exists
840 utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
842 utils.WriteFile(cls._ConfigFileName(instance.name),
843 data=config.getvalue())
844 except EnvironmentError, err:
845 raise errors.HypervisorError("Cannot write Xen instance confile"
847 (cls._ConfigFileName(instance.name), err))