Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ 3d680d45

History | View | Annotate | Download (41.9 kB)

1
#
2
#
3

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

    
21

    
22
"""Xen hypervisors
23

24
"""
25

    
26
import logging
27
import errno
28
import string # pylint: disable=W0402
29
import shutil
30
from cStringIO import StringIO
31

    
32
from ganeti import constants
33
from ganeti import errors
34
from ganeti import utils
35
from ganeti.hypervisor import hv_base
36
from ganeti import netutils
37
from ganeti import objects
38
from ganeti import pathutils
39

    
40

    
41
XEND_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xend-config.sxp")
42
XL_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xen/xl.conf")
43
VIF_BRIDGE_SCRIPT = utils.PathJoin(pathutils.XEN_CONFIG_DIR,
44
                                   "scripts/vif-bridge")
45
_DOM0_NAME = "Domain-0"
46
_DISK_LETTERS = string.ascii_lowercase
47

    
48
_FILE_DRIVER_MAP = {
49
  constants.FD_LOOP: "file",
50
  constants.FD_BLKTAP: "tap:aio",
51
  }
52

    
53

    
54
def _CreateConfigCpus(cpu_mask):
55
  """Create a CPU config string for Xen's config file.
56

57
  """
58
  # Convert the string CPU mask to a list of list of int's
59
  cpu_list = utils.ParseMultiCpuMask(cpu_mask)
60

    
61
  if len(cpu_list) == 1:
62
    all_cpu_mapping = cpu_list[0]
63
    if all_cpu_mapping == constants.CPU_PINNING_OFF:
64
      # If CPU pinning has 1 entry that's "all", then remove the
65
      # parameter from the config file
66
      return None
67
    else:
68
      # If CPU pinning has one non-all entry, mapping all vCPUS (the entire
69
      # VM) to one physical CPU, using format 'cpu = "C"'
70
      return "cpu = \"%s\"" % ",".join(map(str, all_cpu_mapping))
71
  else:
72

    
73
    def _GetCPUMap(vcpu):
74
      if vcpu[0] == constants.CPU_PINNING_ALL_VAL:
75
        cpu_map = constants.CPU_PINNING_ALL_XEN
76
      else:
77
        cpu_map = ",".join(map(str, vcpu))
78
      return "\"%s\"" % cpu_map
79

    
80
    # build the result string in format 'cpus = [ "c", "c", "c" ]',
81
    # where each c is a physical CPU number, a range, a list, or any
82
    # combination
83
    return "cpus = [ %s ]" % ", ".join(map(_GetCPUMap, cpu_list))
84

    
85

    
86
def _RunInstanceList(fn, instance_list_errors):
87
  """Helper function for L{_GetInstanceList} to retrieve the list of instances
88
  from xen.
89

90
  @type fn: callable
91
  @param fn: Function to query xen for the list of instances
92
  @type instance_list_errors: list
93
  @param instance_list_errors: Error list
94
  @rtype: list
95

96
  """
97
  result = fn()
98
  if result.failed:
99
    logging.error("Retrieving the instance list from xen failed (%s): %s",
100
                  result.fail_reason, result.output)
101
    instance_list_errors.append(result)
102
    raise utils.RetryAgain()
103

    
104
  # skip over the heading
105
  return result.stdout.splitlines()
106

    
107

    
108
def _ParseInstanceList(lines, include_node):
109
  """Parses the output of listing instances by xen.
110

111
  @type lines: list
112
  @param lines: Result of retrieving the instance list from xen
113
  @type include_node: boolean
114
  @param include_node: If True, return information for Dom0
115
  @return: list of tuple containing (name, id, memory, vcpus, state, time
116
    spent)
117

118
  """
119
  result = []
120

    
121
  # Iterate through all lines while ignoring header
122
  for line in lines[1:]:
123
    # The format of lines is:
124
    # Name      ID Mem(MiB) VCPUs State  Time(s)
125
    # Domain-0   0  3418     4 r-----    266.2
126
    data = line.split()
127
    if len(data) != 6:
128
      raise errors.HypervisorError("Can't parse instance list,"
129
                                   " line: %s" % line)
130
    try:
131
      data[1] = int(data[1])
132
      data[2] = int(data[2])
133
      data[3] = int(data[3])
134
      data[5] = float(data[5])
135
    except (TypeError, ValueError), err:
136
      raise errors.HypervisorError("Can't parse instance list,"
137
                                   " line: %s, error: %s" % (line, err))
138

    
139
    # skip the Domain-0 (optional)
140
    if include_node or data[0] != _DOM0_NAME:
141
      result.append(data)
142

    
143
  return result
144

    
145

    
146
def _GetInstanceList(fn, include_node, _timeout=5):
147
  """Return the list of running instances.
148

149
  See L{_RunInstanceList} and L{_ParseInstanceList} for parameter details.
150

151
  """
152
  instance_list_errors = []
153
  try:
154
    lines = utils.Retry(_RunInstanceList, (0.3, 1.5, 1.0), _timeout,
155
                        args=(fn, instance_list_errors))
156
  except utils.RetryTimeout:
157
    if instance_list_errors:
158
      instance_list_result = instance_list_errors.pop()
159

    
160
      errmsg = ("listing instances failed, timeout exceeded (%s): %s" %
161
                (instance_list_result.fail_reason, instance_list_result.output))
162
    else:
163
      errmsg = "listing instances failed"
164

    
165
    raise errors.HypervisorError(errmsg)
166

    
167
  return _ParseInstanceList(lines, include_node)
168

    
169

    
170
def _IsInstanceRunning(instance_info):
171
  return instance_info == "r-----" \
172
      or instance_info == "-b----"
173

    
174

    
175
def _IsInstanceShutdown(instance_info):
176
  return instance_info == "---s--"
177

    
178

    
179
def _ParseNodeInfo(info):
180
  """Return information about the node.
181

182
  @return: a dict with the following keys (memory values in MiB):
183
        - memory_total: the total memory size on the node
184
        - memory_free: the available memory on the node for instances
185
        - nr_cpus: total number of CPUs
186
        - nr_nodes: in a NUMA system, the number of domains
187
        - nr_sockets: the number of physical CPU sockets in the node
188
        - hv_version: the hypervisor version in the form (major, minor)
189

190
  """
191
  result = {}
192
  cores_per_socket = threads_per_core = nr_cpus = None
193
  xen_major, xen_minor = None, None
194
  memory_total = None
195
  memory_free = None
196

    
197
  for line in info.splitlines():
198
    fields = line.split(":", 1)
199

    
200
    if len(fields) < 2:
201
      continue
202

    
203
    (key, val) = map(lambda s: s.strip(), fields)
204

    
205
    # Note: in Xen 3, memory has changed to total_memory
206
    if key in ("memory", "total_memory"):
207
      memory_total = int(val)
208
    elif key == "free_memory":
209
      memory_free = int(val)
210
    elif key == "nr_cpus":
211
      nr_cpus = result["cpu_total"] = int(val)
212
    elif key == "nr_nodes":
213
      result["cpu_nodes"] = int(val)
214
    elif key == "cores_per_socket":
215
      cores_per_socket = int(val)
216
    elif key == "threads_per_core":
217
      threads_per_core = int(val)
218
    elif key == "xen_major":
219
      xen_major = int(val)
220
    elif key == "xen_minor":
221
      xen_minor = int(val)
222

    
223
  if None not in [cores_per_socket, threads_per_core, nr_cpus]:
224
    result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
225

    
226
  if memory_free is not None:
227
    result["memory_free"] = memory_free
228

    
229
  if memory_total is not None:
230
    result["memory_total"] = memory_total
231

    
232
  if not (xen_major is None or xen_minor is None):
233
    result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
234

    
235
  return result
236

    
237

    
238
def _MergeInstanceInfo(info, instance_list):
239
  """Updates node information from L{_ParseNodeInfo} with instance info.
240

241
  @type info: dict
242
  @param info: Result from L{_ParseNodeInfo}
243
  @type instance_list: list of tuples
244
  @param instance_list: list of instance information; one tuple per instance
245
  @rtype: dict
246

247
  """
248
  total_instmem = 0
249

    
250
  for (name, _, mem, vcpus, _, _) in instance_list:
251
    if name == _DOM0_NAME:
252
      info["memory_dom0"] = mem
253
      info["cpu_dom0"] = vcpus
254

    
255
    # Include Dom0 in total memory usage
256
    total_instmem += mem
257

    
258
  memory_free = info.get("memory_free")
259
  memory_total = info.get("memory_total")
260

    
261
  # Calculate memory used by hypervisor
262
  if None not in [memory_total, memory_free, total_instmem]:
263
    info["memory_hv"] = memory_total - memory_free - total_instmem
264

    
265
  return info
266

    
267

    
268
def _GetNodeInfo(info, instance_list):
269
  """Combines L{_MergeInstanceInfo} and L{_ParseNodeInfo}.
270

271
  @type instance_list: list of tuples
272
  @param instance_list: list of instance information; one tuple per instance
273

274
  """
275
  return _MergeInstanceInfo(_ParseNodeInfo(info), instance_list)
276

    
277

    
278
def _GetConfigFileDiskData(block_devices, blockdev_prefix,
279
                           _letters=_DISK_LETTERS):
280
  """Get disk directives for Xen config file.
281

282
  This method builds the xen config disk directive according to the
283
  given disk_template and block_devices.
284

285
  @param block_devices: list of tuples (cfdev, rldev):
286
      - cfdev: dict containing ganeti config disk part
287
      - rldev: ganeti.block.bdev.BlockDev object
288
  @param blockdev_prefix: a string containing blockdevice prefix,
289
                          e.g. "sd" for /dev/sda
290

291
  @return: string containing disk directive for xen instance config file
292

293
  """
294
  if len(block_devices) > len(_letters):
295
    raise errors.HypervisorError("Too many disks")
296

    
297
  disk_data = []
298

    
299
  for sd_suffix, (cfdev, dev_path) in zip(_letters, block_devices):
300
    sd_name = blockdev_prefix + sd_suffix
301

    
302
    if cfdev.mode == constants.DISK_RDWR:
303
      mode = "w"
304
    else:
305
      mode = "r"
306

    
307
    if cfdev.dev_type in [constants.DT_FILE, constants.DT_SHARED_FILE]:
308
      driver = _FILE_DRIVER_MAP[cfdev.physical_id[0]]
309
    else:
310
      driver = "phy"
311

    
312
    disk_data.append("'%s:%s,%s,%s'" % (driver, dev_path, sd_name, mode))
313

    
314
  return disk_data
315

    
316

    
317
class XenHypervisor(hv_base.BaseHypervisor):
318
  """Xen generic hypervisor interface
319

320
  This is the Xen base class used for both Xen PVM and HVM. It contains
321
  all the functionality that is identical for both.
322

323
  """
324
  CAN_MIGRATE = True
325
  REBOOT_RETRY_COUNT = 60
326
  REBOOT_RETRY_INTERVAL = 10
327
  _ROOT_DIR = pathutils.RUN_DIR + "/xen-hypervisor"
328
  _NICS_DIR = _ROOT_DIR + "/nic" # contains NICs' info
329
  _DIRS = [_ROOT_DIR, _NICS_DIR]
330

    
331
  ANCILLARY_FILES = [
332
    XEND_CONFIG_FILE,
333
    XL_CONFIG_FILE,
334
    VIF_BRIDGE_SCRIPT,
335
    ]
336
  ANCILLARY_FILES_OPT = [
337
    XL_CONFIG_FILE,
338
    ]
339

    
340
  def __init__(self, _cfgdir=None, _run_cmd_fn=None, _cmd=None):
341
    hv_base.BaseHypervisor.__init__(self)
342

    
343
    if _cfgdir is None:
344
      self._cfgdir = pathutils.XEN_CONFIG_DIR
345
    else:
346
      self._cfgdir = _cfgdir
347

    
348
    if _run_cmd_fn is None:
349
      self._run_cmd_fn = utils.RunCmd
350
    else:
351
      self._run_cmd_fn = _run_cmd_fn
352

    
353
    self._cmd = _cmd
354

    
355
  @staticmethod
356
  def _GetCommandFromHvparams(hvparams):
357
    """Returns the Xen command extracted from the given hvparams.
358

359
    @type hvparams: dict of strings
360
    @param hvparams: hypervisor parameters
361

362
    """
363
    if hvparams is None or constants.HV_XEN_CMD not in hvparams:
364
      raise errors.HypervisorError("Cannot determine xen command.")
365
    else:
366
      return hvparams[constants.HV_XEN_CMD]
367

    
368
  def _GetCommand(self, hvparams):
369
    """Returns Xen command to use.
370

371
    @type hvparams: dict of strings
372
    @param hvparams: hypervisor parameters
373

374
    """
375
    if self._cmd is None:
376
      cmd = XenHypervisor._GetCommandFromHvparams(hvparams)
377
    else:
378
      cmd = self._cmd
379

    
380
    if cmd not in constants.KNOWN_XEN_COMMANDS:
381
      raise errors.ProgrammerError("Unknown Xen command '%s'" % cmd)
382

    
383
    return cmd
384

    
385
  def _RunXen(self, args, hvparams):
386
    """Wrapper around L{utils.process.RunCmd} to run Xen command.
387

388
    @type hvparams: dict of strings
389
    @param hvparams: dictionary of hypervisor params
390
    @see: L{utils.process.RunCmd}
391

392
    """
393
    cmd = [self._GetCommand(hvparams)]
394
    cmd.extend(args)
395

    
396
    return self._run_cmd_fn(cmd)
397

    
398
  def _ConfigFileName(self, instance_name):
399
    """Get the config file name for an instance.
400

401
    @param instance_name: instance name
402
    @type instance_name: str
403
    @return: fully qualified path to instance config file
404
    @rtype: str
405

406
    """
407
    return utils.PathJoin(self._cfgdir, instance_name)
408

    
409
  @classmethod
410
  def _WriteNICInfoFile(cls, instance_name, idx, nic):
411
    """Write the Xen config file for the instance.
412

413
    This version of the function just writes the config file from static data.
414

415
    """
416
    dirs = [(dname, constants.RUN_DIRS_MODE)
417
            for dname in cls._DIRS + [cls._InstanceNICDir(instance_name)]]
418
    utils.EnsureDirs(dirs)
419

    
420
    cfg_file = cls._InstanceNICFile(instance_name, idx)
421
    data = StringIO()
422

    
423
    if nic.netinfo:
424
      netinfo = objects.Network.FromDict(nic.netinfo)
425
      data.write("NETWORK_NAME=%s\n" % netinfo.name)
426
      if netinfo.network:
427
        data.write("NETWORK_SUBNET=%s\n" % netinfo.network)
428
      if netinfo.gateway:
429
        data.write("NETWORK_GATEWAY=%s\n" % netinfo.gateway)
430
      if netinfo.network6:
431
        data.write("NETWORK_SUBNET6=%s\n" % netinfo.network6)
432
      if netinfo.gateway6:
433
        data.write("NETWORK_GATEWAY6=%s\n" % netinfo.gateway6)
434
      if netinfo.mac_prefix:
435
        data.write("NETWORK_MAC_PREFIX=%s\n" % netinfo.mac_prefix)
436
      if netinfo.tags:
437
        data.write("NETWORK_TAGS=%s\n" % r"\ ".join(netinfo.tags))
438

    
439
    data.write("MAC=%s\n" % nic.mac)
440
    data.write("IP=%s\n" % nic.ip)
441
    data.write("MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
442
    data.write("LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
443

    
444
    try:
445
      utils.WriteFile(cfg_file, data=data.getvalue())
446
    except EnvironmentError, err:
447
      raise errors.HypervisorError("Cannot write Xen instance configuration"
448
                                   " file %s: %s" % (cfg_file, err))
449

    
450
  @classmethod
451
  def _InstanceNICDir(cls, instance_name):
452
    """Returns the directory holding the tap device files for a given instance.
453

454
    """
455
    return utils.PathJoin(cls._NICS_DIR, instance_name)
456

    
457
  @classmethod
458
  def _InstanceNICFile(cls, instance_name, seq):
459
    """Returns the name of the file containing the tap device for a given NIC
460

461
    """
462
    return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
463

    
464
  @classmethod
465
  def _GetConfig(cls, instance, startup_memory, block_devices):
466
    """Build Xen configuration for an instance.
467

468
    """
469
    raise NotImplementedError
470

    
471
  def _WriteConfigFile(self, instance_name, data):
472
    """Write the Xen config file for the instance.
473

474
    This version of the function just writes the config file from static data.
475

476
    """
477
    # just in case it exists
478
    utils.RemoveFile(utils.PathJoin(self._cfgdir, "auto", instance_name))
479

    
480
    cfg_file = self._ConfigFileName(instance_name)
481
    try:
482
      utils.WriteFile(cfg_file, data=data)
483
    except EnvironmentError, err:
484
      raise errors.HypervisorError("Cannot write Xen instance configuration"
485
                                   " file %s: %s" % (cfg_file, err))
486

    
487
  def _ReadConfigFile(self, instance_name):
488
    """Returns the contents of the instance config file.
489

490
    """
491
    filename = self._ConfigFileName(instance_name)
492

    
493
    try:
494
      file_content = utils.ReadFile(filename)
495
    except EnvironmentError, err:
496
      raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
497

    
498
    return file_content
499

    
500
  def _RemoveConfigFile(self, instance_name):
501
    """Remove the xen configuration file.
502

503
    """
504
    utils.RemoveFile(self._ConfigFileName(instance_name))
505
    try:
506
      shutil.rmtree(self._InstanceNICDir(instance_name))
507
    except OSError, err:
508
      if err.errno != errno.ENOENT:
509
        raise
510

    
511
  def _StashConfigFile(self, instance_name):
512
    """Move the Xen config file to the log directory and return its new path.
513

514
    """
515
    old_filename = self._ConfigFileName(instance_name)
516
    base = ("%s-%s" %
517
            (instance_name, utils.TimestampForFilename()))
518
    new_filename = utils.PathJoin(pathutils.LOG_XEN_DIR, base)
519
    utils.RenameFile(old_filename, new_filename)
520
    return new_filename
521

    
522
  def _GetInstanceList(self, include_node, hvparams):
523
    """Wrapper around module level L{_GetInstanceList}.
524

525
    @type hvparams: dict of strings
526
    @param hvparams: hypervisor parameters to be used on this node
527

528
    """
529
    return _GetInstanceList(lambda: self._RunXen(["list"], hvparams),
530
                            include_node)
531

    
532
  def ListInstances(self, hvparams=None):
533
    """Get the list of running instances.
534

535
    """
536
    instance_list = self._GetInstanceList(False, hvparams)
537
    names = [info[0] for info in instance_list]
538
    return names
539

    
540
  def GetInstanceInfo(self, instance_name, hvparams=None):
541
    """Get instance properties.
542

543
    @type instance_name: string
544
    @param instance_name: the instance name
545
    @type hvparams: dict of strings
546
    @param hvparams: the instance's hypervisor params
547

548
    @return: tuple (name, id, memory, vcpus, stat, times)
549

550
    """
551
    instance_list = self._GetInstanceList(instance_name == _DOM0_NAME, hvparams)
552
    result = None
553
    for data in instance_list:
554
      if data[0] == instance_name:
555
        result = data
556
        break
557
    return result
558

    
559
  def GetAllInstancesInfo(self, hvparams=None):
560
    """Get properties of all instances.
561

562
    @type hvparams: dict of strings
563
    @param hvparams: hypervisor parameters
564
    @return: list of tuples (name, id, memory, vcpus, stat, times)
565

566
    """
567
    return self._GetInstanceList(False, hvparams)
568

    
569
  def _MakeConfigFile(self, instance, startup_memory, block_devices):
570
    """Gather configuration details and write to disk.
571

572
    See L{_GetConfig} for arguments.
573

574
    """
575
    buf = StringIO()
576
    buf.write("# Automatically generated by Ganeti. Do not edit!\n")
577
    buf.write("\n")
578
    buf.write(self._GetConfig(instance, startup_memory, block_devices))
579
    buf.write("\n")
580

    
581
    self._WriteConfigFile(instance.name, buf.getvalue())
582

    
583
  def StartInstance(self, instance, block_devices, startup_paused):
584
    """Start an instance.
585

586
    """
587
    startup_memory = self._InstanceStartupMemory(instance,
588
                                                 hvparams=instance.hvparams)
589

    
590
    self._MakeConfigFile(instance, startup_memory, block_devices)
591

    
592
    cmd = ["create"]
593
    if startup_paused:
594
      cmd.append("-p")
595
    cmd.append(self._ConfigFileName(instance.name))
596

    
597
    result = self._RunXen(cmd, instance.hvparams)
598
    if result.failed:
599
      # Move the Xen configuration file to the log directory to avoid
600
      # leaving a stale config file behind.
601
      stashed_config = self._StashConfigFile(instance.name)
602
      raise errors.HypervisorError("Failed to start instance %s: %s (%s). Moved"
603
                                   " config file to %s" %
604
                                   (instance.name, result.fail_reason,
605
                                    result.output, stashed_config))
606

    
607
  def StopInstance(self, instance, force=False, retry=False, name=None):
608
    """Stop an instance.
609

610
    """
611
    if name is None:
612
      name = instance.name
613

    
614
    return self._StopInstance(name, force, instance.hvparams)
615

    
616
  def _ShutdownInstance(self, name, hvparams):
617
    """Shutdown an instance if the instance is running.
618

619
    @type name: string
620
    @param name: name of the instance to stop
621
    @type hvparams: dict of string
622
    @param hvparams: hypervisor parameters of the instance
623

624
    The '-w' flag waits for shutdown to complete which avoids the need
625
    to poll in the case where we want to destroy the domain
626
    immediately after shutdown.
627

628
    """
629
    instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
630

    
631
    if instance_info is None or _IsInstanceShutdown(instance_info[4]):
632
      logging.info("Failed to shutdown instance %s, not running", name)
633
      return None
634

    
635
    return self._RunXen(["shutdown", "-w", name], hvparams)
636

    
637
  def _DestroyInstance(self, name, hvparams):
638
    """Destroy an instance if the instance if the instance exists.
639

640
    @type name: string
641
    @param name: name of the instance to destroy
642
    @type hvparams: dict of string
643
    @param hvparams: hypervisor parameters of the instance
644

645
    """
646
    instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
647

    
648
    if instance_info is None:
649
      logging.info("Failed to destroy instance %s, does not exist", name)
650
      return None
651

    
652
    return self._RunXen(["destroy", name], hvparams)
653

    
654
  def _StopInstance(self, name, force, hvparams):
655
    """Stop an instance.
656

657
    @type name: string
658
    @param name: name of the instance to destroy
659

660
    @type force: boolean
661
    @param force: whether to do a "hard" stop (destroy)
662

663
    @type hvparams: dict of string
664
    @param hvparams: hypervisor parameters of the instance
665

666
    """
667
    if force:
668
      result = self._DestroyInstance(name, hvparams)
669
    else:
670
      self._ShutdownInstance(name, hvparams)
671
      result = self._DestroyInstance(name, hvparams)
672

    
673
    if result is not None and result.failed and \
674
          self.GetInstanceInfo(name, hvparams=hvparams) is not None:
675
      raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
676
                                   (name, result.fail_reason, result.output))
677

    
678
    # Remove configuration file if stopping/starting instance was successful
679
    self._RemoveConfigFile(name)
680

    
681
  def RebootInstance(self, instance):
682
    """Reboot an instance.
683

684
    """
685
    ini_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
686

    
687
    if ini_info is None:
688
      raise errors.HypervisorError("Failed to reboot instance %s,"
689
                                   " not running" % instance.name)
690

    
691
    result = self._RunXen(["reboot", instance.name], instance.hvparams)
692
    if result.failed:
693
      raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
694
                                   (instance.name, result.fail_reason,
695
                                    result.output))
696

    
697
    def _CheckInstance():
698
      new_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
699

    
700
      # check if the domain ID has changed or the run time has decreased
701
      if (new_info is not None and
702
          (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])):
703
        return
704

    
705
      raise utils.RetryAgain()
706

    
707
    try:
708
      utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
709
                  self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
710
    except utils.RetryTimeout:
711
      raise errors.HypervisorError("Failed to reboot instance %s: instance"
712
                                   " did not reboot in the expected interval" %
713
                                   (instance.name, ))
714

    
715
  def BalloonInstanceMemory(self, instance, mem):
716
    """Balloon an instance memory to a certain value.
717

718
    @type instance: L{objects.Instance}
719
    @param instance: instance to be accepted
720
    @type mem: int
721
    @param mem: actual memory size to use for instance runtime
722

723
    """
724
    result = self._RunXen(["mem-set", instance.name, mem], instance.hvparams)
725
    if result.failed:
726
      raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" %
727
                                   (instance.name, result.fail_reason,
728
                                    result.output))
729

    
730
    # Update configuration file
731
    cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem]
732
    cmd.append(self._ConfigFileName(instance.name))
733

    
734
    result = utils.RunCmd(cmd)
735
    if result.failed:
736
      raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" %
737
                                   (instance.name, result.fail_reason,
738
                                    result.output))
739

    
740
  def GetNodeInfo(self, hvparams=None):
741
    """Return information about the node.
742

743
    @see: L{_GetNodeInfo} and L{_ParseNodeInfo}
744

745
    """
746
    result = self._RunXen(["info"], hvparams)
747
    if result.failed:
748
      logging.error("Can't retrieve xen hypervisor information (%s): %s",
749
                    result.fail_reason, result.output)
750
      return None
751

    
752
    instance_list = self._GetInstanceList(True, hvparams)
753
    return _GetNodeInfo(result.stdout, instance_list)
754

    
755
  @classmethod
756
  def GetInstanceConsole(cls, instance, primary_node, hvparams, beparams):
757
    """Return a command for connecting to the console of an instance.
758

759
    """
760
    xen_cmd = XenHypervisor._GetCommandFromHvparams(hvparams)
761
    return objects.InstanceConsole(instance=instance.name,
762
                                   kind=constants.CONS_SSH,
763
                                   host=primary_node.name,
764
                                   user=constants.SSH_CONSOLE_USER,
765
                                   command=[pathutils.XEN_CONSOLE_WRAPPER,
766
                                            xen_cmd, instance.name])
767

    
768
  def Verify(self, hvparams=None):
769
    """Verify the hypervisor.
770

771
    For Xen, this verifies that the xend process is running.
772

773
    @type hvparams: dict of strings
774
    @param hvparams: hypervisor parameters to be verified against
775

776
    @return: Problem description if something is wrong, C{None} otherwise
777

778
    """
779
    if hvparams is None:
780
      return "Could not verify the hypervisor, because no hvparams were" \
781
             " provided."
782

    
783
    if constants.HV_XEN_CMD in hvparams:
784
      xen_cmd = hvparams[constants.HV_XEN_CMD]
785
      try:
786
        self._CheckToolstack(xen_cmd)
787
      except errors.HypervisorError:
788
        return "The configured xen toolstack '%s' is not available on this" \
789
               " node." % xen_cmd
790

    
791
    result = self._RunXen(["info"], hvparams)
792
    if result.failed:
793
      return "Retrieving information from xen failed: %s, %s" % \
794
        (result.fail_reason, result.output)
795

    
796
    return None
797

    
798
  def MigrationInfo(self, instance):
799
    """Get instance information to perform a migration.
800

801
    @type instance: L{objects.Instance}
802
    @param instance: instance to be migrated
803
    @rtype: string
804
    @return: content of the xen config file
805

806
    """
807
    return self._ReadConfigFile(instance.name)
808

    
809
  def AcceptInstance(self, instance, info, target):
810
    """Prepare to accept an instance.
811

812
    @type instance: L{objects.Instance}
813
    @param instance: instance to be accepted
814
    @type info: string
815
    @param info: content of the xen config file on the source node
816
    @type target: string
817
    @param target: target host (usually ip), on this node
818

819
    """
820
    pass
821

    
822
  def FinalizeMigrationDst(self, instance, info, success):
823
    """Finalize an instance migration.
824

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

828
    @type instance: L{objects.Instance}
829
    @param instance: instance whose migration is being finalized
830
    @type info: string
831
    @param info: content of the xen config file on the source node
832
    @type success: boolean
833
    @param success: whether the migration was a success or a failure
834

835
    """
836
    if success:
837
      self._WriteConfigFile(instance.name, info)
838

    
839
  def MigrateInstance(self, cluster_name, instance, target, live):
840
    """Migrate an instance to a target node.
841

842
    The migration will not be attempted if the instance is not
843
    currently running.
844

845
    @type instance: L{objects.Instance}
846
    @param instance: the instance to be migrated
847
    @type target: string
848
    @param target: ip address of the target node
849
    @type live: boolean
850
    @param live: perform a live migration
851

852
    """
853
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
854

    
855
    return self._MigrateInstance(cluster_name, instance.name, target, port,
856
                                 live, instance.hvparams)
857

    
858
  def _MigrateInstance(self, cluster_name, instance_name, target, port, live,
859
                       hvparams, _ping_fn=netutils.TcpPing):
860
    """Migrate an instance to a target node.
861

862
    @see: L{MigrateInstance} for details
863

864
    """
865
    if hvparams is None:
866
      raise errors.HypervisorError("No hvparams provided.")
867

    
868
    if self.GetInstanceInfo(instance_name, hvparams=hvparams) is None:
869
      raise errors.HypervisorError("Instance not running, cannot migrate")
870

    
871
    cmd = self._GetCommand(hvparams)
872

    
873
    if (cmd == constants.XEN_CMD_XM and
874
        not _ping_fn(target, port, live_port_needed=True)):
875
      raise errors.HypervisorError("Remote host %s not listening on port"
876
                                   " %s, cannot migrate" % (target, port))
877

    
878
    args = ["migrate"]
879

    
880
    if cmd == constants.XEN_CMD_XM:
881
      args.extend(["-p", "%d" % port])
882
      if live:
883
        args.append("-l")
884

    
885
    elif cmd == constants.XEN_CMD_XL:
886
      args.extend([
887
        "-s", constants.XL_SSH_CMD % cluster_name,
888
        "-C", self._ConfigFileName(instance_name),
889
        ])
890

    
891
    else:
892
      raise errors.HypervisorError("Unsupported Xen command: %s" % self._cmd)
893

    
894
    args.extend([instance_name, target])
895

    
896
    result = self._RunXen(args, hvparams)
897
    if result.failed:
898
      raise errors.HypervisorError("Failed to migrate instance %s: %s" %
899
                                   (instance_name, result.output))
900

    
901
  def FinalizeMigrationSource(self, instance, success, live):
902
    """Finalize the instance migration on the source node.
903

904
    @type instance: L{objects.Instance}
905
    @param instance: the instance that was migrated
906
    @type success: bool
907
    @param success: whether the migration succeeded or not
908
    @type live: bool
909
    @param live: whether the user requested a live migration or not
910

911
    """
912
    # pylint: disable=W0613
913
    if success:
914
      # remove old xen file after migration succeeded
915
      try:
916
        self._RemoveConfigFile(instance.name)
917
      except EnvironmentError:
918
        logging.exception("Failure while removing instance config file")
919

    
920
  def GetMigrationStatus(self, instance):
921
    """Get the migration status
922

923
    As MigrateInstance for Xen is still blocking, if this method is called it
924
    means that MigrateInstance has completed successfully. So we can safely
925
    assume that the migration was successful and notify this fact to the client.
926

927
    @type instance: L{objects.Instance}
928
    @param instance: the instance that is being migrated
929
    @rtype: L{objects.MigrationStatus}
930
    @return: the status of the current migration (one of
931
             L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
932
             progress info that can be retrieved from the hypervisor
933

934
    """
935
    return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
936

    
937
  def PowercycleNode(self, hvparams=None):
938
    """Xen-specific powercycle.
939

940
    This first does a Linux reboot (which triggers automatically a Xen
941
    reboot), and if that fails it tries to do a Xen reboot. The reason
942
    we don't try a Xen reboot first is that the xen reboot launches an
943
    external command which connects to the Xen hypervisor, and that
944
    won't work in case the root filesystem is broken and/or the xend
945
    daemon is not working.
946

947
    @type hvparams: dict of strings
948
    @param hvparams: hypervisor params to be used on this node
949

950
    """
951
    try:
952
      self.LinuxPowercycle()
953
    finally:
954
      xen_cmd = self._GetCommand(hvparams)
955
      utils.RunCmd([xen_cmd, "debug", "R"])
956

    
957
  def _CheckToolstack(self, xen_cmd):
958
    """Check whether the given toolstack is available on the node.
959

960
    @type xen_cmd: string
961
    @param xen_cmd: xen command (e.g. 'xm' or 'xl')
962

963
    """
964
    binary_found = self._CheckToolstackBinary(xen_cmd)
965
    if not binary_found:
966
      raise errors.HypervisorError("No '%s' binary found on node." % xen_cmd)
967
    elif xen_cmd == constants.XEN_CMD_XL:
968
      if not self._CheckToolstackXlConfigured():
969
        raise errors.HypervisorError("Toolstack '%s' is not enabled on this"
970
                                     "node." % xen_cmd)
971

    
972
  def _CheckToolstackBinary(self, xen_cmd):
973
    """Checks whether the xen command's binary is found on the machine.
974

975
    """
976
    if xen_cmd not in constants.KNOWN_XEN_COMMANDS:
977
      raise errors.HypervisorError("Unknown xen command '%s'." % xen_cmd)
978
    result = self._run_cmd_fn(["which", xen_cmd])
979
    return not result.failed
980

    
981
  def _CheckToolstackXlConfigured(self):
982
    """Checks whether xl is enabled on an xl-capable node.
983

984
    @rtype: bool
985
    @returns: C{True} if 'xl' is enabled, C{False} otherwise
986

987
    """
988
    result = self._run_cmd_fn([constants.XEN_CMD_XL, "help"])
989
    if not result.failed:
990
      return True
991
    elif result.failed:
992
      if "toolstack" in result.stderr:
993
        return False
994
      # xl fails for some other reason than the toolstack
995
      else:
996
        raise errors.HypervisorError("Cannot run xen ('%s'). Error: %s."
997
                                     % (constants.XEN_CMD_XL, result.stderr))
998

    
999

    
1000
class XenPvmHypervisor(XenHypervisor):
1001
  """Xen PVM hypervisor interface"""
1002

    
1003
  PARAMETERS = {
1004
    constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
1005
    constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
1006
    constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
1007
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1008
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
1009
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
1010
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
1011
    constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1012
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1013
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
1014
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1015
    constants.HV_REBOOT_BEHAVIOR:
1016
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1017
    constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1018
    constants.HV_CPU_CAP: hv_base.OPT_NONNEGATIVE_INT_CHECK,
1019
    constants.HV_CPU_WEIGHT:
1020
      (False, lambda x: 0 < x < 65536, "invalid weight", None, None),
1021
    constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1022
    constants.HV_XEN_CMD:
1023
      hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1024
    }
1025

    
1026
  def _GetConfig(self, instance, startup_memory, block_devices):
1027
    """Write the Xen config file for the instance.
1028

1029
    """
1030
    hvp = instance.hvparams
1031
    config = StringIO()
1032
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
1033

    
1034
    # if bootloader is True, use bootloader instead of kernel and ramdisk
1035
    # parameters.
1036
    if hvp[constants.HV_USE_BOOTLOADER]:
1037
      # bootloader handling
1038
      bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
1039
      if bootloader_path:
1040
        config.write("bootloader = '%s'\n" % bootloader_path)
1041
      else:
1042
        raise errors.HypervisorError("Bootloader enabled, but missing"
1043
                                     " bootloader path")
1044

    
1045
      bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
1046
      if bootloader_args:
1047
        config.write("bootargs = '%s'\n" % bootloader_args)
1048
    else:
1049
      # kernel handling
1050
      kpath = hvp[constants.HV_KERNEL_PATH]
1051
      config.write("kernel = '%s'\n" % kpath)
1052

    
1053
      # initrd handling
1054
      initrd_path = hvp[constants.HV_INITRD_PATH]
1055
      if initrd_path:
1056
        config.write("ramdisk = '%s'\n" % initrd_path)
1057

    
1058
    # rest of the settings
1059
    config.write("memory = %d\n" % startup_memory)
1060
    config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1061
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1062
    cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1063
    if cpu_pinning:
1064
      config.write("%s\n" % cpu_pinning)
1065
    cpu_cap = hvp[constants.HV_CPU_CAP]
1066
    if cpu_cap:
1067
      config.write("cpu_cap=%d\n" % cpu_cap)
1068
    cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1069
    if cpu_weight:
1070
      config.write("cpu_weight=%d\n" % cpu_weight)
1071

    
1072
    config.write("name = '%s'\n" % instance.name)
1073

    
1074
    vif_data = []
1075
    for idx, nic in enumerate(instance.nics):
1076
      nic_str = "mac=%s" % (nic.mac)
1077
      ip = getattr(nic, "ip", None)
1078
      if ip is not None:
1079
        nic_str += ", ip=%s" % ip
1080
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1081
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1082
      if hvp[constants.HV_VIF_SCRIPT]:
1083
        nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
1084
      vif_data.append("'%s'" % nic_str)
1085
      self._WriteNICInfoFile(instance.name, idx, nic)
1086

    
1087
    disk_data = \
1088
      _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1089

    
1090
    config.write("vif = [%s]\n" % ",".join(vif_data))
1091
    config.write("disk = [%s]\n" % ",".join(disk_data))
1092

    
1093
    if hvp[constants.HV_ROOT_PATH]:
1094
      config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
1095
    config.write("on_poweroff = 'destroy'\n")
1096
    if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
1097
      config.write("on_reboot = 'restart'\n")
1098
    else:
1099
      config.write("on_reboot = 'destroy'\n")
1100
    config.write("on_crash = 'restart'\n")
1101
    config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
1102

    
1103
    return config.getvalue()
1104

    
1105

    
1106
class XenHvmHypervisor(XenHypervisor):
1107
  """Xen HVM hypervisor interface"""
1108

    
1109
  ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
1110
    pathutils.VNC_PASSWORD_FILE,
1111
    ]
1112
  ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [
1113
    pathutils.VNC_PASSWORD_FILE,
1114
    ]
1115

    
1116
  PARAMETERS = {
1117
    constants.HV_ACPI: hv_base.NO_CHECK,
1118
    constants.HV_BOOT_ORDER: (True, ) +
1119
      (lambda x: x and len(x.strip("acdn")) == 0,
1120
       "Invalid boot order specified, must be one or more of [acdn]",
1121
       None, None),
1122
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
1123
    constants.HV_DISK_TYPE:
1124
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
1125
    constants.HV_NIC_TYPE:
1126
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
1127
    constants.HV_PAE: hv_base.NO_CHECK,
1128
    constants.HV_VNC_BIND_ADDRESS:
1129
      (False, netutils.IP4Address.IsValid,
1130
       "VNC bind address is not a valid IP address", None, None),
1131
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1132
    constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
1133
    constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
1134
    constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1135
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1136
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
1137
    # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
1138
    constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1139
    # Add PCI passthrough
1140
    constants.HV_PASSTHROUGH: hv_base.NO_CHECK,
1141
    constants.HV_REBOOT_BEHAVIOR:
1142
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1143
    constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1144
    constants.HV_CPU_CAP: hv_base.NO_CHECK,
1145
    constants.HV_CPU_WEIGHT:
1146
      (False, lambda x: 0 < x < 65535, "invalid weight", None, None),
1147
    constants.HV_VIF_TYPE:
1148
      hv_base.ParamInSet(False, constants.HT_HVM_VALID_VIF_TYPES),
1149
    constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1150
    constants.HV_VIRIDIAN: hv_base.NO_CHECK,
1151
    constants.HV_XEN_CMD:
1152
      hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1153
    }
1154

    
1155
  def _GetConfig(self, instance, startup_memory, block_devices):
1156
    """Create a Xen 3.1 HVM config file.
1157

1158
    """
1159
    hvp = instance.hvparams
1160

    
1161
    config = StringIO()
1162

    
1163
    # kernel handling
1164
    kpath = hvp[constants.HV_KERNEL_PATH]
1165
    config.write("kernel = '%s'\n" % kpath)
1166

    
1167
    config.write("builder = 'hvm'\n")
1168
    config.write("memory = %d\n" % startup_memory)
1169
    config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1170
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1171
    cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1172
    if cpu_pinning:
1173
      config.write("%s\n" % cpu_pinning)
1174
    cpu_cap = hvp[constants.HV_CPU_CAP]
1175
    if cpu_cap:
1176
      config.write("cpu_cap=%d\n" % cpu_cap)
1177
    cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1178
    if cpu_weight:
1179
      config.write("cpu_weight=%d\n" % cpu_weight)
1180

    
1181
    config.write("name = '%s'\n" % instance.name)
1182
    if hvp[constants.HV_PAE]:
1183
      config.write("pae = 1\n")
1184
    else:
1185
      config.write("pae = 0\n")
1186
    if hvp[constants.HV_ACPI]:
1187
      config.write("acpi = 1\n")
1188
    else:
1189
      config.write("acpi = 0\n")
1190
    if hvp[constants.HV_VIRIDIAN]:
1191
      config.write("viridian = 1\n")
1192
    else:
1193
      config.write("viridian = 0\n")
1194

    
1195
    config.write("apic = 1\n")
1196
    config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
1197
    config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
1198
    config.write("sdl = 0\n")
1199
    config.write("usb = 1\n")
1200
    config.write("usbdevice = 'tablet'\n")
1201
    config.write("vnc = 1\n")
1202
    if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
1203
      config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
1204
    else:
1205
      config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
1206

    
1207
    if instance.network_port > constants.VNC_BASE_PORT:
1208
      display = instance.network_port - constants.VNC_BASE_PORT
1209
      config.write("vncdisplay = %s\n" % display)
1210
      config.write("vncunused = 0\n")
1211
    else:
1212
      config.write("# vncdisplay = 1\n")
1213
      config.write("vncunused = 1\n")
1214

    
1215
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
1216
    try:
1217
      password = utils.ReadFile(vnc_pwd_file)
1218
    except EnvironmentError, err:
1219
      raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
1220
                                   (vnc_pwd_file, err))
1221

    
1222
    config.write("vncpasswd = '%s'\n" % password.rstrip())
1223

    
1224
    config.write("serial = 'pty'\n")
1225
    if hvp[constants.HV_USE_LOCALTIME]:
1226
      config.write("localtime = 1\n")
1227

    
1228
    vif_data = []
1229
    # Note: what is called 'nic_type' here, is used as value for the xen nic
1230
    # vif config parameter 'model'. For the xen nic vif parameter 'type', we use
1231
    # the 'vif_type' to avoid a clash of notation.
1232
    nic_type = hvp[constants.HV_NIC_TYPE]
1233

    
1234
    if nic_type is None:
1235
      vif_type_str = ""
1236
      if hvp[constants.HV_VIF_TYPE]:
1237
        vif_type_str = ", type=%s" % hvp[constants.HV_VIF_TYPE]
1238
      # ensure old instances don't change
1239
      nic_type_str = vif_type_str
1240
    elif nic_type == constants.HT_NIC_PARAVIRTUAL:
1241
      nic_type_str = ", type=paravirtualized"
1242
    else:
1243
      # parameter 'model' is only valid with type 'ioemu'
1244
      nic_type_str = ", model=%s, type=%s" % \
1245
        (nic_type, constants.HT_HVM_VIF_IOEMU)
1246
    for idx, nic in enumerate(instance.nics):
1247
      nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
1248
      ip = getattr(nic, "ip", None)
1249
      if ip is not None:
1250
        nic_str += ", ip=%s" % ip
1251
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1252
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1253
      if hvp[constants.HV_VIF_SCRIPT]:
1254
        nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
1255
      vif_data.append("'%s'" % nic_str)
1256
      self._WriteNICInfoFile(instance.name, idx, nic)
1257

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

    
1260
    disk_data = \
1261
      _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1262

    
1263
    iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
1264
    if iso_path:
1265
      iso = "'file:%s,hdc:cdrom,r'" % iso_path
1266
      disk_data.append(iso)
1267

    
1268
    config.write("disk = [%s]\n" % (",".join(disk_data)))
1269
    # Add PCI passthrough
1270
    pci_pass_arr = []
1271
    pci_pass = hvp[constants.HV_PASSTHROUGH]
1272
    if pci_pass:
1273
      pci_pass_arr = pci_pass.split(";")
1274
      config.write("pci = %s\n" % pci_pass_arr)
1275
    config.write("on_poweroff = 'destroy'\n")
1276
    if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
1277
      config.write("on_reboot = 'restart'\n")
1278
    else:
1279
      config.write("on_reboot = 'destroy'\n")
1280
    config.write("on_crash = 'restart'\n")
1281

    
1282
    return config.getvalue()