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