Abstract xen's 'xm' command as a constant
[ganeti-local] / lib / hypervisor / hv_xen.py
1 #
2 #
3
4 # Copyright (C) 2006, 2007, 2008, 2009, 2010 Google Inc.
5 #
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.
10 #
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.
15 #
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
19 # 02110-1301, USA.
20
21
22 """Xen hypervisors
23
24 """
25
26 import logging
27 from cStringIO import StringIO
28
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
36
37 class XenHypervisor(hv_base.BaseHypervisor):
38   """Xen generic hypervisor interface
39
40   This is the Xen base class used for both Xen PVM and HVM. It contains
41   all the functionality that is identical for both.
42
43   """
44   CAN_MIGRATE = True
45   REBOOT_RETRY_COUNT = 60
46   REBOOT_RETRY_INTERVAL = 10
47
48   ANCILLARY_FILES = [
49     "/etc/xen/xend-config.sxp",
50     "/etc/xen/scripts/vif-bridge",
51     ]
52
53   @classmethod
54   def _WriteConfigFile(cls, instance, block_devices):
55     """Write the Xen config file for the instance.
56
57     """
58     raise NotImplementedError
59
60   @staticmethod
61   def _WriteConfigFileStatic(instance_name, data):
62     """Write the Xen config file for the instance.
63
64     This version of the function just writes the config file from static data.
65
66     """
67     utils.WriteFile("/etc/xen/%s" % instance_name, data=data)
68
69   @staticmethod
70   def _ReadConfigFile(instance_name):
71     """Returns the contents of the instance config file.
72
73     """
74     try:
75       file_content = utils.ReadFile("/etc/xen/%s" % instance_name)
76     except EnvironmentError, err:
77       raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
78     return file_content
79
80   @staticmethod
81   def _RemoveConfigFile(instance_name):
82     """Remove the xen configuration file.
83
84     """
85     utils.RemoveFile("/etc/xen/%s" % instance_name)
86
87   @staticmethod
88   def _RunXmList(xmlist_errors):
89     """Helper function for L{_GetXMList} to run "xm list".
90
91     """
92     result = utils.RunCmd([constants.XEN_CMD, "list"])
93     if result.failed:
94       logging.error("xm list failed (%s): %s", result.fail_reason,
95                     result.output)
96       xmlist_errors.append(result)
97       raise utils.RetryAgain()
98
99     # skip over the heading
100     return result.stdout.splitlines()[1:]
101
102   @classmethod
103   def _GetXMList(cls, include_node):
104     """Return the list of running instances.
105
106     If the include_node argument is True, then we return information
107     for dom0 also, otherwise we filter that from the return value.
108
109     @return: list of (name, id, memory, vcpus, state, time spent)
110
111     """
112     xmlist_errors = []
113     try:
114       lines = utils.Retry(cls._RunXmList, 1, 5, args=(xmlist_errors, ))
115     except utils.RetryTimeout:
116       if xmlist_errors:
117         xmlist_result = xmlist_errors.pop()
118
119         errmsg = ("xm list failed, timeout exceeded (%s): %s" %
120                   (xmlist_result.fail_reason, xmlist_result.output))
121       else:
122         errmsg = "xm list failed"
123
124       raise errors.HypervisorError(errmsg)
125
126     result = []
127     for line in lines:
128       # The format of lines is:
129       # Name      ID Mem(MiB) VCPUs State  Time(s)
130       # Domain-0   0  3418     4 r-----    266.2
131       data = line.split()
132       if len(data) != 6:
133         raise errors.HypervisorError("Can't parse output of xm list,"
134                                      " line: %s" % line)
135       try:
136         data[1] = int(data[1])
137         data[2] = int(data[2])
138         data[3] = int(data[3])
139         data[5] = float(data[5])
140       except (TypeError, ValueError), err:
141         raise errors.HypervisorError("Can't parse output of xm list,"
142                                      " line: %s, error: %s" % (line, err))
143
144       # skip the Domain-0 (optional)
145       if include_node or data[0] != "Domain-0":
146         result.append(data)
147
148     return result
149
150   def ListInstances(self):
151     """Get the list of running instances.
152
153     """
154     xm_list = self._GetXMList(False)
155     names = [info[0] for info in xm_list]
156     return names
157
158   def GetInstanceInfo(self, instance_name):
159     """Get instance properties.
160
161     @param instance_name: the instance name
162
163     @return: tuple (name, id, memory, vcpus, stat, times)
164
165     """
166     xm_list = self._GetXMList(instance_name == "Domain-0")
167     result = None
168     for data in xm_list:
169       if data[0] == instance_name:
170         result = data
171         break
172     return result
173
174   def GetAllInstancesInfo(self):
175     """Get properties of all instances.
176
177     @return: list of tuples (name, id, memory, vcpus, stat, times)
178
179     """
180     xm_list = self._GetXMList(False)
181     return xm_list
182
183   def StartInstance(self, instance, block_devices, startup_paused):
184     """Start an instance.
185
186     """
187     self._WriteConfigFile(instance, block_devices)
188     cmd = [constants.XEN_CMD, "create"]
189     if startup_paused:
190       cmd.extend(["--paused"])
191     cmd.extend([instance.name])
192     result = utils.RunCmd(cmd)
193
194     if result.failed:
195       raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
196                                    (instance.name, result.fail_reason,
197                                     result.output))
198
199   def StopInstance(self, instance, force=False, retry=False, name=None):
200     """Stop an instance.
201
202     """
203     if name is None:
204       name = instance.name
205     self._RemoveConfigFile(name)
206     if force:
207       command = [constants.XEN_CMD, "destroy", name]
208     else:
209       command = [constants.XEN_CMD, "shutdown", name]
210     result = utils.RunCmd(command)
211
212     if result.failed:
213       raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
214                                    (name, result.fail_reason, result.output))
215
216   def RebootInstance(self, instance):
217     """Reboot an instance.
218
219     """
220     ini_info = self.GetInstanceInfo(instance.name)
221
222     if ini_info is None:
223       raise errors.HypervisorError("Failed to reboot instance %s,"
224                                    " not running" % instance.name)
225
226     result = utils.RunCmd([constants.XEN_CMD, "reboot", instance.name])
227     if result.failed:
228       raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
229                                    (instance.name, result.fail_reason,
230                                     result.output))
231
232     def _CheckInstance():
233       new_info = self.GetInstanceInfo(instance.name)
234
235       # check if the domain ID has changed or the run time has decreased
236       if (new_info is not None and
237           (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])):
238         return
239
240       raise utils.RetryAgain()
241
242     try:
243       utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
244                   self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
245     except utils.RetryTimeout:
246       raise errors.HypervisorError("Failed to reboot instance %s: instance"
247                                    " did not reboot in the expected interval" %
248                                    (instance.name, ))
249
250   def GetNodeInfo(self):
251     """Return information about the node.
252
253     @return: a dict with the following keys (memory values in MiB):
254           - memory_total: the total memory size on the node
255           - memory_free: the available memory on the node for instances
256           - memory_dom0: the memory used by the node itself, if available
257           - nr_cpus: total number of CPUs
258           - nr_nodes: in a NUMA system, the number of domains
259           - nr_sockets: the number of physical CPU sockets in the node
260           - hv_version: the hypervisor version in the form (major, minor)
261
262     """
263     # note: in xen 3, memory has changed to total_memory
264     result = utils.RunCmd([constants.XEN_CMD, "info"])
265     if result.failed:
266       logging.error("Can't run 'xm info' (%s): %s", result.fail_reason,
267                     result.output)
268       return None
269
270     xmoutput = result.stdout.splitlines()
271     result = {}
272     cores_per_socket = threads_per_core = nr_cpus = None
273     xen_major, xen_minor = None, None
274     for line in xmoutput:
275       splitfields = line.split(":", 1)
276
277       if len(splitfields) > 1:
278         key = splitfields[0].strip()
279         val = splitfields[1].strip()
280         if key == "memory" or key == "total_memory":
281           result["memory_total"] = int(val)
282         elif key == "free_memory":
283           result["memory_free"] = int(val)
284         elif key == "nr_cpus":
285           nr_cpus = result["cpu_total"] = int(val)
286         elif key == "nr_nodes":
287           result["cpu_nodes"] = int(val)
288         elif key == "cores_per_socket":
289           cores_per_socket = int(val)
290         elif key == "threads_per_core":
291           threads_per_core = int(val)
292         elif key == "xen_major":
293           xen_major = int(val)
294         elif key == "xen_minor":
295           xen_minor = int(val)
296
297     if (cores_per_socket is not None and
298         threads_per_core is not None and nr_cpus is not None):
299       result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
300
301     dom0_info = self.GetInstanceInfo("Domain-0")
302     if dom0_info is not None:
303       result["memory_dom0"] = dom0_info[2]
304
305     if not (xen_major is None or xen_minor is None):
306       result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
307
308     return result
309
310   @classmethod
311   def GetInstanceConsole(cls, instance, hvparams, beparams):
312     """Return a command for connecting to the console of an instance.
313
314     """
315     return objects.InstanceConsole(instance=instance.name,
316                                    kind=constants.CONS_SSH,
317                                    host=instance.primary_node,
318                                    user=constants.GANETI_RUNAS,
319                                    command=[constants.XM_CONSOLE_WRAPPER,
320                                             instance.name])
321
322   def Verify(self):
323     """Verify the hypervisor.
324
325     For Xen, this verifies that the xend process is running.
326
327     """
328     result = utils.RunCmd([constants.XEN_CMD, "info"])
329     if result.failed:
330       return "'xm info' failed: %s, %s" % (result.fail_reason, result.output)
331
332   @staticmethod
333   def _GetConfigFileDiskData(block_devices, blockdev_prefix):
334     """Get disk directive for xen config file.
335
336     This method builds the xen config disk directive according to the
337     given disk_template and block_devices.
338
339     @param block_devices: list of tuples (cfdev, rldev):
340         - cfdev: dict containing ganeti config disk part
341         - rldev: ganeti.bdev.BlockDev object
342     @param blockdev_prefix: a string containing blockdevice prefix,
343                             e.g. "sd" for /dev/sda
344
345     @return: string containing disk directive for xen instance config file
346
347     """
348     FILE_DRIVER_MAP = {
349       constants.FD_LOOP: "file",
350       constants.FD_BLKTAP: "tap:aio",
351       }
352     disk_data = []
353     if len(block_devices) > 24:
354       # 'z' - 'a' = 24
355       raise errors.HypervisorError("Too many disks")
356     namespace = [blockdev_prefix + chr(i + ord("a")) for i in range(24)]
357     for sd_name, (cfdev, dev_path) in zip(namespace, block_devices):
358       if cfdev.mode == constants.DISK_RDWR:
359         mode = "w"
360       else:
361         mode = "r"
362       if cfdev.dev_type == constants.LD_FILE:
363         line = "'%s:%s,%s,%s'" % (FILE_DRIVER_MAP[cfdev.physical_id[0]],
364                                   dev_path, sd_name, mode)
365       else:
366         line = "'phy:%s,%s,%s'" % (dev_path, sd_name, mode)
367       disk_data.append(line)
368
369     return disk_data
370
371   def MigrationInfo(self, instance):
372     """Get instance information to perform a migration.
373
374     @type instance: L{objects.Instance}
375     @param instance: instance to be migrated
376     @rtype: string
377     @return: content of the xen config file
378
379     """
380     return self._ReadConfigFile(instance.name)
381
382   def AcceptInstance(self, instance, info, target):
383     """Prepare to accept an instance.
384
385     @type instance: L{objects.Instance}
386     @param instance: instance to be accepted
387     @type info: string
388     @param info: content of the xen config file on the source node
389     @type target: string
390     @param target: target host (usually ip), on this node
391
392     """
393     pass
394
395   def FinalizeMigration(self, instance, info, success):
396     """Finalize an instance migration.
397
398     After a successful migration we write the xen config file.
399     We do nothing on a failure, as we did not change anything at accept time.
400
401     @type instance: L{objects.Instance}
402     @param instance: instance whose migration is being finalized
403     @type info: string
404     @param info: content of the xen config file on the source node
405     @type success: boolean
406     @param success: whether the migration was a success or a failure
407
408     """
409     if success:
410       self._WriteConfigFileStatic(instance.name, info)
411
412   def MigrateInstance(self, instance, target, live):
413     """Migrate an instance to a target node.
414
415     The migration will not be attempted if the instance is not
416     currently running.
417
418     @type instance: L{objects.Instance}
419     @param instance: the instance to be migrated
420     @type target: string
421     @param target: ip address of the target node
422     @type live: boolean
423     @param live: perform a live migration
424
425     """
426     if self.GetInstanceInfo(instance.name) is None:
427       raise errors.HypervisorError("Instance not running, cannot migrate")
428
429     port = instance.hvparams[constants.HV_MIGRATION_PORT]
430
431     if not netutils.TcpPing(target, port, live_port_needed=True):
432       raise errors.HypervisorError("Remote host %s not listening on port"
433                                    " %s, cannot migrate" % (target, port))
434
435     args = [constants.XEN_CMD, "migrate", "-p", "%d" % port]
436     if live:
437       args.append("-l")
438     args.extend([instance.name, target])
439     result = utils.RunCmd(args)
440     if result.failed:
441       raise errors.HypervisorError("Failed to migrate instance %s: %s" %
442                                    (instance.name, result.output))
443     # remove old xen file after migration succeeded
444     try:
445       self._RemoveConfigFile(instance.name)
446     except EnvironmentError:
447       logging.exception("Failure while removing instance config file")
448
449   @classmethod
450   def PowercycleNode(cls):
451     """Xen-specific powercycle.
452
453     This first does a Linux reboot (which triggers automatically a Xen
454     reboot), and if that fails it tries to do a Xen reboot. The reason
455     we don't try a Xen reboot first is that the xen reboot launches an
456     external command which connects to the Xen hypervisor, and that
457     won't work in case the root filesystem is broken and/or the xend
458     daemon is not working.
459
460     """
461     try:
462       cls.LinuxPowercycle()
463     finally:
464       utils.RunCmd([constants.XEN_CMD, "debug", "R"])
465
466
467 class XenPvmHypervisor(XenHypervisor):
468   """Xen PVM hypervisor interface"""
469
470   PARAMETERS = {
471     constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
472     constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
473     constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
474     constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
475     constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
476     constants.HV_ROOT_PATH: hv_base.NO_CHECK,
477     constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
478     constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
479     constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
480     # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
481     constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
482     constants.HV_REBOOT_BEHAVIOR:
483       hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS)
484     }
485
486   @classmethod
487   def _WriteConfigFile(cls, instance, block_devices):
488     """Write the Xen config file for the instance.
489
490     """
491     hvp = instance.hvparams
492     config = StringIO()
493     config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
494
495     # if bootloader is True, use bootloader instead of kernel and ramdisk
496     # parameters.
497     if hvp[constants.HV_USE_BOOTLOADER]:
498       # bootloader handling
499       bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
500       if bootloader_path:
501         config.write("bootloader = '%s'\n" % bootloader_path)
502       else:
503         raise errors.HypervisorError("Bootloader enabled, but missing"
504                                      " bootloader path")
505
506       bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
507       if bootloader_args:
508         config.write("bootargs = '%s'\n" % bootloader_args)
509     else:
510       # kernel handling
511       kpath = hvp[constants.HV_KERNEL_PATH]
512       config.write("kernel = '%s'\n" % kpath)
513
514       # initrd handling
515       initrd_path = hvp[constants.HV_INITRD_PATH]
516       if initrd_path:
517         config.write("ramdisk = '%s'\n" % initrd_path)
518
519     # rest of the settings
520     config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
521     config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
522     config.write("name = '%s'\n" % instance.name)
523
524     vif_data = []
525     for nic in instance.nics:
526       nic_str = "mac=%s" % (nic.mac)
527       ip = getattr(nic, "ip", None)
528       if ip is not None:
529         nic_str += ", ip=%s" % ip
530       if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
531         nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
532       vif_data.append("'%s'" % nic_str)
533
534     disk_data = cls._GetConfigFileDiskData(block_devices,
535                                            hvp[constants.HV_BLOCKDEV_PREFIX])
536
537     config.write("vif = [%s]\n" % ",".join(vif_data))
538     config.write("disk = [%s]\n" % ",".join(disk_data))
539
540     if hvp[constants.HV_ROOT_PATH]:
541       config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
542     config.write("on_poweroff = 'destroy'\n")
543     if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
544       config.write("on_reboot = 'restart'\n")
545     else:
546       config.write("on_reboot = 'destroy'\n")
547     config.write("on_crash = 'restart'\n")
548     config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
549     # just in case it exists
550     utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
551     try:
552       utils.WriteFile("/etc/xen/%s" % instance.name, data=config.getvalue())
553     except EnvironmentError, err:
554       raise errors.HypervisorError("Cannot write Xen instance confile"
555                                    " file /etc/xen/%s: %s" %
556                                    (instance.name, err))
557
558     return True
559
560
561 class XenHvmHypervisor(XenHypervisor):
562   """Xen HVM hypervisor interface"""
563
564   ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
565     constants.VNC_PASSWORD_FILE,
566     ]
567
568   PARAMETERS = {
569     constants.HV_ACPI: hv_base.NO_CHECK,
570     constants.HV_BOOT_ORDER: (True, ) +
571       (lambda x: x and len(x.strip("acdn")) == 0,
572        "Invalid boot order specified, must be one or more of [acdn]",
573        None, None),
574     constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
575     constants.HV_DISK_TYPE:
576       hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
577     constants.HV_NIC_TYPE:
578       hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
579     constants.HV_PAE: hv_base.NO_CHECK,
580     constants.HV_VNC_BIND_ADDRESS:
581       (False, netutils.IP4Address.IsValid,
582        "VNC bind address is not a valid IP address", None, None),
583     constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
584     constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
585     constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
586     constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
587     constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
588     constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
589     # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
590     constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
591     constants.HV_REBOOT_BEHAVIOR:
592       hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS)
593     }
594
595   @classmethod
596   def _WriteConfigFile(cls, instance, block_devices):
597     """Create a Xen 3.1 HVM config file.
598
599     """
600     hvp = instance.hvparams
601
602     config = StringIO()
603     config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
604
605     # kernel handling
606     kpath = hvp[constants.HV_KERNEL_PATH]
607     config.write("kernel = '%s'\n" % kpath)
608
609     config.write("builder = 'hvm'\n")
610     config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
611     config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
612     config.write("name = '%s'\n" % instance.name)
613     if hvp[constants.HV_PAE]:
614       config.write("pae = 1\n")
615     else:
616       config.write("pae = 0\n")
617     if hvp[constants.HV_ACPI]:
618       config.write("acpi = 1\n")
619     else:
620       config.write("acpi = 0\n")
621     config.write("apic = 1\n")
622     config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
623     config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
624     config.write("sdl = 0\n")
625     config.write("usb = 1\n")
626     config.write("usbdevice = 'tablet'\n")
627     config.write("vnc = 1\n")
628     if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
629       config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
630     else:
631       config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
632
633     if instance.network_port > constants.VNC_BASE_PORT:
634       display = instance.network_port - constants.VNC_BASE_PORT
635       config.write("vncdisplay = %s\n" % display)
636       config.write("vncunused = 0\n")
637     else:
638       config.write("# vncdisplay = 1\n")
639       config.write("vncunused = 1\n")
640
641     vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
642     try:
643       password = utils.ReadFile(vnc_pwd_file)
644     except EnvironmentError, err:
645       raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
646                                    (vnc_pwd_file, err))
647
648     config.write("vncpasswd = '%s'\n" % password.rstrip())
649
650     config.write("serial = 'pty'\n")
651     if hvp[constants.HV_USE_LOCALTIME]:
652       config.write("localtime = 1\n")
653
654     vif_data = []
655     nic_type = hvp[constants.HV_NIC_TYPE]
656     if nic_type is None:
657       # ensure old instances don't change
658       nic_type_str = ", type=ioemu"
659     elif nic_type == constants.HT_NIC_PARAVIRTUAL:
660       nic_type_str = ", type=paravirtualized"
661     else:
662       nic_type_str = ", model=%s, type=ioemu" % nic_type
663     for nic in instance.nics:
664       nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
665       ip = getattr(nic, "ip", None)
666       if ip is not None:
667         nic_str += ", ip=%s" % ip
668       if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
669         nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
670       vif_data.append("'%s'" % nic_str)
671
672     config.write("vif = [%s]\n" % ",".join(vif_data))
673
674     disk_data = cls._GetConfigFileDiskData(block_devices,
675                                            hvp[constants.HV_BLOCKDEV_PREFIX])
676
677     iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
678     if iso_path:
679       iso = "'file:%s,hdc:cdrom,r'" % iso_path
680       disk_data.append(iso)
681
682     config.write("disk = [%s]\n" % (",".join(disk_data)))
683
684     config.write("on_poweroff = 'destroy'\n")
685     if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
686       config.write("on_reboot = 'restart'\n")
687     else:
688       config.write("on_reboot = 'destroy'\n")
689     config.write("on_crash = 'restart'\n")
690     # just in case it exists
691     utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
692     try:
693       utils.WriteFile("/etc/xen/%s" % instance.name,
694                       data=config.getvalue())
695     except EnvironmentError, err:
696       raise errors.HypervisorError("Cannot write Xen instance confile"
697                                    " file /etc/xen/%s: %s" %
698                                    (instance.name, err))
699
700     return True