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