Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ 990ade2d

History | View | Annotate | Download (22.9 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008, 2009, 2010 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""Xen hypervisors
23

24
"""
25

    
26
import logging
27
from cStringIO import StringIO
28

    
29
from ganeti import constants
30
from ganeti import errors
31
from ganeti import utils
32
from ganeti.hypervisor import hv_base
33
from ganeti import netutils
34
from ganeti import objects
35

    
36

    
37
class XenHypervisor(hv_base.BaseHypervisor):
38
  """Xen generic hypervisor interface
39

40
  This is the Xen base class used for both Xen PVM and HVM. It contains
41
  all the functionality that is identical for both.
42

43
  """
44
  CAN_MIGRATE = True
45
  REBOOT_RETRY_COUNT = 60
46
  REBOOT_RETRY_INTERVAL = 10
47

    
48
  ANCILLARY_FILES = [
49
    "/etc/xen/xend-config.sxp",
50
    "/etc/xen/scripts/vif-bridge",
51
    ]
52

    
53
  @classmethod
54
  def _WriteConfigFile(cls, instance, block_devices):
55
    """Write the Xen config file for the instance.
56

57
    """
58
    raise NotImplementedError
59

    
60
  @staticmethod
61
  def _WriteConfigFileStatic(instance_name, data):
62
    """Write the Xen config file for the instance.
63

64
    This version of the function just writes the config file from static data.
65

66
    """
67
    utils.WriteFile("/etc/xen/%s" % instance_name, data=data)
68

    
69
  @staticmethod
70
  def _ReadConfigFile(instance_name):
71
    """Returns the contents of the instance config file.
72

73
    """
74
    try:
75
      file_content = utils.ReadFile("/etc/xen/%s" % instance_name)
76
    except EnvironmentError, err:
77
      raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
78
    return file_content
79

    
80
  @staticmethod
81
  def _RemoveConfigFile(instance_name):
82
    """Remove the xen configuration file.
83

84
    """
85
    utils.RemoveFile("/etc/xen/%s" % instance_name)
86

    
87
  @staticmethod
88
  def _RunXmList(xmlist_errors):
89
    """Helper function for L{_GetXMList} to run "xm list".
90

91
    """
92
    result = utils.RunCmd(["xm", "list"])
93
    if result.failed:
94
      logging.error("xm list failed (%s): %s", result.fail_reason,
95
                    result.output)
96
      xmlist_errors.append(result)
97
      raise utils.RetryAgain()
98

    
99
    # skip over the heading
100
    return result.stdout.splitlines()[1:]
101

    
102
  @classmethod
103
  def _GetXMList(cls, include_node):
104
    """Return the list of running instances.
105

106
    If the include_node argument is True, then we return information
107
    for dom0 also, otherwise we filter that from the return value.
108

109
    @return: list of (name, id, memory, vcpus, state, time spent)
110

111
    """
112
    xmlist_errors = []
113
    try:
114
      lines = utils.Retry(cls._RunXmList, 1, 5, args=(xmlist_errors, ))
115
    except utils.RetryTimeout:
116
      if xmlist_errors:
117
        xmlist_result = xmlist_errors.pop()
118

    
119
        errmsg = ("xm list failed, timeout exceeded (%s): %s" %
120
                  (xmlist_result.fail_reason, xmlist_result.output))
121
      else:
122
        errmsg = "xm list failed"
123

    
124
      raise errors.HypervisorError(errmsg)
125

    
126
    result = []
127
    for line in lines:
128
      # The format of lines is:
129
      # Name      ID Mem(MiB) VCPUs State  Time(s)
130
      # Domain-0   0  3418     4 r-----    266.2
131
      data = line.split()
132
      if len(data) != 6:
133
        raise errors.HypervisorError("Can't parse output of xm list,"
134
                                     " line: %s" % line)
135
      try:
136
        data[1] = int(data[1])
137
        data[2] = int(data[2])
138
        data[3] = int(data[3])
139
        data[5] = float(data[5])
140
      except (TypeError, ValueError), err:
141
        raise errors.HypervisorError("Can't parse output of xm list,"
142
                                     " line: %s, error: %s" % (line, err))
143

    
144
      # skip the Domain-0 (optional)
145
      if include_node or data[0] != "Domain-0":
146
        result.append(data)
147

    
148
    return result
149

    
150
  def ListInstances(self):
151
    """Get the list of running instances.
152

153
    """
154
    xm_list = self._GetXMList(False)
155
    names = [info[0] for info in xm_list]
156
    return names
157

    
158
  def GetInstanceInfo(self, instance_name):
159
    """Get instance properties.
160

161
    @param instance_name: the instance name
162

163
    @return: tuple (name, id, memory, vcpus, stat, times)
164

165
    """
166
    xm_list = self._GetXMList(instance_name=="Domain-0")
167
    result = None
168
    for data in xm_list:
169
      if data[0] == instance_name:
170
        result = data
171
        break
172
    return result
173

    
174
  def GetAllInstancesInfo(self):
175
    """Get properties of all instances.
176

177
    @return: list of tuples (name, id, memory, vcpus, stat, times)
178

179
    """
180
    xm_list = self._GetXMList(False)
181
    return xm_list
182

    
183
  def StartInstance(self, instance, block_devices, startup_paused):
184
    """Start an instance.
185

186
    """
187
    self._WriteConfigFile(instance, block_devices)
188
    cmd = ["xm", "create"]
189
    if startup_paused:
190
      cmd.extend(["--paused"])
191
    cmd.extend([instance.name])
192
    result = utils.RunCmd(cmd)
193

    
194
    if result.failed:
195
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
196
                                   (instance.name, result.fail_reason,
197
                                    result.output))
198

    
199
  def StopInstance(self, instance, force=False, retry=False, name=None):
200
    """Stop an instance.
201

202
    """
203
    if name is None:
204
      name = instance.name
205
    self._RemoveConfigFile(name)
206
    if force:
207
      command = ["xm", "destroy", name]
208
    else:
209
      command = ["xm", "shutdown", name]
210
    result = utils.RunCmd(command)
211

    
212
    if result.failed:
213
      raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
214
                                   (name, result.fail_reason, result.output))
215

    
216
  def RebootInstance(self, instance):
217
    """Reboot an instance.
218

219
    """
220
    ini_info = self.GetInstanceInfo(instance.name)
221

    
222
    if ini_info is None:
223
      raise errors.HypervisorError("Failed to reboot instance %s,"
224
                                   " not running" % instance.name)
225

    
226
    result = utils.RunCmd(["xm", "reboot", instance.name])
227
    if result.failed:
228
      raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
229
                                   (instance.name, result.fail_reason,
230
                                    result.output))
231

    
232
    def _CheckInstance():
233
      new_info = self.GetInstanceInfo(instance.name)
234

    
235
      # check if the domain ID has changed or the run time has decreased
236
      if (new_info is not None and
237
          (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])):
238
        return
239

    
240
      raise utils.RetryAgain()
241

    
242
    try:
243
      utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
244
                  self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
245
    except utils.RetryTimeout:
246
      raise errors.HypervisorError("Failed to reboot instance %s: instance"
247
                                   " did not reboot in the expected interval" %
248
                                   (instance.name, ))
249

    
250
  def GetNodeInfo(self):
251
    """Return information about the node.
252

253
    @return: a dict with the following keys (memory values in MiB):
254
          - memory_total: the total memory size on the node
255
          - memory_free: the available memory on the node for instances
256
          - memory_dom0: the memory used by the node itself, if available
257
          - nr_cpus: total number of CPUs
258
          - nr_nodes: in a NUMA system, the number of domains
259
          - nr_sockets: the number of physical CPU sockets in the node
260

261
    """
262
    # note: in xen 3, memory has changed to total_memory
263
    result = utils.RunCmd(["xm", "info"])
264
    if result.failed:
265
      logging.error("Can't run 'xm info' (%s): %s", result.fail_reason,
266
                    result.output)
267
      return None
268

    
269
    xmoutput = result.stdout.splitlines()
270
    result = {}
271
    cores_per_socket = threads_per_core = nr_cpus = None
272
    for line in xmoutput:
273
      splitfields = line.split(":", 1)
274

    
275
      if len(splitfields) > 1:
276
        key = splitfields[0].strip()
277
        val = splitfields[1].strip()
278
        if key == "memory" or key == "total_memory":
279
          result["memory_total"] = int(val)
280
        elif key == "free_memory":
281
          result["memory_free"] = int(val)
282
        elif key == "nr_cpus":
283
          nr_cpus = result["cpu_total"] = int(val)
284
        elif key == "nr_nodes":
285
          result["cpu_nodes"] = int(val)
286
        elif key == "cores_per_socket":
287
          cores_per_socket = int(val)
288
        elif key == "threads_per_core":
289
          threads_per_core = int(val)
290

    
291
    if (cores_per_socket is not None and
292
        threads_per_core is not None and nr_cpus is not None):
293
      result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
294

    
295
    dom0_info = self.GetInstanceInfo("Domain-0")
296
    if dom0_info is not None:
297
      result["memory_dom0"] = dom0_info[2]
298

    
299
    return result
300

    
301
  @classmethod
302
  def GetInstanceConsole(cls, instance, hvparams, beparams):
303
    """Return a command for connecting to the console of an instance.
304

305
    """
306
    return objects.InstanceConsole(instance=instance.name,
307
                                   kind=constants.CONS_SSH,
308
                                   host=instance.primary_node,
309
                                   user=constants.GANETI_RUNAS,
310
                                   command=[constants.XM_CONSOLE_WRAPPER,
311
                                            instance.name])
312

    
313
  def Verify(self):
314
    """Verify the hypervisor.
315

316
    For Xen, this verifies that the xend process is running.
317

318
    """
319
    result = utils.RunCmd(["xm", "info"])
320
    if result.failed:
321
      return "'xm info' failed: %s, %s" % (result.fail_reason, result.output)
322

    
323
  @staticmethod
324
  def _GetConfigFileDiskData(block_devices, blockdev_prefix):
325
    """Get disk directive for xen config file.
326

327
    This method builds the xen config disk directive according to the
328
    given disk_template and block_devices.
329

330
    @param block_devices: list of tuples (cfdev, rldev):
331
        - cfdev: dict containing ganeti config disk part
332
        - rldev: ganeti.bdev.BlockDev object
333
    @param blockdev_prefix: a string containing blockdevice prefix,
334
                            e.g. "sd" for /dev/sda
335

336
    @return: string containing disk directive for xen instance config file
337

338
    """
339
    FILE_DRIVER_MAP = {
340
      constants.FD_LOOP: "file",
341
      constants.FD_BLKTAP: "tap:aio",
342
      }
343
    disk_data = []
344
    if len(block_devices) > 24:
345
      # 'z' - 'a' = 24
346
      raise errors.HypervisorError("Too many disks")
347
    namespace = [blockdev_prefix + chr(i + ord("a")) for i in range(24)]
348
    for sd_name, (cfdev, dev_path) in zip(namespace, block_devices):
349
      if cfdev.mode == constants.DISK_RDWR:
350
        mode = "w"
351
      else:
352
        mode = "r"
353
      if cfdev.dev_type == constants.LD_FILE:
354
        line = "'%s:%s,%s,%s'" % (FILE_DRIVER_MAP[cfdev.physical_id[0]],
355
                                  dev_path, sd_name, mode)
356
      else:
357
        line = "'phy:%s,%s,%s'" % (dev_path, sd_name, mode)
358
      disk_data.append(line)
359

    
360
    return disk_data
361

    
362
  def MigrationInfo(self, instance):
363
    """Get instance information to perform a migration.
364

365
    @type instance: L{objects.Instance}
366
    @param instance: instance to be migrated
367
    @rtype: string
368
    @return: content of the xen config file
369

370
    """
371
    return self._ReadConfigFile(instance.name)
372

    
373
  def AcceptInstance(self, instance, info, target):
374
    """Prepare to accept an instance.
375

376
    @type instance: L{objects.Instance}
377
    @param instance: instance to be accepted
378
    @type info: string
379
    @param info: content of the xen config file on the source node
380
    @type target: string
381
    @param target: target host (usually ip), on this node
382

383
    """
384
    pass
385

    
386
  def FinalizeMigration(self, instance, info, success):
387
    """Finalize an instance migration.
388

389
    After a successful migration we write the xen config file.
390
    We do nothing on a failure, as we did not change anything at accept time.
391

392
    @type instance: L{objects.Instance}
393
    @param instance: instance whose migration is being finalized
394
    @type info: string
395
    @param info: content of the xen config file on the source node
396
    @type success: boolean
397
    @param success: whether the migration was a success or a failure
398

399
    """
400
    if success:
401
      self._WriteConfigFileStatic(instance.name, info)
402

    
403
  def MigrateInstance(self, instance, target, live):
404
    """Migrate an instance to a target node.
405

406
    The migration will not be attempted if the instance is not
407
    currently running.
408

409
    @type instance: L{objects.Instance}
410
    @param instance: the instance to be migrated
411
    @type target: string
412
    @param target: ip address of the target node
413
    @type live: boolean
414
    @param live: perform a live migration
415

416
    """
417
    if self.GetInstanceInfo(instance.name) is None:
418
      raise errors.HypervisorError("Instance not running, cannot migrate")
419

    
420
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
421

    
422
    if not netutils.TcpPing(target, port, live_port_needed=True):
423
      raise errors.HypervisorError("Remote host %s not listening on port"
424
                                   " %s, cannot migrate" % (target, port))
425

    
426
    args = ["xm", "migrate", "-p", "%d" % port]
427
    if live:
428
      args.append("-l")
429
    args.extend([instance.name, target])
430
    result = utils.RunCmd(args)
431
    if result.failed:
432
      raise errors.HypervisorError("Failed to migrate instance %s: %s" %
433
                                   (instance.name, result.output))
434
    # remove old xen file after migration succeeded
435
    try:
436
      self._RemoveConfigFile(instance.name)
437
    except EnvironmentError:
438
      logging.exception("Failure while removing instance config file")
439

    
440
  @classmethod
441
  def PowercycleNode(cls):
442
    """Xen-specific powercycle.
443

444
    This first does a Linux reboot (which triggers automatically a Xen
445
    reboot), and if that fails it tries to do a Xen reboot. The reason
446
    we don't try a Xen reboot first is that the xen reboot launches an
447
    external command which connects to the Xen hypervisor, and that
448
    won't work in case the root filesystem is broken and/or the xend
449
    daemon is not working.
450

451
    """
452
    try:
453
      cls.LinuxPowercycle()
454
    finally:
455
      utils.RunCmd(["xm", "debug", "R"])
456

    
457

    
458
class XenPvmHypervisor(XenHypervisor):
459
  """Xen PVM hypervisor interface"""
460

    
461
  PARAMETERS = {
462
    constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
463
    constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
464
    constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
465
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
466
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
467
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
468
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
469
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
470
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
471
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
472
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
473
    constants.HV_REBOOT_BEHAVIOR:
474
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS)
475
    }
476

    
477
  @classmethod
478
  def _WriteConfigFile(cls, instance, block_devices):
479
    """Write the Xen config file for the instance.
480

481
    """
482
    hvp = instance.hvparams
483
    config = StringIO()
484
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
485

    
486
    # if bootloader is True, use bootloader instead of kernel and ramdisk
487
    # parameters.
488
    if hvp[constants.HV_USE_BOOTLOADER]:
489
      # bootloader handling
490
      bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
491
      if bootloader_path:
492
        config.write("bootloader = '%s'\n" % bootloader_path)
493
      else:
494
        raise errors.HypervisorError("Bootloader enabled, but missing"
495
                                     " bootloader path")
496

    
497
      bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
498
      if bootloader_args:
499
        config.write("bootargs = '%s'\n" % bootloader_args)
500
    else:
501
      # kernel handling
502
      kpath = hvp[constants.HV_KERNEL_PATH]
503
      config.write("kernel = '%s'\n" % kpath)
504

    
505
      # initrd handling
506
      initrd_path = hvp[constants.HV_INITRD_PATH]
507
      if initrd_path:
508
        config.write("ramdisk = '%s'\n" % initrd_path)
509

    
510
    # rest of the settings
511
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
512
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
513
    config.write("name = '%s'\n" % instance.name)
514

    
515
    vif_data = []
516
    for nic in instance.nics:
517
      nic_str = "mac=%s" % (nic.mac)
518
      ip = getattr(nic, "ip", None)
519
      if ip is not None:
520
        nic_str += ", ip=%s" % ip
521
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
522
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
523
      vif_data.append("'%s'" % nic_str)
524

    
525
    disk_data = cls._GetConfigFileDiskData(block_devices,
526
                                           hvp[constants.HV_BLOCKDEV_PREFIX])
527

    
528
    config.write("vif = [%s]\n" % ",".join(vif_data))
529
    config.write("disk = [%s]\n" % ",".join(disk_data))
530

    
531
    if hvp[constants.HV_ROOT_PATH]:
532
      config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
533
    config.write("on_poweroff = 'destroy'\n")
534
    if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
535
      config.write("on_reboot = 'restart'\n")
536
    else:
537
      config.write("on_reboot = 'destroy'\n")
538
    config.write("on_crash = 'restart'\n")
539
    config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
540
    # just in case it exists
541
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
542
    try:
543
      utils.WriteFile("/etc/xen/%s" % instance.name, data=config.getvalue())
544
    except EnvironmentError, err:
545
      raise errors.HypervisorError("Cannot write Xen instance confile"
546
                                   " file /etc/xen/%s: %s" %
547
                                   (instance.name, err))
548

    
549
    return True
550

    
551

    
552
class XenHvmHypervisor(XenHypervisor):
553
  """Xen HVM hypervisor interface"""
554

    
555
  ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
556
    constants.VNC_PASSWORD_FILE,
557
    ]
558

    
559
  PARAMETERS = {
560
    constants.HV_ACPI: hv_base.NO_CHECK,
561
    constants.HV_BOOT_ORDER: (True, ) +
562
      (lambda x: x and len(x.strip("acdn")) == 0,
563
       "Invalid boot order specified, must be one or more of [acdn]",
564
       None, None),
565
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
566
    constants.HV_DISK_TYPE:
567
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
568
    constants.HV_NIC_TYPE:
569
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
570
    constants.HV_PAE: hv_base.NO_CHECK,
571
    constants.HV_VNC_BIND_ADDRESS:
572
      (False, netutils.IP4Address.IsValid,
573
       "VNC bind address is not a valid IP address", None, None),
574
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
575
    constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
576
    constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
577
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
578
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
579
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
580
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
581
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
582
    constants.HV_REBOOT_BEHAVIOR:
583
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS)
584
    }
585

    
586
  @classmethod
587
  def _WriteConfigFile(cls, instance, block_devices):
588
    """Create a Xen 3.1 HVM config file.
589

590
    """
591
    hvp = instance.hvparams
592

    
593
    config = StringIO()
594
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
595

    
596
    # kernel handling
597
    kpath = hvp[constants.HV_KERNEL_PATH]
598
    config.write("kernel = '%s'\n" % kpath)
599

    
600
    config.write("builder = 'hvm'\n")
601
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
602
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
603
    config.write("name = '%s'\n" % instance.name)
604
    if hvp[constants.HV_PAE]:
605
      config.write("pae = 1\n")
606
    else:
607
      config.write("pae = 0\n")
608
    if hvp[constants.HV_ACPI]:
609
      config.write("acpi = 1\n")
610
    else:
611
      config.write("acpi = 0\n")
612
    config.write("apic = 1\n")
613
    config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
614
    config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
615
    config.write("sdl = 0\n")
616
    config.write("usb = 1\n")
617
    config.write("usbdevice = 'tablet'\n")
618
    config.write("vnc = 1\n")
619
    if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
620
      config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
621
    else:
622
      config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
623

    
624
    if instance.network_port > constants.VNC_BASE_PORT:
625
      display = instance.network_port - constants.VNC_BASE_PORT
626
      config.write("vncdisplay = %s\n" % display)
627
      config.write("vncunused = 0\n")
628
    else:
629
      config.write("# vncdisplay = 1\n")
630
      config.write("vncunused = 1\n")
631

    
632
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
633
    try:
634
      password = utils.ReadFile(vnc_pwd_file)
635
    except EnvironmentError, err:
636
      raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
637
                                   (vnc_pwd_file, err))
638

    
639
    config.write("vncpasswd = '%s'\n" % password.rstrip())
640

    
641
    config.write("serial = 'pty'\n")
642
    if hvp[constants.HV_USE_LOCALTIME]:
643
      config.write("localtime = 1\n")
644

    
645
    vif_data = []
646
    nic_type = hvp[constants.HV_NIC_TYPE]
647
    if nic_type is None:
648
      # ensure old instances don't change
649
      nic_type_str = ", type=ioemu"
650
    elif nic_type == constants.HT_NIC_PARAVIRTUAL:
651
      nic_type_str = ", type=paravirtualized"
652
    else:
653
      nic_type_str = ", model=%s, type=ioemu" % nic_type
654
    for nic in instance.nics:
655
      nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
656
      ip = getattr(nic, "ip", None)
657
      if ip is not None:
658
        nic_str += ", ip=%s" % ip
659
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
660
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
661
      vif_data.append("'%s'" % nic_str)
662

    
663
    config.write("vif = [%s]\n" % ",".join(vif_data))
664

    
665
    disk_data = cls._GetConfigFileDiskData(block_devices,
666
                                           hvp[constants.HV_BLOCKDEV_PREFIX])
667

    
668
    iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
669
    if iso_path:
670
      iso = "'file:%s,hdc:cdrom,r'" % iso_path
671
      disk_data.append(iso)
672

    
673
    config.write("disk = [%s]\n" % (",".join(disk_data)))
674

    
675
    config.write("on_poweroff = 'destroy'\n")
676
    if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
677
      config.write("on_reboot = 'restart'\n")
678
    else:
679
      config.write("on_reboot = 'destroy'\n")
680
    config.write("on_crash = 'restart'\n")
681
    # just in case it exists
682
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
683
    try:
684
      utils.WriteFile("/etc/xen/%s" % instance.name,
685
                      data=config.getvalue())
686
    except EnvironmentError, err:
687
      raise errors.HypervisorError("Cannot write Xen instance confile"
688
                                   " file /etc/xen/%s: %s" %
689
                                   (instance.name, err))
690

    
691
    return True