Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ d34b16d7

History | View | Annotate | Download (21.4 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008 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 os
27
import os.path
28
import time
29
import logging
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

    
37

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

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

44
  """
45

    
46
  @classmethod
47
  def _WriteConfigFile(cls, instance, block_devices, extra_args):
48
    """Write the Xen config file for the instance.
49

50
    """
51
    raise NotImplementedError
52

    
53
  @staticmethod
54
  def _WriteConfigFileStatic(instance_name, data):
55
    """Write the Xen config file for the instance.
56

57
    This version of the function just writes the config file from static data.
58

59
    """
60
    utils.WriteFile("/etc/xen/%s" % instance_name, data=data)
61

    
62
  @staticmethod
63
  def _ReadConfigFile(instance_name):
64
    """Returns the contents of the instance config file.
65

66
    """
67
    try:
68
      file_content = utils.ReadFile("/etc/xen/%s" % instance_name)
69
    except EnvironmentError, err:
70
      raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
71
    return file_content
72

    
73
  @staticmethod
74
  def _RemoveConfigFile(instance_name):
75
    """Remove the xen configuration file.
76

77
    """
78
    utils.RemoveFile("/etc/xen/%s" % instance_name)
79

    
80
  @staticmethod
81
  def _GetXMList(include_node):
82
    """Return the list of running instances.
83

84
    If the include_node argument is True, then we return information
85
    for dom0 also, otherwise we filter that from the return value.
86

87
    @return: list of (name, id, memory, vcpus, state, time spent)
88

89
    """
90
    for dummy in range(5):
91
      result = utils.RunCmd(["xm", "list"])
92
      if not result.failed:
93
        break
94
      logging.error("xm list failed (%s): %s", result.fail_reason,
95
                    result.output)
96
      time.sleep(1)
97

    
98
    if result.failed:
99
      raise errors.HypervisorError("xm list failed, retries"
100
                                   " exceeded (%s): %s" %
101
                                   (result.fail_reason, result.stderr))
102

    
103
    # skip over the heading
104
    lines = result.stdout.splitlines()[1:]
105
    result = []
106
    for line in lines:
107
      # The format of lines is:
108
      # Name      ID Mem(MiB) VCPUs State  Time(s)
109
      # Domain-0   0  3418     4 r-----    266.2
110
      data = line.split()
111
      if len(data) != 6:
112
        raise errors.HypervisorError("Can't parse output of xm list,"
113
                                     " line: %s" % line)
114
      try:
115
        data[1] = int(data[1])
116
        data[2] = int(data[2])
117
        data[3] = int(data[3])
118
        data[5] = float(data[5])
119
      except ValueError, err:
120
        raise errors.HypervisorError("Can't parse output of xm list,"
121
                                     " line: %s, error: %s" % (line, err))
122

    
123
      # skip the Domain-0 (optional)
124
      if include_node or data[0] != 'Domain-0':
125
        result.append(data)
126

    
127
    return result
128

    
129
  def ListInstances(self):
130
    """Get the list of running instances.
131

132
    """
133
    xm_list = self._GetXMList(False)
134
    names = [info[0] for info in xm_list]
135
    return names
136

    
137
  def GetInstanceInfo(self, instance_name):
138
    """Get instance properties.
139

140
    @param instance_name: the instance name
141

142
    @return: tuple (name, id, memory, vcpus, stat, times)
143

144
    """
145
    xm_list = self._GetXMList(instance_name=="Domain-0")
146
    result = None
147
    for data in xm_list:
148
      if data[0] == instance_name:
149
        result = data
150
        break
151
    return result
152

    
153
  def GetAllInstancesInfo(self):
154
    """Get properties of all instances.
155

156
    @return: list of tuples (name, id, memory, vcpus, stat, times)
157

158
    """
159
    xm_list = self._GetXMList(False)
160
    return xm_list
161

    
162
  def StartInstance(self, instance, block_devices, extra_args):
163
    """Start an instance.
164

165
    """
166
    self._WriteConfigFile(instance, block_devices, extra_args)
167
    result = utils.RunCmd(["xm", "create", instance.name])
168

    
169
    if result.failed:
170
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
171
                                   (instance.name, result.fail_reason,
172
                                    result.output))
173

    
174
  def StopInstance(self, instance, force=False):
175
    """Stop an instance.
176

177
    """
178
    self._RemoveConfigFile(instance.name)
179
    if force:
180
      command = ["xm", "destroy", instance.name]
181
    else:
182
      command = ["xm", "shutdown", instance.name]
183
    result = utils.RunCmd(command)
184

    
185
    if result.failed:
186
      raise errors.HypervisorError("Failed to stop instance %s: %s" %
187
                                   (instance.name, result.fail_reason))
188

    
189
  def RebootInstance(self, instance):
190
    """Reboot an instance.
191

192
    """
193
    result = utils.RunCmd(["xm", "reboot", instance.name])
194

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

    
199
  def GetNodeInfo(self):
200
    """Return information about the node.
201

202
    @return: a dict with the following keys (values in MiB):
203
          - memory_total: the total memory size on the node
204
          - memory_free: the available memory on the node for instances
205
          - memory_dom0: the memory used by the node itself, if available
206

207
    """
208
    # note: in xen 3, memory has changed to total_memory
209
    result = utils.RunCmd(["xm", "info"])
210
    if result.failed:
211
      logging.error("Can't run 'xm info' (%s): %s", result.fail_reason,
212
                    result.output)
213
      return None
214

    
215
    xmoutput = result.stdout.splitlines()
216
    result = {}
217
    for line in xmoutput:
218
      splitfields = line.split(":", 1)
219

    
220
      if len(splitfields) > 1:
221
        key = splitfields[0].strip()
222
        val = splitfields[1].strip()
223
        if key == 'memory' or key == 'total_memory':
224
          result['memory_total'] = int(val)
225
        elif key == 'free_memory':
226
          result['memory_free'] = int(val)
227
        elif key == 'nr_cpus':
228
          result['cpu_total'] = int(val)
229
    dom0_info = self.GetInstanceInfo("Domain-0")
230
    if dom0_info is not None:
231
      result['memory_dom0'] = dom0_info[2]
232

    
233
    return result
234

    
235
  @classmethod
236
  def GetShellCommandForConsole(cls, instance):
237
    """Return a command for connecting to the console of an instance.
238

239
    """
240
    return "xm console %s" % instance.name
241

    
242

    
243
  def Verify(self):
244
    """Verify the hypervisor.
245

246
    For Xen, this verifies that the xend process is running.
247

248
    """
249
    result = utils.RunCmd(["xm", "info"])
250
    if result.failed:
251
      return "'xm info' failed: %s" % result.fail_reason
252

    
253
  @staticmethod
254
  def _GetConfigFileDiskData(disk_template, block_devices):
255
    """Get disk directive for xen config file.
256

257
    This method builds the xen config disk directive according to the
258
    given disk_template and block_devices.
259

260
    @param disk_template: string containing instance disk template
261
    @param block_devices: list of tuples (cfdev, rldev):
262
        - cfdev: dict containing ganeti config disk part
263
        - rldev: ganeti.bdev.BlockDev object
264

265
    @return: string containing disk directive for xen instance config file
266

267
    """
268
    FILE_DRIVER_MAP = {
269
      constants.FD_LOOP: "file",
270
      constants.FD_BLKTAP: "tap:aio",
271
      }
272
    disk_data = []
273
    if len(block_devices) > 24:
274
      # 'z' - 'a' = 24
275
      raise errors.HypervisorError("Too many disks")
276
    # FIXME: instead of this hardcoding here, each of PVM/HVM should
277
    # directly export their info (currently HVM will just sed this info)
278
    namespace = ["sd" + chr(i + ord('a')) for i in range(24)]
279
    for sd_name, (cfdev, dev_path) in zip(namespace, block_devices):
280
      if cfdev.mode == constants.DISK_RDWR:
281
        mode = "w"
282
      else:
283
        mode = "r"
284
      if cfdev.dev_type == constants.LD_FILE:
285
        line = "'%s:%s,%s,%s'" % (FILE_DRIVER_MAP[cfdev.physical_id[0]],
286
                                  dev_path, sd_name, mode)
287
      else:
288
        line = "'phy:%s,%s,%s'" % (dev_path, sd_name, mode)
289
      disk_data.append(line)
290

    
291
    return disk_data
292

    
293
  def MigrationInfo(self, instance):
294
    """Get instance information to perform a migration.
295

296
    @type instance: L{objects.Instance}
297
    @param instance: instance to be migrated
298
    @rtype: string
299
    @return: content of the xen config file
300

301
    """
302
    return self._ReadConfigFile(instance.name)
303

    
304
  def AcceptInstance(self, instance, info, target):
305
    """Prepare to accept an instance.
306

307
    @type instance: L{objects.Instance}
308
    @param instance: instance to be accepted
309
    @type info: string
310
    @param info: content of the xen config file on the source node
311
    @type target: string
312
    @param target: target host (usually ip), on this node
313

314
    """
315
    pass
316

    
317
  def FinalizeMigration(self, instance, info, success):
318
    """Finalize an instance migration.
319

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

323
    @type instance: L{objects.Instance}
324
    @param instance: instance whose migration is being aborted
325
    @type info: string
326
    @param info: content of the xen config file on the source node
327
    @type success: boolean
328
    @param success: whether the migration was a success or a failure
329

330
    """
331
    if success:
332
      self._WriteConfigFileStatic(instance.name, info)
333

    
334
  def MigrateInstance(self, instance, target, live):
335
    """Migrate an instance to a target node.
336

337
    The migration will not be attempted if the instance is not
338
    currently running.
339

340
    @type instance: string
341
    @param instance: instance name
342
    @type target: string
343
    @param target: ip address of the target node
344
    @type live: boolean
345
    @param live: perform a live migration
346

347
    """
348
    if self.GetInstanceInfo(instance) is None:
349
      raise errors.HypervisorError("Instance not running, cannot migrate")
350
    args = ["xm", "migrate"]
351
    if live:
352
      args.append("-l")
353
    args.extend([instance, target])
354
    result = utils.RunCmd(args)
355
    if result.failed:
356
      raise errors.HypervisorError("Failed to migrate instance %s: %s" %
357
                                   (instance, result.output))
358
    # remove old xen file after migration succeeded
359
    try:
360
      self._RemoveConfigFile(instance)
361
    except EnvironmentError:
362
      logging.exception("Failure while removing instance config file")
363

    
364

    
365
class XenPvmHypervisor(XenHypervisor):
366
  """Xen PVM hypervisor interface"""
367

    
368
  PARAMETERS = [
369
    constants.HV_KERNEL_PATH,
370
    constants.HV_INITRD_PATH,
371
    constants.HV_ROOT_PATH,
372
    ]
373

    
374
  @classmethod
375
  def CheckParameterSyntax(cls, hvparams):
376
    """Check the given parameters for validity.
377

378
    For the PVM hypervisor, this only check the existence of the
379
    kernel.
380

381
    @type hvparams:  dict
382
    @param hvparams: dictionary with parameter names/value
383
    @raise errors.HypervisorError: when a parameter is not valid
384

385
    """
386
    super(XenPvmHypervisor, cls).CheckParameterSyntax(hvparams)
387

    
388
    if not hvparams[constants.HV_KERNEL_PATH]:
389
      raise errors.HypervisorError("Need a kernel for the instance")
390

    
391
    if not os.path.isabs(hvparams[constants.HV_KERNEL_PATH]):
392
      raise errors.HypervisorError("The kernel path must be an absolute path")
393

    
394
    if not hvparams[constants.HV_ROOT_PATH]:
395
      raise errors.HypervisorError("Need a root partition for the instance")
396

    
397
    if hvparams[constants.HV_INITRD_PATH]:
398
      if not os.path.isabs(hvparams[constants.HV_INITRD_PATH]):
399
        raise errors.HypervisorError("The initrd path must be an absolute path"
400
                                     ", if defined")
401

    
402
  def ValidateParameters(self, hvparams):
403
    """Check the given parameters for validity.
404

405
    For the PVM hypervisor, this only check the existence of the
406
    kernel.
407

408
    """
409
    super(XenPvmHypervisor, self).ValidateParameters(hvparams)
410

    
411
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
412
    if not os.path.isfile(kernel_path):
413
      raise errors.HypervisorError("Instance kernel '%s' not found or"
414
                                   " not a file" % kernel_path)
415
    initrd_path = hvparams[constants.HV_INITRD_PATH]
416
    if initrd_path and not os.path.isfile(initrd_path):
417
      raise errors.HypervisorError("Instance initrd '%s' not found or"
418
                                   " not a file" % initrd_path)
419

    
420
  @classmethod
421
  def _WriteConfigFile(cls, instance, block_devices, extra_args):
422
    """Write the Xen config file for the instance.
423

424
    """
425
    config = StringIO()
426
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
427

    
428
    # kernel handling
429
    kpath = instance.hvparams[constants.HV_KERNEL_PATH]
430
    config.write("kernel = '%s'\n" % kpath)
431

    
432
    # initrd handling
433
    initrd_path = instance.hvparams[constants.HV_INITRD_PATH]
434
    if initrd_path:
435
      config.write("ramdisk = '%s'\n" % initrd_path)
436

    
437
    # rest of the settings
438
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
439
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
440
    config.write("name = '%s'\n" % instance.name)
441

    
442
    vif_data = []
443
    for nic in instance.nics:
444
      nic_str = "mac=%s, bridge=%s" % (nic.mac, nic.bridge)
445
      ip = getattr(nic, "ip", None)
446
      if ip is not None:
447
        nic_str += ", ip=%s" % ip
448
      vif_data.append("'%s'" % nic_str)
449

    
450
    config.write("vif = [%s]\n" % ",".join(vif_data))
451
    config.write("disk = [%s]\n" % ",".join(
452
                 cls._GetConfigFileDiskData(instance.disk_template,
453
                                            block_devices)))
454

    
455
    rpath = instance.hvparams[constants.HV_ROOT_PATH]
456
    config.write("root = '%s ro'\n" % rpath)
457
    config.write("on_poweroff = 'destroy'\n")
458
    config.write("on_reboot = 'restart'\n")
459
    config.write("on_crash = 'restart'\n")
460
    if extra_args:
461
      config.write("extra = '%s'\n" % extra_args)
462
    # just in case it exists
463
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
464
    try:
465
      utils.WriteFile("/etc/xen/%s" % instance.name,
466
                      data=config.getvalue())
467
    except EnvironmentError, err:
468
      raise errors.HypervisorError("Cannot write Xen instance confile"
469
                                   " file /etc/xen/%s: %s" %
470
                                   (instance.name, err))
471

    
472
    return True
473

    
474

    
475
class XenHvmHypervisor(XenHypervisor):
476
  """Xen HVM hypervisor interface"""
477

    
478
  PARAMETERS = [
479
    constants.HV_ACPI,
480
    constants.HV_BOOT_ORDER,
481
    constants.HV_CDROM_IMAGE_PATH,
482
    constants.HV_DISK_TYPE,
483
    constants.HV_NIC_TYPE,
484
    constants.HV_PAE,
485
    constants.HV_VNC_BIND_ADDRESS,
486
    ]
487

    
488
  @classmethod
489
  def CheckParameterSyntax(cls, hvparams):
490
    """Check the given parameter syntax.
491

492
    """
493
    super(XenHvmHypervisor, cls).CheckParameterSyntax(hvparams)
494
    # boot order verification
495
    boot_order = hvparams[constants.HV_BOOT_ORDER]
496
    if len(boot_order.strip("acdn")) != 0:
497
      raise errors.HypervisorError("Invalid boot order '%s' specified,"
498
                                   " must be one or more of [acdn]" %
499
                                   boot_order)
500
    # device type checks
501
    nic_type = hvparams[constants.HV_NIC_TYPE]
502
    if nic_type not in constants.HT_HVM_VALID_NIC_TYPES:
503
      raise errors.HypervisorError("Invalid NIC type %s specified for Xen HVM"
504
                                   " hypervisor" % nic_type)
505
    disk_type = hvparams[constants.HV_DISK_TYPE]
506
    if disk_type not in constants.HT_HVM_VALID_DISK_TYPES:
507
      raise errors.HypervisorError("Invalid disk type %s specified for Xen HVM"
508
                                   " hypervisor" % disk_type)
509
    # vnc_bind_address verification
510
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
511
    if vnc_bind_address is not None:
512
      if not utils.IsValidIP(vnc_bind_address):
513
        raise errors.OpPrereqError("given VNC bind address '%s' doesn't look"
514
                                   " like a valid IP address" %
515
                                   vnc_bind_address)
516

    
517
    iso_path = hvparams[constants.HV_CDROM_IMAGE_PATH]
518
    if iso_path and not os.path.isabs(iso_path):
519
      raise errors.HypervisorError("The path to the HVM CDROM image must"
520
                                   " be an absolute path or None, not %s" %
521
                                   iso_path)
522

    
523
  def ValidateParameters(self, hvparams):
524
    """Check the given parameters for validity.
525

526
    For the PVM hypervisor, this only check the existence of the
527
    kernel.
528

529
    @type hvparams:  dict
530
    @param hvparams: dictionary with parameter names/value
531
    @raise errors.HypervisorError: when a parameter is not valid
532

533
    """
534
    super(XenHvmHypervisor, self).ValidateParameters(hvparams)
535

    
536
    # hvm_cdrom_image_path verification
537
    iso_path = hvparams[constants.HV_CDROM_IMAGE_PATH]
538
    if iso_path and not os.path.isfile(iso_path):
539
      raise errors.HypervisorError("The HVM CDROM image must either be a"
540
                                   " regular file or a symlink pointing to"
541
                                   " an existing regular file, not %s" %
542
                                   iso_path)
543

    
544
  @classmethod
545
  def _WriteConfigFile(cls, instance, block_devices, extra_args):
546
    """Create a Xen 3.1 HVM config file.
547

548
    """
549
    config = StringIO()
550
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
551
    config.write("kernel = '/usr/lib/xen/boot/hvmloader'\n")
552
    config.write("builder = 'hvm'\n")
553
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
554
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
555
    config.write("name = '%s'\n" % instance.name)
556
    if instance.hvparams[constants.HV_PAE]:
557
      config.write("pae = 1\n")
558
    else:
559
      config.write("pae = 0\n")
560
    if instance.hvparams[constants.HV_ACPI]:
561
      config.write("acpi = 1\n")
562
    else:
563
      config.write("acpi = 0\n")
564
    config.write("apic = 1\n")
565
    arch = os.uname()[4]
566
    if '64' in arch:
567
      config.write("device_model = '/usr/lib64/xen/bin/qemu-dm'\n")
568
    else:
569
      config.write("device_model = '/usr/lib/xen/bin/qemu-dm'\n")
570
    if instance.hvparams[constants.HV_BOOT_ORDER] is None:
571
      config.write("boot = '%s'\n" % constants.HT_HVM_DEFAULT_BOOT_ORDER)
572
    else:
573
      config.write("boot = '%s'\n" % instance.hvparams["boot_order"])
574
    config.write("sdl = 0\n")
575
    config.write("usb = 1\n")
576
    config.write("usbdevice = 'tablet'\n")
577
    config.write("vnc = 1\n")
578
    if instance.hvparams[constants.HV_VNC_BIND_ADDRESS] is None:
579
      config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
580
    else:
581
      config.write("vnclisten = '%s'\n" %
582
                   instance.hvparams["vnc_bind_address"])
583

    
584
    if instance.network_port > constants.HT_HVM_VNC_BASE_PORT:
585
      display = instance.network_port - constants.HT_HVM_VNC_BASE_PORT
586
      config.write("vncdisplay = %s\n" % display)
587
      config.write("vncunused = 0\n")
588
    else:
589
      config.write("# vncdisplay = 1\n")
590
      config.write("vncunused = 1\n")
591

    
592
    try:
593
      password = utils.ReadFile(constants.VNC_PASSWORD_FILE)
594
    except EnvironmentError, err:
595
      raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
596
                                   (constants.VNC_PASSWORD_FILE, err))
597

    
598
    config.write("vncpasswd = '%s'\n" % password.rstrip())
599

    
600
    config.write("serial = 'pty'\n")
601
    config.write("localtime = 1\n")
602

    
603
    vif_data = []
604
    nic_type = instance.hvparams[constants.HV_NIC_TYPE]
605
    if nic_type is None:
606
      # ensure old instances don't change
607
      nic_type_str = ", type=ioemu"
608
    elif nic_type == constants.HT_HVM_DEV_PARAVIRTUAL:
609
      nic_type_str = ", type=paravirtualized"
610
    else:
611
      nic_type_str = ", model=%s, type=ioemu" % nic_type
612
    for nic in instance.nics:
613
      nic_str = "mac=%s, bridge=%s%s" % (nic.mac, nic.bridge, nic_type_str)
614
      ip = getattr(nic, "ip", None)
615
      if ip is not None:
616
        nic_str += ", ip=%s" % ip
617
      vif_data.append("'%s'" % nic_str)
618

    
619
    config.write("vif = [%s]\n" % ",".join(vif_data))
620
    disk_data = cls._GetConfigFileDiskData(instance.disk_template,
621
                                            block_devices)
622
    disk_type = instance.hvparams[constants.HV_DISK_TYPE]
623
    if disk_type in (None, constants.HT_HVM_DEV_IOEMU):
624
      replacement = ",ioemu:hd"
625
    else:
626
      replacement = ",hd"
627
    disk_data = [line.replace(",sd", replacement) for line in disk_data]
628
    iso_path = instance.hvparams[constants.HV_CDROM_IMAGE_PATH]
629
    if iso_path:
630
      iso = "'file:%s,hdc:cdrom,r'" % iso_path
631
      disk_data.append(iso)
632

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

    
635
    config.write("on_poweroff = 'destroy'\n")
636
    config.write("on_reboot = 'restart'\n")
637
    config.write("on_crash = 'restart'\n")
638
    if extra_args:
639
      config.write("extra = '%s'\n" % extra_args)
640
    # just in case it exists
641
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
642
    try:
643
      utils.WriteFile("/etc/xen/%s" % instance.name,
644
                      data=config.getvalue())
645
    except EnvironmentError, err:
646
      raise errors.HypervisorError("Cannot write Xen instance confile"
647
                                   " file /etc/xen/%s: %s" %
648
                                   (instance.name, err))
649

    
650
    return True