Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ 2876c2d6

History | View | Annotate | Download (23.4 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([constants.XEN_CMD, "list"])
93
    if result.failed:
94
      logging.error("xm list failed (%s): %s", result.fail_reason,
95
                    result.output)
96
      xmlist_errors.append(result)
97
      raise utils.RetryAgain()
98

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

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

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

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

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

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

    
124
      raise errors.HypervisorError(errmsg)
125

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

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

    
148
    return result
149

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

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

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

161
    @param instance_name: the instance name
162

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
240
      raise utils.RetryAgain()
241

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

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

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

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

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

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

    
297
    if (cores_per_socket is not None and
298
        threads_per_core is not None and nr_cpus is not None):
299
      result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
300

    
301
    dom0_info = self.GetInstanceInfo("Domain-0")
302
    if dom0_info is not None:
303
      result["memory_dom0"] = dom0_info[2]
304

    
305
    if not (xen_major is None or xen_minor is None):
306
      result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
307

    
308
    return result
309

    
310
  @classmethod
311
  def GetInstanceConsole(cls, instance, hvparams, beparams):
312
    """Return a command for connecting to the console of an instance.
313

314
    """
315
    return objects.InstanceConsole(instance=instance.name,
316
                                   kind=constants.CONS_SSH,
317
                                   host=instance.primary_node,
318
                                   user=constants.GANETI_RUNAS,
319
                                   command=[constants.XM_CONSOLE_WRAPPER,
320
                                            instance.name])
321

    
322
  def Verify(self):
323
    """Verify the hypervisor.
324

325
    For Xen, this verifies that the xend process is running.
326

327
    """
328
    result = utils.RunCmd([constants.XEN_CMD, "info"])
329
    if result.failed:
330
      return "'xm info' failed: %s, %s" % (result.fail_reason, result.output)
331

    
332
  @staticmethod
333
  def _GetConfigFileDiskData(block_devices, blockdev_prefix):
334
    """Get disk directive for xen config file.
335

336
    This method builds the xen config disk directive according to the
337
    given disk_template and block_devices.
338

339
    @param block_devices: list of tuples (cfdev, rldev):
340
        - cfdev: dict containing ganeti config disk part
341
        - rldev: ganeti.bdev.BlockDev object
342
    @param blockdev_prefix: a string containing blockdevice prefix,
343
                            e.g. "sd" for /dev/sda
344

345
    @return: string containing disk directive for xen instance config file
346

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

    
369
    return disk_data
370

    
371
  def MigrationInfo(self, instance):
372
    """Get instance information to perform a migration.
373

374
    @type instance: L{objects.Instance}
375
    @param instance: instance to be migrated
376
    @rtype: string
377
    @return: content of the xen config file
378

379
    """
380
    return self._ReadConfigFile(instance.name)
381

    
382
  def AcceptInstance(self, instance, info, target):
383
    """Prepare to accept an instance.
384

385
    @type instance: L{objects.Instance}
386
    @param instance: instance to be accepted
387
    @type info: string
388
    @param info: content of the xen config file on the source node
389
    @type target: string
390
    @param target: target host (usually ip), on this node
391

392
    """
393
    pass
394

    
395
  def FinalizeMigration(self, instance, info, success):
396
    """Finalize an instance migration.
397

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

401
    @type instance: L{objects.Instance}
402
    @param instance: instance whose migration is being finalized
403
    @type info: string
404
    @param info: content of the xen config file on the source node
405
    @type success: boolean
406
    @param success: whether the migration was a success or a failure
407

408
    """
409
    if success:
410
      self._WriteConfigFileStatic(instance.name, info)
411

    
412
  def MigrateInstance(self, instance, target, live):
413
    """Migrate an instance to a target node.
414

415
    The migration will not be attempted if the instance is not
416
    currently running.
417

418
    @type instance: L{objects.Instance}
419
    @param instance: the instance to be migrated
420
    @type target: string
421
    @param target: ip address of the target node
422
    @type live: boolean
423
    @param live: perform a live migration
424

425
    """
426
    if self.GetInstanceInfo(instance.name) is None:
427
      raise errors.HypervisorError("Instance not running, cannot migrate")
428

    
429
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
430

    
431
    if not netutils.TcpPing(target, port, live_port_needed=True):
432
      raise errors.HypervisorError("Remote host %s not listening on port"
433
                                   " %s, cannot migrate" % (target, port))
434

    
435
    args = [constants.XEN_CMD, "migrate", "-p", "%d" % port]
436
    if live:
437
      args.append("-l")
438
    args.extend([instance.name, target])
439
    result = utils.RunCmd(args)
440
    if result.failed:
441
      raise errors.HypervisorError("Failed to migrate instance %s: %s" %
442
                                   (instance.name, result.output))
443
    # remove old xen file after migration succeeded
444
    try:
445
      self._RemoveConfigFile(instance.name)
446
    except EnvironmentError:
447
      logging.exception("Failure while removing instance config file")
448

    
449
  @classmethod
450
  def PowercycleNode(cls):
451
    """Xen-specific powercycle.
452

453
    This first does a Linux reboot (which triggers automatically a Xen
454
    reboot), and if that fails it tries to do a Xen reboot. The reason
455
    we don't try a Xen reboot first is that the xen reboot launches an
456
    external command which connects to the Xen hypervisor, and that
457
    won't work in case the root filesystem is broken and/or the xend
458
    daemon is not working.
459

460
    """
461
    try:
462
      cls.LinuxPowercycle()
463
    finally:
464
      utils.RunCmd([constants.XEN_CMD, "debug", "R"])
465

    
466

    
467
class XenPvmHypervisor(XenHypervisor):
468
  """Xen PVM hypervisor interface"""
469

    
470
  PARAMETERS = {
471
    constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
472
    constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
473
    constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
474
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
475
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
476
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
477
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
478
    constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
479
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
480
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
481
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
482
    constants.HV_REBOOT_BEHAVIOR:
483
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS)
484
    }
485

    
486
  @classmethod
487
  def _WriteConfigFile(cls, instance, block_devices):
488
    """Write the Xen config file for the instance.
489

490
    """
491
    hvp = instance.hvparams
492
    config = StringIO()
493
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
494

    
495
    # if bootloader is True, use bootloader instead of kernel and ramdisk
496
    # parameters.
497
    if hvp[constants.HV_USE_BOOTLOADER]:
498
      # bootloader handling
499
      bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
500
      if bootloader_path:
501
        config.write("bootloader = '%s'\n" % bootloader_path)
502
      else:
503
        raise errors.HypervisorError("Bootloader enabled, but missing"
504
                                     " bootloader path")
505

    
506
      bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
507
      if bootloader_args:
508
        config.write("bootargs = '%s'\n" % bootloader_args)
509
    else:
510
      # kernel handling
511
      kpath = hvp[constants.HV_KERNEL_PATH]
512
      config.write("kernel = '%s'\n" % kpath)
513

    
514
      # initrd handling
515
      initrd_path = hvp[constants.HV_INITRD_PATH]
516
      if initrd_path:
517
        config.write("ramdisk = '%s'\n" % initrd_path)
518

    
519
    # rest of the settings
520
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
521
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
522
    config.write("name = '%s'\n" % instance.name)
523

    
524
    vif_data = []
525
    for nic in instance.nics:
526
      nic_str = "mac=%s" % (nic.mac)
527
      ip = getattr(nic, "ip", None)
528
      if ip is not None:
529
        nic_str += ", ip=%s" % ip
530
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
531
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
532
      vif_data.append("'%s'" % nic_str)
533

    
534
    disk_data = cls._GetConfigFileDiskData(block_devices,
535
                                           hvp[constants.HV_BLOCKDEV_PREFIX])
536

    
537
    config.write("vif = [%s]\n" % ",".join(vif_data))
538
    config.write("disk = [%s]\n" % ",".join(disk_data))
539

    
540
    if hvp[constants.HV_ROOT_PATH]:
541
      config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
542
    config.write("on_poweroff = 'destroy'\n")
543
    if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
544
      config.write("on_reboot = 'restart'\n")
545
    else:
546
      config.write("on_reboot = 'destroy'\n")
547
    config.write("on_crash = 'restart'\n")
548
    config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
549
    # just in case it exists
550
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
551
    try:
552
      utils.WriteFile("/etc/xen/%s" % instance.name, data=config.getvalue())
553
    except EnvironmentError, err:
554
      raise errors.HypervisorError("Cannot write Xen instance confile"
555
                                   " file /etc/xen/%s: %s" %
556
                                   (instance.name, err))
557

    
558
    return True
559

    
560

    
561
class XenHvmHypervisor(XenHypervisor):
562
  """Xen HVM hypervisor interface"""
563

    
564
  ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
565
    constants.VNC_PASSWORD_FILE,
566
    ]
567

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

    
595
  @classmethod
596
  def _WriteConfigFile(cls, instance, block_devices):
597
    """Create a Xen 3.1 HVM config file.
598

599
    """
600
    hvp = instance.hvparams
601

    
602
    config = StringIO()
603
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
604

    
605
    # kernel handling
606
    kpath = hvp[constants.HV_KERNEL_PATH]
607
    config.write("kernel = '%s'\n" % kpath)
608

    
609
    config.write("builder = 'hvm'\n")
610
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
611
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
612
    config.write("name = '%s'\n" % instance.name)
613
    if hvp[constants.HV_PAE]:
614
      config.write("pae = 1\n")
615
    else:
616
      config.write("pae = 0\n")
617
    if hvp[constants.HV_ACPI]:
618
      config.write("acpi = 1\n")
619
    else:
620
      config.write("acpi = 0\n")
621
    config.write("apic = 1\n")
622
    config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
623
    config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
624
    config.write("sdl = 0\n")
625
    config.write("usb = 1\n")
626
    config.write("usbdevice = 'tablet'\n")
627
    config.write("vnc = 1\n")
628
    if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
629
      config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
630
    else:
631
      config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
632

    
633
    if instance.network_port > constants.VNC_BASE_PORT:
634
      display = instance.network_port - constants.VNC_BASE_PORT
635
      config.write("vncdisplay = %s\n" % display)
636
      config.write("vncunused = 0\n")
637
    else:
638
      config.write("# vncdisplay = 1\n")
639
      config.write("vncunused = 1\n")
640

    
641
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
642
    try:
643
      password = utils.ReadFile(vnc_pwd_file)
644
    except EnvironmentError, err:
645
      raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
646
                                   (vnc_pwd_file, err))
647

    
648
    config.write("vncpasswd = '%s'\n" % password.rstrip())
649

    
650
    config.write("serial = 'pty'\n")
651
    if hvp[constants.HV_USE_LOCALTIME]:
652
      config.write("localtime = 1\n")
653

    
654
    vif_data = []
655
    nic_type = hvp[constants.HV_NIC_TYPE]
656
    if nic_type is None:
657
      # ensure old instances don't change
658
      nic_type_str = ", type=ioemu"
659
    elif nic_type == constants.HT_NIC_PARAVIRTUAL:
660
      nic_type_str = ", type=paravirtualized"
661
    else:
662
      nic_type_str = ", model=%s, type=ioemu" % nic_type
663
    for nic in instance.nics:
664
      nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
665
      ip = getattr(nic, "ip", None)
666
      if ip is not None:
667
        nic_str += ", ip=%s" % ip
668
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
669
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
670
      vif_data.append("'%s'" % nic_str)
671

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

    
674
    disk_data = cls._GetConfigFileDiskData(block_devices,
675
                                           hvp[constants.HV_BLOCKDEV_PREFIX])
676

    
677
    iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
678
    if iso_path:
679
      iso = "'file:%s,hdc:cdrom,r'" % iso_path
680
      disk_data.append(iso)
681

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

    
684
    config.write("on_poweroff = 'destroy'\n")
685
    if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
686
      config.write("on_reboot = 'restart'\n")
687
    else:
688
      config.write("on_reboot = 'destroy'\n")
689
    config.write("on_crash = 'restart'\n")
690
    # just in case it exists
691
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
692
    try:
693
      utils.WriteFile("/etc/xen/%s" % instance.name,
694
                      data=config.getvalue())
695
    except EnvironmentError, err:
696
      raise errors.HypervisorError("Cannot write Xen instance confile"
697
                                   " file /etc/xen/%s: %s" %
698
                                   (instance.name, err))
699

    
700
    return True