Merge branch 'stable-2.9' into stable-2.10
[ganeti-local] / lib / hypervisor / hv_xen.py
1 #
2 #
3
4 # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 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 import errno
28 import string # pylint: disable=W0402
29 import shutil
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 from ganeti import netutils
37 from ganeti import objects
38 from ganeti import pathutils
39
40
41 XEND_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xend-config.sxp")
42 XL_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xen/xl.conf")
43 VIF_BRIDGE_SCRIPT = utils.PathJoin(pathutils.XEN_CONFIG_DIR,
44                                    "scripts/vif-bridge")
45 _DOM0_NAME = "Domain-0"
46 _DISK_LETTERS = string.ascii_lowercase
47
48 _FILE_DRIVER_MAP = {
49   constants.FD_LOOP: "file",
50   constants.FD_BLKTAP: "tap:aio",
51   constants.FD_BLKTAP2: "tap2:tapdisk:aio",
52   }
53
54
55 def _CreateConfigCpus(cpu_mask):
56   """Create a CPU config string for Xen's config file.
57
58   """
59   # Convert the string CPU mask to a list of list of int's
60   cpu_list = utils.ParseMultiCpuMask(cpu_mask)
61
62   if len(cpu_list) == 1:
63     all_cpu_mapping = cpu_list[0]
64     if all_cpu_mapping == constants.CPU_PINNING_OFF:
65       # If CPU pinning has 1 entry that's "all", then remove the
66       # parameter from the config file
67       return None
68     else:
69       # If CPU pinning has one non-all entry, mapping all vCPUS (the entire
70       # VM) to one physical CPU, using format 'cpu = "C"'
71       return "cpu = \"%s\"" % ",".join(map(str, all_cpu_mapping))
72   else:
73
74     def _GetCPUMap(vcpu):
75       if vcpu[0] == constants.CPU_PINNING_ALL_VAL:
76         cpu_map = constants.CPU_PINNING_ALL_XEN
77       else:
78         cpu_map = ",".join(map(str, vcpu))
79       return "\"%s\"" % cpu_map
80
81     # build the result string in format 'cpus = [ "c", "c", "c" ]',
82     # where each c is a physical CPU number, a range, a list, or any
83     # combination
84     return "cpus = [ %s ]" % ", ".join(map(_GetCPUMap, cpu_list))
85
86
87 def _RunInstanceList(fn, instance_list_errors):
88   """Helper function for L{_GetInstanceList} to retrieve the list of instances
89   from xen.
90
91   @type fn: callable
92   @param fn: Function to query xen for the list of instances
93   @type instance_list_errors: list
94   @param instance_list_errors: Error list
95   @rtype: list
96
97   """
98   result = fn()
99   if result.failed:
100     logging.error("Retrieving the instance list from xen failed (%s): %s",
101                   result.fail_reason, result.output)
102     instance_list_errors.append(result)
103     raise utils.RetryAgain()
104
105   # skip over the heading
106   return result.stdout.splitlines()
107
108
109 def _ParseInstanceList(lines, include_node):
110   """Parses the output of listing instances by xen.
111
112   @type lines: list
113   @param lines: Result of retrieving the instance list from xen
114   @type include_node: boolean
115   @param include_node: If True, return information for Dom0
116   @return: list of tuple containing (name, id, memory, vcpus, state, time
117     spent)
118
119   """
120   result = []
121
122   # Iterate through all lines while ignoring header
123   for line in lines[1:]:
124     # The format of lines is:
125     # Name      ID Mem(MiB) VCPUs State  Time(s)
126     # Domain-0   0  3418     4 r-----    266.2
127     data = line.split()
128     if len(data) != 6:
129       raise errors.HypervisorError("Can't parse instance list,"
130                                    " line: %s" % line)
131     try:
132       data[1] = int(data[1])
133       data[2] = int(data[2])
134       data[3] = int(data[3])
135       data[5] = float(data[5])
136     except (TypeError, ValueError), err:
137       raise errors.HypervisorError("Can't parse instance list,"
138                                    " line: %s, error: %s" % (line, err))
139
140     # skip the Domain-0 (optional)
141     if include_node or data[0] != _DOM0_NAME:
142       result.append(data)
143
144   return result
145
146
147 def _GetInstanceList(fn, include_node, _timeout=5):
148   """Return the list of running instances.
149
150   See L{_RunInstanceList} and L{_ParseInstanceList} for parameter details.
151
152   """
153   instance_list_errors = []
154   try:
155     lines = utils.Retry(_RunInstanceList, (0.3, 1.5, 1.0), _timeout,
156                         args=(fn, instance_list_errors))
157   except utils.RetryTimeout:
158     if instance_list_errors:
159       instance_list_result = instance_list_errors.pop()
160
161       errmsg = ("listing instances failed, timeout exceeded (%s): %s" %
162                 (instance_list_result.fail_reason, instance_list_result.output))
163     else:
164       errmsg = "listing instances failed"
165
166     raise errors.HypervisorError(errmsg)
167
168   return _ParseInstanceList(lines, include_node)
169
170
171 def _IsInstanceRunning(instance_info):
172   return instance_info == "r-----" \
173       or instance_info == "-b----"
174
175
176 def _IsInstanceShutdown(instance_info):
177   return instance_info == "---s--"
178
179
180 def _ParseNodeInfo(info):
181   """Return information about the node.
182
183   @return: a dict with the following keys (memory values in MiB):
184         - memory_total: the total memory size on the node
185         - memory_free: the available memory on the node for instances
186         - nr_cpus: total number of CPUs
187         - nr_nodes: in a NUMA system, the number of domains
188         - nr_sockets: the number of physical CPU sockets in the node
189         - hv_version: the hypervisor version in the form (major, minor)
190
191   """
192   result = {}
193   cores_per_socket = threads_per_core = nr_cpus = None
194   xen_major, xen_minor = None, None
195   memory_total = None
196   memory_free = None
197
198   for line in info.splitlines():
199     fields = line.split(":", 1)
200
201     if len(fields) < 2:
202       continue
203
204     (key, val) = map(lambda s: s.strip(), fields)
205
206     # Note: in Xen 3, memory has changed to total_memory
207     if key in ("memory", "total_memory"):
208       memory_total = int(val)
209     elif key == "free_memory":
210       memory_free = int(val)
211     elif key == "nr_cpus":
212       nr_cpus = result["cpu_total"] = int(val)
213     elif key == "nr_nodes":
214       result["cpu_nodes"] = int(val)
215     elif key == "cores_per_socket":
216       cores_per_socket = int(val)
217     elif key == "threads_per_core":
218       threads_per_core = int(val)
219     elif key == "xen_major":
220       xen_major = int(val)
221     elif key == "xen_minor":
222       xen_minor = int(val)
223
224   if None not in [cores_per_socket, threads_per_core, nr_cpus]:
225     result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
226
227   if memory_free is not None:
228     result["memory_free"] = memory_free
229
230   if memory_total is not None:
231     result["memory_total"] = memory_total
232
233   if not (xen_major is None or xen_minor is None):
234     result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
235
236   return result
237
238
239 def _MergeInstanceInfo(info, instance_list):
240   """Updates node information from L{_ParseNodeInfo} with instance info.
241
242   @type info: dict
243   @param info: Result from L{_ParseNodeInfo}
244   @type instance_list: list of tuples
245   @param instance_list: list of instance information; one tuple per instance
246   @rtype: dict
247
248   """
249   total_instmem = 0
250
251   for (name, _, mem, vcpus, _, _) in instance_list:
252     if name == _DOM0_NAME:
253       info["memory_dom0"] = mem
254       info["cpu_dom0"] = vcpus
255
256     # Include Dom0 in total memory usage
257     total_instmem += mem
258
259   memory_free = info.get("memory_free")
260   memory_total = info.get("memory_total")
261
262   # Calculate memory used by hypervisor
263   if None not in [memory_total, memory_free, total_instmem]:
264     info["memory_hv"] = memory_total - memory_free - total_instmem
265
266   return info
267
268
269 def _GetNodeInfo(info, instance_list):
270   """Combines L{_MergeInstanceInfo} and L{_ParseNodeInfo}.
271
272   @type instance_list: list of tuples
273   @param instance_list: list of instance information; one tuple per instance
274
275   """
276   return _MergeInstanceInfo(_ParseNodeInfo(info), instance_list)
277
278
279 def _GetConfigFileDiskData(block_devices, blockdev_prefix,
280                            _letters=_DISK_LETTERS):
281   """Get disk directives for Xen config file.
282
283   This method builds the xen config disk directive according to the
284   given disk_template and block_devices.
285
286   @param block_devices: list of tuples (cfdev, rldev):
287       - cfdev: dict containing ganeti config disk part
288       - rldev: ganeti.block.bdev.BlockDev object
289   @param blockdev_prefix: a string containing blockdevice prefix,
290                           e.g. "sd" for /dev/sda
291
292   @return: string containing disk directive for xen instance config file
293
294   """
295   if len(block_devices) > len(_letters):
296     raise errors.HypervisorError("Too many disks")
297
298   disk_data = []
299
300   for sd_suffix, (cfdev, dev_path, _) in zip(_letters, block_devices):
301     sd_name = blockdev_prefix + sd_suffix
302
303     if cfdev.mode == constants.DISK_RDWR:
304       mode = "w"
305     else:
306       mode = "r"
307
308     if cfdev.dev_type in [constants.DT_FILE, constants.DT_SHARED_FILE]:
309       driver = _FILE_DRIVER_MAP[cfdev.logical_id[0]]
310     else:
311       driver = "phy"
312
313     disk_data.append("'%s:%s,%s,%s'" % (driver, dev_path, sd_name, mode))
314
315   return disk_data
316
317
318 def _QuoteCpuidField(data):
319   """Add quotes around the CPUID field only if necessary.
320
321   Xen CPUID fields come in two shapes: LIBXL strings, which need quotes around
322   them, and lists of XEND strings, which don't.
323
324   @param data: Either type of parameter.
325   @return: The quoted version thereof.
326
327   """
328   return "'%s'" % data if data.startswith("host") else data
329
330
331 class XenHypervisor(hv_base.BaseHypervisor):
332   """Xen generic hypervisor interface
333
334   This is the Xen base class used for both Xen PVM and HVM. It contains
335   all the functionality that is identical for both.
336
337   """
338   CAN_MIGRATE = True
339   REBOOT_RETRY_COUNT = 60
340   REBOOT_RETRY_INTERVAL = 10
341   _ROOT_DIR = pathutils.RUN_DIR + "/xen-hypervisor"
342   _NICS_DIR = _ROOT_DIR + "/nic" # contains NICs' info
343   _DIRS = [_ROOT_DIR, _NICS_DIR]
344
345   ANCILLARY_FILES = [
346     XEND_CONFIG_FILE,
347     XL_CONFIG_FILE,
348     VIF_BRIDGE_SCRIPT,
349     ]
350   ANCILLARY_FILES_OPT = [
351     XL_CONFIG_FILE,
352     ]
353
354   def __init__(self, _cfgdir=None, _run_cmd_fn=None, _cmd=None):
355     hv_base.BaseHypervisor.__init__(self)
356
357     if _cfgdir is None:
358       self._cfgdir = pathutils.XEN_CONFIG_DIR
359     else:
360       self._cfgdir = _cfgdir
361
362     if _run_cmd_fn is None:
363       self._run_cmd_fn = utils.RunCmd
364     else:
365       self._run_cmd_fn = _run_cmd_fn
366
367     self._cmd = _cmd
368
369   @staticmethod
370   def _GetCommandFromHvparams(hvparams):
371     """Returns the Xen command extracted from the given hvparams.
372
373     @type hvparams: dict of strings
374     @param hvparams: hypervisor parameters
375
376     """
377     if hvparams is None or constants.HV_XEN_CMD not in hvparams:
378       raise errors.HypervisorError("Cannot determine xen command.")
379     else:
380       return hvparams[constants.HV_XEN_CMD]
381
382   def _GetCommand(self, hvparams):
383     """Returns Xen command to use.
384
385     @type hvparams: dict of strings
386     @param hvparams: hypervisor parameters
387
388     """
389     if self._cmd is None:
390       cmd = XenHypervisor._GetCommandFromHvparams(hvparams)
391     else:
392       cmd = self._cmd
393
394     if cmd not in constants.KNOWN_XEN_COMMANDS:
395       raise errors.ProgrammerError("Unknown Xen command '%s'" % cmd)
396
397     return cmd
398
399   def _RunXen(self, args, hvparams):
400     """Wrapper around L{utils.process.RunCmd} to run Xen command.
401
402     @type hvparams: dict of strings
403     @param hvparams: dictionary of hypervisor params
404     @see: L{utils.process.RunCmd}
405
406     """
407     cmd = [self._GetCommand(hvparams)]
408     cmd.extend(args)
409
410     return self._run_cmd_fn(cmd)
411
412   def _ConfigFileName(self, instance_name):
413     """Get the config file name for an instance.
414
415     @param instance_name: instance name
416     @type instance_name: str
417     @return: fully qualified path to instance config file
418     @rtype: str
419
420     """
421     return utils.PathJoin(self._cfgdir, instance_name)
422
423   @classmethod
424   def _WriteNICInfoFile(cls, instance, idx, nic):
425     """Write the Xen config file for the instance.
426
427     This version of the function just writes the config file from static data.
428
429     """
430     instance_name = instance.name
431     dirs = [(dname, constants.RUN_DIRS_MODE)
432             for dname in cls._DIRS + [cls._InstanceNICDir(instance_name)]]
433     utils.EnsureDirs(dirs)
434
435     cfg_file = cls._InstanceNICFile(instance_name, idx)
436     data = StringIO()
437
438     data.write("TAGS=%s\n" % r"\ ".join(instance.GetTags()))
439     if nic.netinfo:
440       netinfo = objects.Network.FromDict(nic.netinfo)
441       for k, v in netinfo.HooksDict().iteritems():
442         data.write("%s=%s\n" % (k, v))
443
444     data.write("MAC=%s\n" % nic.mac)
445     if nic.ip:
446       data.write("IP=%s\n" % nic.ip)
447     data.write("INTERFACE_INDEX=%s\n" % str(idx))
448     if nic.name:
449       data.write("INTERFACE_NAME=%s\n" % nic.name)
450     data.write("INTERFACE_UUID=%s\n" % nic.uuid)
451     data.write("MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
452     data.write("LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
453
454     try:
455       utils.WriteFile(cfg_file, data=data.getvalue())
456     except EnvironmentError, err:
457       raise errors.HypervisorError("Cannot write Xen instance configuration"
458                                    " file %s: %s" % (cfg_file, err))
459
460   @classmethod
461   def _InstanceNICDir(cls, instance_name):
462     """Returns the directory holding the tap device files for a given instance.
463
464     """
465     return utils.PathJoin(cls._NICS_DIR, instance_name)
466
467   @classmethod
468   def _InstanceNICFile(cls, instance_name, seq):
469     """Returns the name of the file containing the tap device for a given NIC
470
471     """
472     return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
473
474   @classmethod
475   def _GetConfig(cls, instance, startup_memory, block_devices):
476     """Build Xen configuration for an instance.
477
478     """
479     raise NotImplementedError
480
481   def _WriteConfigFile(self, instance_name, data):
482     """Write the Xen config file for the instance.
483
484     This version of the function just writes the config file from static data.
485
486     """
487     # just in case it exists
488     utils.RemoveFile(utils.PathJoin(self._cfgdir, "auto", instance_name))
489
490     cfg_file = self._ConfigFileName(instance_name)
491     try:
492       utils.WriteFile(cfg_file, data=data)
493     except EnvironmentError, err:
494       raise errors.HypervisorError("Cannot write Xen instance configuration"
495                                    " file %s: %s" % (cfg_file, err))
496
497   def _ReadConfigFile(self, instance_name):
498     """Returns the contents of the instance config file.
499
500     """
501     filename = self._ConfigFileName(instance_name)
502
503     try:
504       file_content = utils.ReadFile(filename)
505     except EnvironmentError, err:
506       raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
507
508     return file_content
509
510   def _RemoveConfigFile(self, instance_name):
511     """Remove the xen configuration file.
512
513     """
514     utils.RemoveFile(self._ConfigFileName(instance_name))
515     try:
516       shutil.rmtree(self._InstanceNICDir(instance_name))
517     except OSError, err:
518       if err.errno != errno.ENOENT:
519         raise
520
521   def _StashConfigFile(self, instance_name):
522     """Move the Xen config file to the log directory and return its new path.
523
524     """
525     old_filename = self._ConfigFileName(instance_name)
526     base = ("%s-%s" %
527             (instance_name, utils.TimestampForFilename()))
528     new_filename = utils.PathJoin(pathutils.LOG_XEN_DIR, base)
529     utils.RenameFile(old_filename, new_filename)
530     return new_filename
531
532   def _GetInstanceList(self, include_node, hvparams):
533     """Wrapper around module level L{_GetInstanceList}.
534
535     @type hvparams: dict of strings
536     @param hvparams: hypervisor parameters to be used on this node
537
538     """
539     return _GetInstanceList(lambda: self._RunXen(["list"], hvparams),
540                             include_node)
541
542   def ListInstances(self, hvparams=None):
543     """Get the list of running instances.
544
545     """
546     instance_list = self._GetInstanceList(False, hvparams)
547     names = [info[0] for info in instance_list]
548     return names
549
550   def GetInstanceInfo(self, instance_name, hvparams=None):
551     """Get instance properties.
552
553     @type instance_name: string
554     @param instance_name: the instance name
555     @type hvparams: dict of strings
556     @param hvparams: the instance's hypervisor params
557
558     @return: tuple (name, id, memory, vcpus, stat, times)
559
560     """
561     instance_list = self._GetInstanceList(instance_name == _DOM0_NAME, hvparams)
562     result = None
563     for data in instance_list:
564       if data[0] == instance_name:
565         result = data
566         break
567     return result
568
569   def GetAllInstancesInfo(self, hvparams=None):
570     """Get properties of all instances.
571
572     @type hvparams: dict of strings
573     @param hvparams: hypervisor parameters
574     @return: list of tuples (name, id, memory, vcpus, stat, times)
575
576     """
577     return self._GetInstanceList(False, hvparams)
578
579   def _MakeConfigFile(self, instance, startup_memory, block_devices):
580     """Gather configuration details and write to disk.
581
582     See L{_GetConfig} for arguments.
583
584     """
585     buf = StringIO()
586     buf.write("# Automatically generated by Ganeti. Do not edit!\n")
587     buf.write("\n")
588     buf.write(self._GetConfig(instance, startup_memory, block_devices))
589     buf.write("\n")
590
591     self._WriteConfigFile(instance.name, buf.getvalue())
592
593   def StartInstance(self, instance, block_devices, startup_paused):
594     """Start an instance.
595
596     """
597     startup_memory = self._InstanceStartupMemory(instance)
598
599     self._MakeConfigFile(instance, startup_memory, block_devices)
600
601     cmd = ["create"]
602     if startup_paused:
603       cmd.append("-p")
604     cmd.append(self._ConfigFileName(instance.name))
605
606     result = self._RunXen(cmd, instance.hvparams)
607     if result.failed:
608       # Move the Xen configuration file to the log directory to avoid
609       # leaving a stale config file behind.
610       stashed_config = self._StashConfigFile(instance.name)
611       raise errors.HypervisorError("Failed to start instance %s: %s (%s). Moved"
612                                    " config file to %s" %
613                                    (instance.name, result.fail_reason,
614                                     result.output, stashed_config))
615
616   def StopInstance(self, instance, force=False, retry=False, name=None):
617     """Stop an instance.
618
619     """
620     if name is None:
621       name = instance.name
622
623     return self._StopInstance(name, force, instance.hvparams)
624
625   def _ShutdownInstance(self, name, hvparams):
626     """Shutdown an instance if the instance is running.
627
628     @type name: string
629     @param name: name of the instance to stop
630     @type hvparams: dict of string
631     @param hvparams: hypervisor parameters of the instance
632
633     The '-w' flag waits for shutdown to complete which avoids the need
634     to poll in the case where we want to destroy the domain
635     immediately after shutdown.
636
637     """
638     instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
639
640     if instance_info is None or _IsInstanceShutdown(instance_info[4]):
641       logging.info("Failed to shutdown instance %s, not running", name)
642       return None
643
644     return self._RunXen(["shutdown", "-w", name], hvparams)
645
646   def _DestroyInstance(self, name, hvparams):
647     """Destroy an instance if the instance if the instance exists.
648
649     @type name: string
650     @param name: name of the instance to destroy
651     @type hvparams: dict of string
652     @param hvparams: hypervisor parameters of the instance
653
654     """
655     instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
656
657     if instance_info is None:
658       logging.info("Failed to destroy instance %s, does not exist", name)
659       return None
660
661     return self._RunXen(["destroy", name], hvparams)
662
663   def _StopInstance(self, name, force, hvparams):
664     """Stop an instance.
665
666     @type name: string
667     @param name: name of the instance to destroy
668
669     @type force: boolean
670     @param force: whether to do a "hard" stop (destroy)
671
672     @type hvparams: dict of string
673     @param hvparams: hypervisor parameters of the instance
674
675     """
676     if force:
677       result = self._DestroyInstance(name, hvparams)
678     else:
679       self._ShutdownInstance(name, hvparams)
680       result = self._DestroyInstance(name, hvparams)
681
682     if result is not None and result.failed and \
683           self.GetInstanceInfo(name, hvparams=hvparams) is not None:
684       raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
685                                    (name, result.fail_reason, result.output))
686
687     # Remove configuration file if stopping/starting instance was successful
688     self._RemoveConfigFile(name)
689
690   def RebootInstance(self, instance):
691     """Reboot an instance.
692
693     """
694     ini_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
695
696     if ini_info is None:
697       raise errors.HypervisorError("Failed to reboot instance %s,"
698                                    " not running" % instance.name)
699
700     result = self._RunXen(["reboot", instance.name], instance.hvparams)
701     if result.failed:
702       raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
703                                    (instance.name, result.fail_reason,
704                                     result.output))
705
706     def _CheckInstance():
707       new_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
708
709       # check if the domain ID has changed or the run time has decreased
710       if (new_info is not None and
711           (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])):
712         return
713
714       raise utils.RetryAgain()
715
716     try:
717       utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
718                   self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
719     except utils.RetryTimeout:
720       raise errors.HypervisorError("Failed to reboot instance %s: instance"
721                                    " did not reboot in the expected interval" %
722                                    (instance.name, ))
723
724   def BalloonInstanceMemory(self, instance, mem):
725     """Balloon an instance memory to a certain value.
726
727     @type instance: L{objects.Instance}
728     @param instance: instance to be accepted
729     @type mem: int
730     @param mem: actual memory size to use for instance runtime
731
732     """
733     result = self._RunXen(["mem-set", instance.name, mem], instance.hvparams)
734     if result.failed:
735       raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" %
736                                    (instance.name, result.fail_reason,
737                                     result.output))
738
739     # Update configuration file
740     cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem]
741     cmd.append(self._ConfigFileName(instance.name))
742
743     result = utils.RunCmd(cmd)
744     if result.failed:
745       raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" %
746                                    (instance.name, result.fail_reason,
747                                     result.output))
748
749   def GetNodeInfo(self, hvparams=None):
750     """Return information about the node.
751
752     @see: L{_GetNodeInfo} and L{_ParseNodeInfo}
753
754     """
755     result = self._RunXen(["info"], hvparams)
756     if result.failed:
757       logging.error("Can't retrieve xen hypervisor information (%s): %s",
758                     result.fail_reason, result.output)
759       return None
760
761     instance_list = self._GetInstanceList(True, hvparams)
762     return _GetNodeInfo(result.stdout, instance_list)
763
764   @classmethod
765   def GetInstanceConsole(cls, instance, primary_node, hvparams, beparams):
766     """Return a command for connecting to the console of an instance.
767
768     """
769     xen_cmd = XenHypervisor._GetCommandFromHvparams(hvparams)
770     return objects.InstanceConsole(instance=instance.name,
771                                    kind=constants.CONS_SSH,
772                                    host=primary_node.name,
773                                    user=constants.SSH_CONSOLE_USER,
774                                    command=[pathutils.XEN_CONSOLE_WRAPPER,
775                                             xen_cmd, instance.name])
776
777   def Verify(self, hvparams=None):
778     """Verify the hypervisor.
779
780     For Xen, this verifies that the xend process is running.
781
782     @type hvparams: dict of strings
783     @param hvparams: hypervisor parameters to be verified against
784
785     @return: Problem description if something is wrong, C{None} otherwise
786
787     """
788     if hvparams is None:
789       return "Could not verify the hypervisor, because no hvparams were" \
790              " provided."
791
792     if constants.HV_XEN_CMD in hvparams:
793       xen_cmd = hvparams[constants.HV_XEN_CMD]
794       try:
795         self._CheckToolstack(xen_cmd)
796       except errors.HypervisorError:
797         return "The configured xen toolstack '%s' is not available on this" \
798                " node." % xen_cmd
799
800     result = self._RunXen(["info"], hvparams)
801     if result.failed:
802       return "Retrieving information from xen failed: %s, %s" % \
803         (result.fail_reason, result.output)
804
805     return None
806
807   def MigrationInfo(self, instance):
808     """Get instance information to perform a migration.
809
810     @type instance: L{objects.Instance}
811     @param instance: instance to be migrated
812     @rtype: string
813     @return: content of the xen config file
814
815     """
816     return self._ReadConfigFile(instance.name)
817
818   def AcceptInstance(self, instance, info, target):
819     """Prepare to accept an instance.
820
821     @type instance: L{objects.Instance}
822     @param instance: instance to be accepted
823     @type info: string
824     @param info: content of the xen config file on the source node
825     @type target: string
826     @param target: target host (usually ip), on this node
827
828     """
829     pass
830
831   def FinalizeMigrationDst(self, instance, info, success):
832     """Finalize an instance migration.
833
834     After a successful migration we write the xen config file.
835     We do nothing on a failure, as we did not change anything at accept time.
836
837     @type instance: L{objects.Instance}
838     @param instance: instance whose migration is being finalized
839     @type info: string
840     @param info: content of the xen config file on the source node
841     @type success: boolean
842     @param success: whether the migration was a success or a failure
843
844     """
845     if success:
846       self._WriteConfigFile(instance.name, info)
847
848   def MigrateInstance(self, cluster_name, instance, target, live):
849     """Migrate an instance to a target node.
850
851     The migration will not be attempted if the instance is not
852     currently running.
853
854     @type instance: L{objects.Instance}
855     @param instance: the instance to be migrated
856     @type target: string
857     @param target: ip address of the target node
858     @type live: boolean
859     @param live: perform a live migration
860
861     """
862     port = instance.hvparams[constants.HV_MIGRATION_PORT]
863
864     return self._MigrateInstance(cluster_name, instance.name, target, port,
865                                  live, instance.hvparams)
866
867   def _MigrateInstance(self, cluster_name, instance_name, target, port, live,
868                        hvparams, _ping_fn=netutils.TcpPing):
869     """Migrate an instance to a target node.
870
871     @see: L{MigrateInstance} for details
872
873     """
874     if hvparams is None:
875       raise errors.HypervisorError("No hvparams provided.")
876
877     if self.GetInstanceInfo(instance_name, hvparams=hvparams) is None:
878       raise errors.HypervisorError("Instance not running, cannot migrate")
879
880     cmd = self._GetCommand(hvparams)
881
882     if (cmd == constants.XEN_CMD_XM and
883         not _ping_fn(target, port, live_port_needed=True)):
884       raise errors.HypervisorError("Remote host %s not listening on port"
885                                    " %s, cannot migrate" % (target, port))
886
887     args = ["migrate"]
888
889     if cmd == constants.XEN_CMD_XM:
890       args.extend(["-p", "%d" % port])
891       if live:
892         args.append("-l")
893
894     elif cmd == constants.XEN_CMD_XL:
895       args.extend([
896         "-s", constants.XL_SSH_CMD % cluster_name,
897         "-C", self._ConfigFileName(instance_name),
898         ])
899
900     else:
901       raise errors.HypervisorError("Unsupported Xen command: %s" % self._cmd)
902
903     args.extend([instance_name, target])
904
905     result = self._RunXen(args, hvparams)
906     if result.failed:
907       raise errors.HypervisorError("Failed to migrate instance %s: %s" %
908                                    (instance_name, result.output))
909
910   def FinalizeMigrationSource(self, instance, success, live):
911     """Finalize the instance migration on the source node.
912
913     @type instance: L{objects.Instance}
914     @param instance: the instance that was migrated
915     @type success: bool
916     @param success: whether the migration succeeded or not
917     @type live: bool
918     @param live: whether the user requested a live migration or not
919
920     """
921     # pylint: disable=W0613
922     if success:
923       # remove old xen file after migration succeeded
924       try:
925         self._RemoveConfigFile(instance.name)
926       except EnvironmentError:
927         logging.exception("Failure while removing instance config file")
928
929   def GetMigrationStatus(self, instance):
930     """Get the migration status
931
932     As MigrateInstance for Xen is still blocking, if this method is called it
933     means that MigrateInstance has completed successfully. So we can safely
934     assume that the migration was successful and notify this fact to the client.
935
936     @type instance: L{objects.Instance}
937     @param instance: the instance that is being migrated
938     @rtype: L{objects.MigrationStatus}
939     @return: the status of the current migration (one of
940              L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
941              progress info that can be retrieved from the hypervisor
942
943     """
944     return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
945
946   def PowercycleNode(self, hvparams=None):
947     """Xen-specific powercycle.
948
949     This first does a Linux reboot (which triggers automatically a Xen
950     reboot), and if that fails it tries to do a Xen reboot. The reason
951     we don't try a Xen reboot first is that the xen reboot launches an
952     external command which connects to the Xen hypervisor, and that
953     won't work in case the root filesystem is broken and/or the xend
954     daemon is not working.
955
956     @type hvparams: dict of strings
957     @param hvparams: hypervisor params to be used on this node
958
959     """
960     try:
961       self.LinuxPowercycle()
962     finally:
963       xen_cmd = self._GetCommand(hvparams)
964       utils.RunCmd([xen_cmd, "debug", "R"])
965
966   def _CheckToolstack(self, xen_cmd):
967     """Check whether the given toolstack is available on the node.
968
969     @type xen_cmd: string
970     @param xen_cmd: xen command (e.g. 'xm' or 'xl')
971
972     """
973     binary_found = self._CheckToolstackBinary(xen_cmd)
974     if not binary_found:
975       raise errors.HypervisorError("No '%s' binary found on node." % xen_cmd)
976     elif xen_cmd == constants.XEN_CMD_XL:
977       if not self._CheckToolstackXlConfigured():
978         raise errors.HypervisorError("Toolstack '%s' is not enabled on this"
979                                      "node." % xen_cmd)
980
981   def _CheckToolstackBinary(self, xen_cmd):
982     """Checks whether the xen command's binary is found on the machine.
983
984     """
985     if xen_cmd not in constants.KNOWN_XEN_COMMANDS:
986       raise errors.HypervisorError("Unknown xen command '%s'." % xen_cmd)
987     result = self._run_cmd_fn(["which", xen_cmd])
988     return not result.failed
989
990   def _CheckToolstackXlConfigured(self):
991     """Checks whether xl is enabled on an xl-capable node.
992
993     @rtype: bool
994     @returns: C{True} if 'xl' is enabled, C{False} otherwise
995
996     """
997     result = self._run_cmd_fn([constants.XEN_CMD_XL, "help"])
998     if not result.failed:
999       return True
1000     elif result.failed:
1001       if "toolstack" in result.stderr:
1002         return False
1003       # xl fails for some other reason than the toolstack
1004       else:
1005         raise errors.HypervisorError("Cannot run xen ('%s'). Error: %s."
1006                                      % (constants.XEN_CMD_XL, result.stderr))
1007
1008
1009 class XenPvmHypervisor(XenHypervisor):
1010   """Xen PVM hypervisor interface"""
1011
1012   PARAMETERS = {
1013     constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
1014     constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
1015     constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
1016     constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1017     constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
1018     constants.HV_ROOT_PATH: hv_base.NO_CHECK,
1019     constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
1020     constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1021     constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1022     # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
1023     constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1024     constants.HV_REBOOT_BEHAVIOR:
1025       hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1026     constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1027     constants.HV_CPU_CAP: hv_base.OPT_NONNEGATIVE_INT_CHECK,
1028     constants.HV_CPU_WEIGHT:
1029       (False, lambda x: 0 < x < 65536, "invalid weight", None, None),
1030     constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1031     constants.HV_XEN_CMD:
1032       hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1033     constants.HV_XEN_CPUID: hv_base.NO_CHECK,
1034     constants.HV_SOUNDHW: hv_base.NO_CHECK,
1035     }
1036
1037   def _GetConfig(self, instance, startup_memory, block_devices):
1038     """Write the Xen config file for the instance.
1039
1040     """
1041     hvp = instance.hvparams
1042     config = StringIO()
1043     config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
1044
1045     # if bootloader is True, use bootloader instead of kernel and ramdisk
1046     # parameters.
1047     if hvp[constants.HV_USE_BOOTLOADER]:
1048       # bootloader handling
1049       bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
1050       if bootloader_path:
1051         config.write("bootloader = '%s'\n" % bootloader_path)
1052       else:
1053         raise errors.HypervisorError("Bootloader enabled, but missing"
1054                                      " bootloader path")
1055
1056       bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
1057       if bootloader_args:
1058         config.write("bootargs = '%s'\n" % bootloader_args)
1059     else:
1060       # kernel handling
1061       kpath = hvp[constants.HV_KERNEL_PATH]
1062       config.write("kernel = '%s'\n" % kpath)
1063
1064       # initrd handling
1065       initrd_path = hvp[constants.HV_INITRD_PATH]
1066       if initrd_path:
1067         config.write("ramdisk = '%s'\n" % initrd_path)
1068
1069     # rest of the settings
1070     config.write("memory = %d\n" % startup_memory)
1071     config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1072     config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1073     cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1074     if cpu_pinning:
1075       config.write("%s\n" % cpu_pinning)
1076     cpu_cap = hvp[constants.HV_CPU_CAP]
1077     if cpu_cap:
1078       config.write("cpu_cap=%d\n" % cpu_cap)
1079     cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1080     if cpu_weight:
1081       config.write("cpu_weight=%d\n" % cpu_weight)
1082
1083     config.write("name = '%s'\n" % instance.name)
1084
1085     vif_data = []
1086     for idx, nic in enumerate(instance.nics):
1087       nic_str = "mac=%s" % (nic.mac)
1088       ip = getattr(nic, "ip", None)
1089       if ip is not None:
1090         nic_str += ", ip=%s" % ip
1091       if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1092         nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1093       if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_OVS:
1094         nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1095         if nic.nicparams[constants.NIC_VLAN]:
1096           nic_str += "%s" % nic.nicparams[constants.NIC_VLAN]
1097       if hvp[constants.HV_VIF_SCRIPT]:
1098         nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
1099       vif_data.append("'%s'" % nic_str)
1100       self._WriteNICInfoFile(instance, idx, nic)
1101
1102     disk_data = \
1103       _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1104
1105     config.write("vif = [%s]\n" % ",".join(vif_data))
1106     config.write("disk = [%s]\n" % ",".join(disk_data))
1107
1108     if hvp[constants.HV_ROOT_PATH]:
1109       config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
1110     config.write("on_poweroff = 'destroy'\n")
1111     if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
1112       config.write("on_reboot = 'restart'\n")
1113     else:
1114       config.write("on_reboot = 'destroy'\n")
1115     config.write("on_crash = 'restart'\n")
1116     config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
1117
1118     cpuid = hvp[constants.HV_XEN_CPUID]
1119     if cpuid:
1120       config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1121
1122     if hvp[constants.HV_SOUNDHW]:
1123       config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1124
1125     return config.getvalue()
1126
1127
1128 class XenHvmHypervisor(XenHypervisor):
1129   """Xen HVM hypervisor interface"""
1130
1131   ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
1132     pathutils.VNC_PASSWORD_FILE,
1133     ]
1134   ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [
1135     pathutils.VNC_PASSWORD_FILE,
1136     ]
1137
1138   PARAMETERS = {
1139     constants.HV_ACPI: hv_base.NO_CHECK,
1140     constants.HV_BOOT_ORDER: (True, ) +
1141       (lambda x: x and len(x.strip("acdn")) == 0,
1142        "Invalid boot order specified, must be one or more of [acdn]",
1143        None, None),
1144     constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
1145     constants.HV_DISK_TYPE:
1146       hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
1147     constants.HV_NIC_TYPE:
1148       hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
1149     constants.HV_PAE: hv_base.NO_CHECK,
1150     constants.HV_VNC_BIND_ADDRESS:
1151       (False, netutils.IP4Address.IsValid,
1152        "VNC bind address is not a valid IP address", None, None),
1153     constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1154     constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
1155     constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
1156     constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1157     constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1158     constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
1159     # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
1160     constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1161     # Add PCI passthrough
1162     constants.HV_PASSTHROUGH: hv_base.NO_CHECK,
1163     constants.HV_REBOOT_BEHAVIOR:
1164       hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1165     constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1166     constants.HV_CPU_CAP: hv_base.NO_CHECK,
1167     constants.HV_CPU_WEIGHT:
1168       (False, lambda x: 0 < x < 65535, "invalid weight", None, None),
1169     constants.HV_VIF_TYPE:
1170       hv_base.ParamInSet(False, constants.HT_HVM_VALID_VIF_TYPES),
1171     constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1172     constants.HV_VIRIDIAN: hv_base.NO_CHECK,
1173     constants.HV_XEN_CMD:
1174       hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1175     constants.HV_XEN_CPUID: hv_base.NO_CHECK,
1176     constants.HV_SOUNDHW: hv_base.NO_CHECK,
1177     }
1178
1179   def _GetConfig(self, instance, startup_memory, block_devices):
1180     """Create a Xen 3.1 HVM config file.
1181
1182     """
1183     hvp = instance.hvparams
1184
1185     config = StringIO()
1186
1187     # kernel handling
1188     kpath = hvp[constants.HV_KERNEL_PATH]
1189     config.write("kernel = '%s'\n" % kpath)
1190
1191     config.write("builder = 'hvm'\n")
1192     config.write("memory = %d\n" % startup_memory)
1193     config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1194     config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1195     cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1196     if cpu_pinning:
1197       config.write("%s\n" % cpu_pinning)
1198     cpu_cap = hvp[constants.HV_CPU_CAP]
1199     if cpu_cap:
1200       config.write("cpu_cap=%d\n" % cpu_cap)
1201     cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1202     if cpu_weight:
1203       config.write("cpu_weight=%d\n" % cpu_weight)
1204
1205     config.write("name = '%s'\n" % instance.name)
1206     if hvp[constants.HV_PAE]:
1207       config.write("pae = 1\n")
1208     else:
1209       config.write("pae = 0\n")
1210     if hvp[constants.HV_ACPI]:
1211       config.write("acpi = 1\n")
1212     else:
1213       config.write("acpi = 0\n")
1214     if hvp[constants.HV_VIRIDIAN]:
1215       config.write("viridian = 1\n")
1216     else:
1217       config.write("viridian = 0\n")
1218
1219     config.write("apic = 1\n")
1220     config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
1221     config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
1222     config.write("sdl = 0\n")
1223     config.write("usb = 1\n")
1224     config.write("usbdevice = 'tablet'\n")
1225     config.write("vnc = 1\n")
1226     if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
1227       config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
1228     else:
1229       config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
1230
1231     if instance.network_port > constants.VNC_BASE_PORT:
1232       display = instance.network_port - constants.VNC_BASE_PORT
1233       config.write("vncdisplay = %s\n" % display)
1234       config.write("vncunused = 0\n")
1235     else:
1236       config.write("# vncdisplay = 1\n")
1237       config.write("vncunused = 1\n")
1238
1239     vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
1240     try:
1241       password = utils.ReadFile(vnc_pwd_file)
1242     except EnvironmentError, err:
1243       raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
1244                                    (vnc_pwd_file, err))
1245
1246     config.write("vncpasswd = '%s'\n" % password.rstrip())
1247
1248     config.write("serial = 'pty'\n")
1249     if hvp[constants.HV_USE_LOCALTIME]:
1250       config.write("localtime = 1\n")
1251
1252     vif_data = []
1253     # Note: what is called 'nic_type' here, is used as value for the xen nic
1254     # vif config parameter 'model'. For the xen nic vif parameter 'type', we use
1255     # the 'vif_type' to avoid a clash of notation.
1256     nic_type = hvp[constants.HV_NIC_TYPE]
1257
1258     if nic_type is None:
1259       vif_type_str = ""
1260       if hvp[constants.HV_VIF_TYPE]:
1261         vif_type_str = ", type=%s" % hvp[constants.HV_VIF_TYPE]
1262       # ensure old instances don't change
1263       nic_type_str = vif_type_str
1264     elif nic_type == constants.HT_NIC_PARAVIRTUAL:
1265       nic_type_str = ", type=paravirtualized"
1266     else:
1267       # parameter 'model' is only valid with type 'ioemu'
1268       nic_type_str = ", model=%s, type=%s" % \
1269         (nic_type, constants.HT_HVM_VIF_IOEMU)
1270     for idx, nic in enumerate(instance.nics):
1271       nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
1272       ip = getattr(nic, "ip", None)
1273       if ip is not None:
1274         nic_str += ", ip=%s" % ip
1275       if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1276         nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1277       if hvp[constants.HV_VIF_SCRIPT]:
1278         nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
1279       vif_data.append("'%s'" % nic_str)
1280       self._WriteNICInfoFile(instance, idx, nic)
1281
1282     config.write("vif = [%s]\n" % ",".join(vif_data))
1283
1284     disk_data = \
1285       _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1286
1287     iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
1288     if iso_path:
1289       iso = "'file:%s,hdc:cdrom,r'" % iso_path
1290       disk_data.append(iso)
1291
1292     config.write("disk = [%s]\n" % (",".join(disk_data)))
1293     # Add PCI passthrough
1294     pci_pass_arr = []
1295     pci_pass = hvp[constants.HV_PASSTHROUGH]
1296     if pci_pass:
1297       pci_pass_arr = pci_pass.split(";")
1298       config.write("pci = %s\n" % pci_pass_arr)
1299     config.write("on_poweroff = 'destroy'\n")
1300     if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
1301       config.write("on_reboot = 'restart'\n")
1302     else:
1303       config.write("on_reboot = 'destroy'\n")
1304     config.write("on_crash = 'restart'\n")
1305
1306     cpuid = hvp[constants.HV_XEN_CPUID]
1307     if cpuid:
1308       config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1309
1310     if hvp[constants.HV_SOUNDHW]:
1311       config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1312
1313     return config.getvalue()