Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ 22d568c2

History | View | Annotate | Download (24.2 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
XEND_CONFIG_FILE = "/etc/xen/xend-config.sxp"
38
XL_CONFIG_FILE = "/etc/xen/xl.conf"
39
VIF_BRIDGE_SCRIPT = "/etc/xen/scripts/vif-bridge"
40

    
41

    
42
class XenHypervisor(hv_base.BaseHypervisor):
43
  """Xen generic hypervisor interface
44

45
  This is the Xen base class used for both Xen PVM and HVM. It contains
46
  all the functionality that is identical for both.
47

48
  """
49
  CAN_MIGRATE = True
50
  REBOOT_RETRY_COUNT = 60
51
  REBOOT_RETRY_INTERVAL = 10
52

    
53
  ANCILLARY_FILES = [
54
    XEND_CONFIG_FILE,
55
    XL_CONFIG_FILE,
56
    VIF_BRIDGE_SCRIPT,
57
    ]
58

    
59
  @staticmethod
60
  def _ConfigFileName(instance_name):
61
    """Get the config file name for an instance.
62

63
    @param instance_name: instance name
64
    @type instance_name: str
65
    @return: fully qualified path to instance config file
66
    @rtype: str
67

68
    """
69
    return "/etc/xen/%s" % instance_name
70

    
71
  @classmethod
72
  def _WriteConfigFile(cls, instance, block_devices):
73
    """Write the Xen config file for the instance.
74

75
    """
76
    raise NotImplementedError
77

    
78
  @staticmethod
79
  def _WriteConfigFileStatic(instance_name, data):
80
    """Write the Xen config file for the instance.
81

82
    This version of the function just writes the config file from static data.
83

84
    """
85
    utils.WriteFile(XenHypervisor._ConfigFileName(instance_name), data=data)
86

    
87
  @staticmethod
88
  def _ReadConfigFile(instance_name):
89
    """Returns the contents of the instance config file.
90

91
    """
92
    try:
93
      file_content = utils.ReadFile(
94
                       XenHypervisor._ConfigFileName(instance_name))
95
    except EnvironmentError, err:
96
      raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
97
    return file_content
98

    
99
  @staticmethod
100
  def _RemoveConfigFile(instance_name):
101
    """Remove the xen configuration file.
102

103
    """
104
    utils.RemoveFile(XenHypervisor._ConfigFileName(instance_name))
105

    
106
  @staticmethod
107
  def _RunXmList(xmlist_errors):
108
    """Helper function for L{_GetXMList} to run "xm list".
109

110
    """
111
    result = utils.RunCmd([constants.XEN_CMD, "list"])
112
    if result.failed:
113
      logging.error("xm list failed (%s): %s", result.fail_reason,
114
                    result.output)
115
      xmlist_errors.append(result)
116
      raise utils.RetryAgain()
117

    
118
    # skip over the heading
119
    return result.stdout.splitlines()[1:]
120

    
121
  @classmethod
122
  def _GetXMList(cls, include_node):
123
    """Return the list of running instances.
124

125
    If the include_node argument is True, then we return information
126
    for dom0 also, otherwise we filter that from the return value.
127

128
    @return: list of (name, id, memory, vcpus, state, time spent)
129

130
    """
131
    xmlist_errors = []
132
    try:
133
      lines = utils.Retry(cls._RunXmList, 1, 5, args=(xmlist_errors, ))
134
    except utils.RetryTimeout:
135
      if xmlist_errors:
136
        xmlist_result = xmlist_errors.pop()
137

    
138
        errmsg = ("xm list failed, timeout exceeded (%s): %s" %
139
                  (xmlist_result.fail_reason, xmlist_result.output))
140
      else:
141
        errmsg = "xm list failed"
142

    
143
      raise errors.HypervisorError(errmsg)
144

    
145
    result = []
146
    for line in lines:
147
      # The format of lines is:
148
      # Name      ID Mem(MiB) VCPUs State  Time(s)
149
      # Domain-0   0  3418     4 r-----    266.2
150
      data = line.split()
151
      if len(data) != 6:
152
        raise errors.HypervisorError("Can't parse output of xm list,"
153
                                     " line: %s" % line)
154
      try:
155
        data[1] = int(data[1])
156
        data[2] = int(data[2])
157
        data[3] = int(data[3])
158
        data[5] = float(data[5])
159
      except (TypeError, ValueError), err:
160
        raise errors.HypervisorError("Can't parse output of xm list,"
161
                                     " line: %s, error: %s" % (line, err))
162

    
163
      # skip the Domain-0 (optional)
164
      if include_node or data[0] != "Domain-0":
165
        result.append(data)
166

    
167
    return result
168

    
169
  def ListInstances(self):
170
    """Get the list of running instances.
171

172
    """
173
    xm_list = self._GetXMList(False)
174
    names = [info[0] for info in xm_list]
175
    return names
176

    
177
  def GetInstanceInfo(self, instance_name):
178
    """Get instance properties.
179

180
    @param instance_name: the instance name
181

182
    @return: tuple (name, id, memory, vcpus, stat, times)
183

184
    """
185
    xm_list = self._GetXMList(instance_name == "Domain-0")
186
    result = None
187
    for data in xm_list:
188
      if data[0] == instance_name:
189
        result = data
190
        break
191
    return result
192

    
193
  def GetAllInstancesInfo(self):
194
    """Get properties of all instances.
195

196
    @return: list of tuples (name, id, memory, vcpus, stat, times)
197

198
    """
199
    xm_list = self._GetXMList(False)
200
    return xm_list
201

    
202
  def StartInstance(self, instance, block_devices, startup_paused):
203
    """Start an instance.
204

205
    """
206
    self._WriteConfigFile(instance, block_devices)
207
    cmd = [constants.XEN_CMD, "create"]
208
    if startup_paused:
209
      cmd.extend(["-p"])
210
    cmd.extend([self._ConfigFileName(instance.name)])
211
    result = utils.RunCmd(cmd)
212

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

    
218
  def StopInstance(self, instance, force=False, retry=False, name=None):
219
    """Stop an instance.
220

221
    """
222
    if name is None:
223
      name = instance.name
224
    self._RemoveConfigFile(name)
225
    if force:
226
      command = [constants.XEN_CMD, "destroy", name]
227
    else:
228
      command = [constants.XEN_CMD, "shutdown", name]
229
    result = utils.RunCmd(command)
230

    
231
    if result.failed:
232
      raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
233
                                   (name, result.fail_reason, result.output))
234

    
235
  def RebootInstance(self, instance):
236
    """Reboot an instance.
237

238
    """
239
    ini_info = self.GetInstanceInfo(instance.name)
240

    
241
    if ini_info is None:
242
      raise errors.HypervisorError("Failed to reboot instance %s,"
243
                                   " not running" % instance.name)
244

    
245
    result = utils.RunCmd([constants.XEN_CMD, "reboot", instance.name])
246
    if result.failed:
247
      raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
248
                                   (instance.name, result.fail_reason,
249
                                    result.output))
250

    
251
    def _CheckInstance():
252
      new_info = self.GetInstanceInfo(instance.name)
253

    
254
      # check if the domain ID has changed or the run time has decreased
255
      if (new_info is not None and
256
          (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])):
257
        return
258

    
259
      raise utils.RetryAgain()
260

    
261
    try:
262
      utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
263
                  self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
264
    except utils.RetryTimeout:
265
      raise errors.HypervisorError("Failed to reboot instance %s: instance"
266
                                   " did not reboot in the expected interval" %
267
                                   (instance.name, ))
268

    
269
  def GetNodeInfo(self):
270
    """Return information about the node.
271

272
    @return: a dict with the following keys (memory values in MiB):
273
          - memory_total: the total memory size on the node
274
          - memory_free: the available memory on the node for instances
275
          - memory_dom0: the memory used by the node itself, if available
276
          - nr_cpus: total number of CPUs
277
          - nr_nodes: in a NUMA system, the number of domains
278
          - nr_sockets: the number of physical CPU sockets in the node
279
          - hv_version: the hypervisor version in the form (major, minor)
280

281
    """
282
    # note: in xen 3, memory has changed to total_memory
283
    result = utils.RunCmd([constants.XEN_CMD, "info"])
284
    if result.failed:
285
      logging.error("Can't run 'xm info' (%s): %s", result.fail_reason,
286
                    result.output)
287
      return None
288

    
289
    xmoutput = result.stdout.splitlines()
290
    result = {}
291
    cores_per_socket = threads_per_core = nr_cpus = None
292
    xen_major, xen_minor = None, None
293
    for line in xmoutput:
294
      splitfields = line.split(":", 1)
295

    
296
      if len(splitfields) > 1:
297
        key = splitfields[0].strip()
298
        val = splitfields[1].strip()
299
        if key == "memory" or key == "total_memory":
300
          result["memory_total"] = int(val)
301
        elif key == "free_memory":
302
          result["memory_free"] = int(val)
303
        elif key == "nr_cpus":
304
          nr_cpus = result["cpu_total"] = int(val)
305
        elif key == "nr_nodes":
306
          result["cpu_nodes"] = int(val)
307
        elif key == "cores_per_socket":
308
          cores_per_socket = int(val)
309
        elif key == "threads_per_core":
310
          threads_per_core = int(val)
311
        elif key == "xen_major":
312
          xen_major = int(val)
313
        elif key == "xen_minor":
314
          xen_minor = int(val)
315

    
316
    if (cores_per_socket is not None and
317
        threads_per_core is not None and nr_cpus is not None):
318
      result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
319

    
320
    dom0_info = self.GetInstanceInfo("Domain-0")
321
    if dom0_info is not None:
322
      result["memory_dom0"] = dom0_info[2]
323

    
324
    if not (xen_major is None or xen_minor is None):
325
      result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
326

    
327
    return result
328

    
329
  @classmethod
330
  def GetInstanceConsole(cls, instance, hvparams, beparams):
331
    """Return a command for connecting to the console of an instance.
332

333
    """
334
    return objects.InstanceConsole(instance=instance.name,
335
                                   kind=constants.CONS_SSH,
336
                                   host=instance.primary_node,
337
                                   user=constants.GANETI_RUNAS,
338
                                   command=[constants.XM_CONSOLE_WRAPPER,
339
                                            instance.name])
340

    
341
  def Verify(self):
342
    """Verify the hypervisor.
343

344
    For Xen, this verifies that the xend process is running.
345

346
    """
347
    result = utils.RunCmd([constants.XEN_CMD, "info"])
348
    if result.failed:
349
      return "'xm info' failed: %s, %s" % (result.fail_reason, result.output)
350

    
351
  @staticmethod
352
  def _GetConfigFileDiskData(block_devices, blockdev_prefix):
353
    """Get disk directive for xen config file.
354

355
    This method builds the xen config disk directive according to the
356
    given disk_template and block_devices.
357

358
    @param block_devices: list of tuples (cfdev, rldev):
359
        - cfdev: dict containing ganeti config disk part
360
        - rldev: ganeti.bdev.BlockDev object
361
    @param blockdev_prefix: a string containing blockdevice prefix,
362
                            e.g. "sd" for /dev/sda
363

364
    @return: string containing disk directive for xen instance config file
365

366
    """
367
    FILE_DRIVER_MAP = {
368
      constants.FD_LOOP: "file",
369
      constants.FD_BLKTAP: "tap:aio",
370
      }
371
    disk_data = []
372
    if len(block_devices) > 24:
373
      # 'z' - 'a' = 24
374
      raise errors.HypervisorError("Too many disks")
375
    namespace = [blockdev_prefix + chr(i + ord("a")) for i in range(24)]
376
    for sd_name, (cfdev, dev_path) in zip(namespace, block_devices):
377
      if cfdev.mode == constants.DISK_RDWR:
378
        mode = "w"
379
      else:
380
        mode = "r"
381
      if cfdev.dev_type == constants.LD_FILE:
382
        line = "'%s:%s,%s,%s'" % (FILE_DRIVER_MAP[cfdev.physical_id[0]],
383
                                  dev_path, sd_name, mode)
384
      else:
385
        line = "'phy:%s,%s,%s'" % (dev_path, sd_name, mode)
386
      disk_data.append(line)
387

    
388
    return disk_data
389

    
390
  def MigrationInfo(self, instance):
391
    """Get instance information to perform a migration.
392

393
    @type instance: L{objects.Instance}
394
    @param instance: instance to be migrated
395
    @rtype: string
396
    @return: content of the xen config file
397

398
    """
399
    return self._ReadConfigFile(instance.name)
400

    
401
  def AcceptInstance(self, instance, info, target):
402
    """Prepare to accept an instance.
403

404
    @type instance: L{objects.Instance}
405
    @param instance: instance to be accepted
406
    @type info: string
407
    @param info: content of the xen config file on the source node
408
    @type target: string
409
    @param target: target host (usually ip), on this node
410

411
    """
412
    pass
413

    
414
  def FinalizeMigration(self, instance, info, success):
415
    """Finalize an instance migration.
416

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

420
    @type instance: L{objects.Instance}
421
    @param instance: instance whose migration is being finalized
422
    @type info: string
423
    @param info: content of the xen config file on the source node
424
    @type success: boolean
425
    @param success: whether the migration was a success or a failure
426

427
    """
428
    if success:
429
      self._WriteConfigFileStatic(instance.name, info)
430

    
431
  def MigrateInstance(self, instance, target, live):
432
    """Migrate an instance to a target node.
433

434
    The migration will not be attempted if the instance is not
435
    currently running.
436

437
    @type instance: L{objects.Instance}
438
    @param instance: the instance to be migrated
439
    @type target: string
440
    @param target: ip address of the target node
441
    @type live: boolean
442
    @param live: perform a live migration
443

444
    """
445
    if self.GetInstanceInfo(instance.name) is None:
446
      raise errors.HypervisorError("Instance not running, cannot migrate")
447

    
448
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
449

    
450
    if not netutils.TcpPing(target, port, live_port_needed=True):
451
      raise errors.HypervisorError("Remote host %s not listening on port"
452
                                   " %s, cannot migrate" % (target, port))
453

    
454
    # FIXME: migrate must be upgraded for transitioning to "xl" (xen 4.1).
455
    #  -l doesn't exist anymore
456
    #  -p doesn't exist anymore
457
    #  -C config_file must be passed
458
    #  ssh must recognize the key of the target host for the migration
459
    args = [constants.XEN_CMD, "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([constants.XEN_CMD, "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
    }
509

    
510
  @classmethod
511
  def _WriteConfigFile(cls, instance, block_devices):
512
    """Write the Xen config file for the instance.
513

514
    """
515
    hvp = instance.hvparams
516
    config = StringIO()
517
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
518

    
519
    # if bootloader is True, use bootloader instead of kernel and ramdisk
520
    # parameters.
521
    if hvp[constants.HV_USE_BOOTLOADER]:
522
      # bootloader handling
523
      bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
524
      if bootloader_path:
525
        config.write("bootloader = '%s'\n" % bootloader_path)
526
      else:
527
        raise errors.HypervisorError("Bootloader enabled, but missing"
528
                                     " bootloader path")
529

    
530
      bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
531
      if bootloader_args:
532
        config.write("bootargs = '%s'\n" % bootloader_args)
533
    else:
534
      # kernel handling
535
      kpath = hvp[constants.HV_KERNEL_PATH]
536
      config.write("kernel = '%s'\n" % kpath)
537

    
538
      # initrd handling
539
      initrd_path = hvp[constants.HV_INITRD_PATH]
540
      if initrd_path:
541
        config.write("ramdisk = '%s'\n" % initrd_path)
542

    
543
    # rest of the settings
544
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
545
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
546
    config.write("name = '%s'\n" % instance.name)
547

    
548
    vif_data = []
549
    for nic in instance.nics:
550
      nic_str = "mac=%s" % (nic.mac)
551
      ip = getattr(nic, "ip", None)
552
      if ip is not None:
553
        nic_str += ", ip=%s" % ip
554
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
555
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
556
      vif_data.append("'%s'" % nic_str)
557

    
558
    disk_data = cls._GetConfigFileDiskData(block_devices,
559
                                           hvp[constants.HV_BLOCKDEV_PREFIX])
560

    
561
    config.write("vif = [%s]\n" % ",".join(vif_data))
562
    config.write("disk = [%s]\n" % ",".join(disk_data))
563

    
564
    if hvp[constants.HV_ROOT_PATH]:
565
      config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
566
    config.write("on_poweroff = 'destroy'\n")
567
    if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
568
      config.write("on_reboot = 'restart'\n")
569
    else:
570
      config.write("on_reboot = 'destroy'\n")
571
    config.write("on_crash = 'restart'\n")
572
    config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
573
    # just in case it exists
574
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
575
    try:
576
      utils.WriteFile(cls._ConfigFileName(instance.name),
577
                      data=config.getvalue())
578
    except EnvironmentError, err:
579
      raise errors.HypervisorError("Cannot write Xen instance confile"
580
                                   " file %s: %s" %
581
                                   (cls._ConfigFileName(instance.name), err))
582

    
583
    return True
584

    
585

    
586
class XenHvmHypervisor(XenHypervisor):
587
  """Xen HVM hypervisor interface"""
588

    
589
  ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
590
    constants.VNC_PASSWORD_FILE,
591
    ]
592

    
593
  PARAMETERS = {
594
    constants.HV_ACPI: hv_base.NO_CHECK,
595
    constants.HV_BOOT_ORDER: (True, ) +
596
      (lambda x: x and len(x.strip("acdn")) == 0,
597
       "Invalid boot order specified, must be one or more of [acdn]",
598
       None, None),
599
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
600
    constants.HV_DISK_TYPE:
601
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
602
    constants.HV_NIC_TYPE:
603
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
604
    constants.HV_PAE: hv_base.NO_CHECK,
605
    constants.HV_VNC_BIND_ADDRESS:
606
      (False, netutils.IP4Address.IsValid,
607
       "VNC bind address is not a valid IP address", None, None),
608
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
609
    constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
610
    constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
611
    constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
612
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
613
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
614
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
615
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
616
    constants.HV_REBOOT_BEHAVIOR:
617
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS)
618
    }
619

    
620
  @classmethod
621
  def _WriteConfigFile(cls, instance, block_devices):
622
    """Create a Xen 3.1 HVM config file.
623

624
    """
625
    hvp = instance.hvparams
626

    
627
    config = StringIO()
628
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
629

    
630
    # kernel handling
631
    kpath = hvp[constants.HV_KERNEL_PATH]
632
    config.write("kernel = '%s'\n" % kpath)
633

    
634
    config.write("builder = 'hvm'\n")
635
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
636
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
637
    config.write("name = '%s'\n" % instance.name)
638
    if hvp[constants.HV_PAE]:
639
      config.write("pae = 1\n")
640
    else:
641
      config.write("pae = 0\n")
642
    if hvp[constants.HV_ACPI]:
643
      config.write("acpi = 1\n")
644
    else:
645
      config.write("acpi = 0\n")
646
    config.write("apic = 1\n")
647
    config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
648
    config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
649
    config.write("sdl = 0\n")
650
    config.write("usb = 1\n")
651
    config.write("usbdevice = 'tablet'\n")
652
    config.write("vnc = 1\n")
653
    if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
654
      config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
655
    else:
656
      config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
657

    
658
    if instance.network_port > constants.VNC_BASE_PORT:
659
      display = instance.network_port - constants.VNC_BASE_PORT
660
      config.write("vncdisplay = %s\n" % display)
661
      config.write("vncunused = 0\n")
662
    else:
663
      config.write("# vncdisplay = 1\n")
664
      config.write("vncunused = 1\n")
665

    
666
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
667
    try:
668
      password = utils.ReadFile(vnc_pwd_file)
669
    except EnvironmentError, err:
670
      raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
671
                                   (vnc_pwd_file, err))
672

    
673
    config.write("vncpasswd = '%s'\n" % password.rstrip())
674

    
675
    config.write("serial = 'pty'\n")
676
    if hvp[constants.HV_USE_LOCALTIME]:
677
      config.write("localtime = 1\n")
678

    
679
    vif_data = []
680
    nic_type = hvp[constants.HV_NIC_TYPE]
681
    if nic_type is None:
682
      # ensure old instances don't change
683
      nic_type_str = ", type=ioemu"
684
    elif nic_type == constants.HT_NIC_PARAVIRTUAL:
685
      nic_type_str = ", type=paravirtualized"
686
    else:
687
      nic_type_str = ", model=%s, type=ioemu" % nic_type
688
    for nic in instance.nics:
689
      nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
690
      ip = getattr(nic, "ip", None)
691
      if ip is not None:
692
        nic_str += ", ip=%s" % ip
693
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
694
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
695
      vif_data.append("'%s'" % nic_str)
696

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

    
699
    disk_data = cls._GetConfigFileDiskData(block_devices,
700
                                           hvp[constants.HV_BLOCKDEV_PREFIX])
701

    
702
    iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
703
    if iso_path:
704
      iso = "'file:%s,hdc:cdrom,r'" % iso_path
705
      disk_data.append(iso)
706

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

    
709
    config.write("on_poweroff = 'destroy'\n")
710
    if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
711
      config.write("on_reboot = 'restart'\n")
712
    else:
713
      config.write("on_reboot = 'destroy'\n")
714
    config.write("on_crash = 'restart'\n")
715
    # just in case it exists
716
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
717
    try:
718
      utils.WriteFile(cls._ConfigFileName(instance.name),
719
                      data=config.getvalue())
720
    except EnvironmentError, err:
721
      raise errors.HypervisorError("Cannot write Xen instance confile"
722
                                   " file %s: %s" %
723
                                   (cls._ConfigFileName(instance.name), err))
724

    
725
    return True