Package ganeti :: Package hypervisor :: Module hv_xen
[hide private]
[frames] | no frames]

Source Code for Module ganeti.hypervisor.hv_xen

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2006, 2007, 2008, 2009, 2010 Google Inc. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, but 
 12  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 14  # General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the Free Software 
 18  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
 19  # 02110-1301, USA. 
 20   
 21   
 22  """Xen hypervisors 
 23   
 24  """ 
 25   
 26  import logging 
 27  from cStringIO import StringIO 
 28   
 29  from ganeti import constants 
 30  from ganeti import errors 
 31  from ganeti import utils 
 32  from ganeti.hypervisor import hv_base 
 33  from ganeti import netutils 
 34  from ganeti import objects 
35 36 37 -class XenHypervisor(hv_base.BaseHypervisor):
38 """Xen generic hypervisor interface 39 40 This is the Xen base class used for both Xen PVM and HVM. It contains 41 all the functionality that is identical for both. 42 43 """ 44 CAN_MIGRATE = True 45 REBOOT_RETRY_COUNT = 60 46 REBOOT_RETRY_INTERVAL = 10 47 48 ANCILLARY_FILES = [ 49 '/etc/xen/xend-config.sxp', 50 '/etc/xen/scripts/vif-bridge', 51 ] 52 53 @classmethod
54 - def _WriteConfigFile(cls, instance, block_devices):
55 """Write the Xen config file for the instance. 56 57 """ 58 raise NotImplementedError
59 60 @staticmethod
61 - def _WriteConfigFileStatic(instance_name, data):
62 """Write the Xen config file for the instance. 63 64 This version of the function just writes the config file from static data. 65 66 """ 67 utils.WriteFile("/etc/xen/%s" % instance_name, data=data)
68 69 @staticmethod
70 - def _ReadConfigFile(instance_name):
71 """Returns the contents of the instance config file. 72 73 """ 74 try: 75 file_content = utils.ReadFile("/etc/xen/%s" % instance_name) 76 except EnvironmentError, err: 77 raise errors.HypervisorError("Failed to load Xen config file: %s" % err) 78 return file_content
79 80 @staticmethod
81 - def _RemoveConfigFile(instance_name):
82 """Remove the xen configuration file. 83 84 """ 85 utils.RemoveFile("/etc/xen/%s" % instance_name)
86 87 @staticmethod
88 - def _RunXmList(xmlist_errors):
89 """Helper function for L{_GetXMList} to run "xm list". 90 91 """ 92 result = utils.RunCmd(["xm", "list"]) 93 if result.failed: 94 logging.error("xm list failed (%s): %s", result.fail_reason, 95 result.output) 96 xmlist_errors.append(result) 97 raise utils.RetryAgain() 98 99 # skip over the heading 100 return result.stdout.splitlines()[1:]
101 102 @classmethod
103 - def _GetXMList(cls, include_node):
104 """Return the list of running instances. 105 106 If the include_node argument is True, then we return information 107 for dom0 also, otherwise we filter that from the return value. 108 109 @return: list of (name, id, memory, vcpus, state, time spent) 110 111 """ 112 xmlist_errors = [] 113 try: 114 lines = utils.Retry(cls._RunXmList, 1, 5, args=(xmlist_errors, )) 115 except utils.RetryTimeout: 116 if xmlist_errors: 117 xmlist_result = xmlist_errors.pop() 118 119 errmsg = ("xm list failed, timeout exceeded (%s): %s" % 120 (xmlist_result.fail_reason, xmlist_result.output)) 121 else: 122 errmsg = "xm list failed" 123 124 raise errors.HypervisorError(errmsg) 125 126 result = [] 127 for line in lines: 128 # The format of lines is: 129 # Name ID Mem(MiB) VCPUs State Time(s) 130 # Domain-0 0 3418 4 r----- 266.2 131 data = line.split() 132 if len(data) != 6: 133 raise errors.HypervisorError("Can't parse output of xm list," 134 " line: %s" % line) 135 try: 136 data[1] = int(data[1]) 137 data[2] = int(data[2]) 138 data[3] = int(data[3]) 139 data[5] = float(data[5]) 140 except (TypeError, ValueError), err: 141 raise errors.HypervisorError("Can't parse output of xm list," 142 " line: %s, error: %s" % (line, err)) 143 144 # skip the Domain-0 (optional) 145 if include_node or data[0] != 'Domain-0': 146 result.append(data) 147 148 return result
149
150 - def ListInstances(self):
151 """Get the list of running instances. 152 153 """ 154 xm_list = self._GetXMList(False) 155 names = [info[0] for info in xm_list] 156 return names
157
158 - def GetInstanceInfo(self, instance_name):
159 """Get instance properties. 160 161 @param instance_name: the instance name 162 163 @return: tuple (name, id, memory, vcpus, stat, times) 164 165 """ 166 xm_list = self._GetXMList(instance_name=="Domain-0") 167 result = None 168 for data in xm_list: 169 if data[0] == instance_name: 170 result = data 171 break 172 return result
173
174 - def GetAllInstancesInfo(self):
175 """Get properties of all instances. 176 177 @return: list of tuples (name, id, memory, vcpus, stat, times) 178 179 """ 180 xm_list = self._GetXMList(False) 181 return xm_list
182
183 - def StartInstance(self, instance, block_devices):
184 """Start an instance. 185 186 """ 187 self._WriteConfigFile(instance, block_devices) 188 result = utils.RunCmd(["xm", "create", instance.name]) 189 190 if result.failed: 191 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" % 192 (instance.name, result.fail_reason, 193 result.output))
194
195 - def StopInstance(self, instance, force=False, retry=False, name=None):
196 """Stop an instance. 197 198 """ 199 if name is None: 200 name = instance.name 201 self._RemoveConfigFile(name) 202 if force: 203 command = ["xm", "destroy", name] 204 else: 205 command = ["xm", "shutdown", name] 206 result = utils.RunCmd(command) 207 208 if result.failed: 209 raise errors.HypervisorError("Failed to stop instance %s: %s, %s" % 210 (name, result.fail_reason, result.output))
211
212 - def RebootInstance(self, instance):
213 """Reboot an instance. 214 215 """ 216 ini_info = self.GetInstanceInfo(instance.name) 217 218 if ini_info is None: 219 raise errors.HypervisorError("Failed to reboot instance %s," 220 " not running" % instance.name) 221 222 result = utils.RunCmd(["xm", "reboot", instance.name]) 223 if result.failed: 224 raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" % 225 (instance.name, result.fail_reason, 226 result.output)) 227 228 def _CheckInstance(): 229 new_info = self.GetInstanceInfo(instance.name) 230 231 # check if the domain ID has changed or the run time has decreased 232 if (new_info is not None and 233 (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])): 234 return 235 236 raise utils.RetryAgain()
237 238 try: 239 utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL, 240 self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT) 241 except utils.RetryTimeout: 242 raise errors.HypervisorError("Failed to reboot instance %s: instance" 243 " did not reboot in the expected interval" % 244 (instance.name, ))
245
246 - def GetNodeInfo(self):
247 """Return information about the node. 248 249 @return: a dict with the following keys (memory values in MiB): 250 - memory_total: the total memory size on the node 251 - memory_free: the available memory on the node for instances 252 - memory_dom0: the memory used by the node itself, if available 253 - nr_cpus: total number of CPUs 254 - nr_nodes: in a NUMA system, the number of domains 255 - nr_sockets: the number of physical CPU sockets in the node 256 257 """ 258 # note: in xen 3, memory has changed to total_memory 259 result = utils.RunCmd(["xm", "info"]) 260 if result.failed: 261 logging.error("Can't run 'xm info' (%s): %s", result.fail_reason, 262 result.output) 263 return None 264 265 xmoutput = result.stdout.splitlines() 266 result = {} 267 cores_per_socket = threads_per_core = nr_cpus = None 268 for line in xmoutput: 269 splitfields = line.split(":", 1) 270 271 if len(splitfields) > 1: 272 key = splitfields[0].strip() 273 val = splitfields[1].strip() 274 if key == 'memory' or key == 'total_memory': 275 result['memory_total'] = int(val) 276 elif key == 'free_memory': 277 result['memory_free'] = int(val) 278 elif key == 'nr_cpus': 279 nr_cpus = result['cpu_total'] = int(val) 280 elif key == 'nr_nodes': 281 result['cpu_nodes'] = int(val) 282 elif key == 'cores_per_socket': 283 cores_per_socket = int(val) 284 elif key == 'threads_per_core': 285 threads_per_core = int(val) 286 287 if (cores_per_socket is not None and 288 threads_per_core is not None and nr_cpus is not None): 289 result['cpu_sockets'] = nr_cpus / (cores_per_socket * threads_per_core) 290 291 dom0_info = self.GetInstanceInfo("Domain-0") 292 if dom0_info is not None: 293 result['memory_dom0'] = dom0_info[2] 294 295 return result
296 297 @classmethod
298 - def GetInstanceConsole(cls, instance, hvparams, beparams):
299 """Return a command for connecting to the console of an instance. 300 301 """ 302 return objects.InstanceConsole(instance=instance.name, 303 kind=constants.CONS_SSH, 304 host=instance.primary_node, 305 user=constants.GANETI_RUNAS, 306 command=["xm", "console", instance.name])
307
308 - def Verify(self):
309 """Verify the hypervisor. 310 311 For Xen, this verifies that the xend process is running. 312 313 """ 314 result = utils.RunCmd(["xm", "info"]) 315 if result.failed: 316 return "'xm info' failed: %s, %s" % (result.fail_reason, result.output)
317 318 @staticmethod
319 - def _GetConfigFileDiskData(block_devices, blockdev_prefix):
320 """Get disk directive for xen config file. 321 322 This method builds the xen config disk directive according to the 323 given disk_template and block_devices. 324 325 @param block_devices: list of tuples (cfdev, rldev): 326 - cfdev: dict containing ganeti config disk part 327 - rldev: ganeti.bdev.BlockDev object 328 @param blockdev_prefix: a string containing blockdevice prefix, 329 e.g. "sd" for /dev/sda 330 331 @return: string containing disk directive for xen instance config file 332 333 """ 334 FILE_DRIVER_MAP = { 335 constants.FD_LOOP: "file", 336 constants.FD_BLKTAP: "tap:aio", 337 } 338 disk_data = [] 339 if len(block_devices) > 24: 340 # 'z' - 'a' = 24 341 raise errors.HypervisorError("Too many disks") 342 namespace = [blockdev_prefix + chr(i + ord('a')) for i in range(24)] 343 for sd_name, (cfdev, dev_path) in zip(namespace, block_devices): 344 if cfdev.mode == constants.DISK_RDWR: 345 mode = "w" 346 else: 347 mode = "r" 348 if cfdev.dev_type == constants.LD_FILE: 349 line = "'%s:%s,%s,%s'" % (FILE_DRIVER_MAP[cfdev.physical_id[0]], 350 dev_path, sd_name, mode) 351 else: 352 line = "'phy:%s,%s,%s'" % (dev_path, sd_name, mode) 353 disk_data.append(line) 354 355 return disk_data
356
357 - def MigrationInfo(self, instance):
358 """Get instance information to perform a migration. 359 360 @type instance: L{objects.Instance} 361 @param instance: instance to be migrated 362 @rtype: string 363 @return: content of the xen config file 364 365 """ 366 return self._ReadConfigFile(instance.name)
367
368 - def AcceptInstance(self, instance, info, target):
369 """Prepare to accept an instance. 370 371 @type instance: L{objects.Instance} 372 @param instance: instance to be accepted 373 @type info: string 374 @param info: content of the xen config file on the source node 375 @type target: string 376 @param target: target host (usually ip), on this node 377 378 """ 379 pass
380
381 - def FinalizeMigration(self, instance, info, success):
382 """Finalize an instance migration. 383 384 After a successful migration we write the xen config file. 385 We do nothing on a failure, as we did not change anything at accept time. 386 387 @type instance: L{objects.Instance} 388 @param instance: instance whose migration is being finalized 389 @type info: string 390 @param info: content of the xen config file on the source node 391 @type success: boolean 392 @param success: whether the migration was a success or a failure 393 394 """ 395 if success: 396 self._WriteConfigFileStatic(instance.name, info)
397
398 - def MigrateInstance(self, instance, target, live):
399 """Migrate an instance to a target node. 400 401 The migration will not be attempted if the instance is not 402 currently running. 403 404 @type instance: L{objects.Instance} 405 @param instance: the instance to be migrated 406 @type target: string 407 @param target: ip address of the target node 408 @type live: boolean 409 @param live: perform a live migration 410 411 """ 412 if self.GetInstanceInfo(instance.name) is None: 413 raise errors.HypervisorError("Instance not running, cannot migrate") 414 415 port = instance.hvparams[constants.HV_MIGRATION_PORT] 416 417 if not netutils.TcpPing(target, port, live_port_needed=True): 418 raise errors.HypervisorError("Remote host %s not listening on port" 419 " %s, cannot migrate" % (target, port)) 420 421 args = ["xm", "migrate", "-p", "%d" % port] 422 if live: 423 args.append("-l") 424 args.extend([instance.name, target]) 425 result = utils.RunCmd(args) 426 if result.failed: 427 raise errors.HypervisorError("Failed to migrate instance %s: %s" % 428 (instance.name, result.output)) 429 # remove old xen file after migration succeeded 430 try: 431 self._RemoveConfigFile(instance.name) 432 except EnvironmentError: 433 logging.exception("Failure while removing instance config file")
434 435 @classmethod
436 - def PowercycleNode(cls):
437 """Xen-specific powercycle. 438 439 This first does a Linux reboot (which triggers automatically a Xen 440 reboot), and if that fails it tries to do a Xen reboot. The reason 441 we don't try a Xen reboot first is that the xen reboot launches an 442 external command which connects to the Xen hypervisor, and that 443 won't work in case the root filesystem is broken and/or the xend 444 daemon is not working. 445 446 """ 447 try: 448 cls.LinuxPowercycle() 449 finally: 450 utils.RunCmd(["xm", "debug", "R"])
451
452 453 -class XenPvmHypervisor(XenHypervisor):
454 """Xen PVM hypervisor interface""" 455 456 PARAMETERS = { 457 constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK, 458 constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK, 459 constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK, 460 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK, 461 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK, 462 constants.HV_ROOT_PATH: hv_base.NO_CHECK, 463 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK, 464 constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK, 465 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK, 466 # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar). 467 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK, 468 } 469 470 @classmethod
471 - def _WriteConfigFile(cls, instance, block_devices):
472 """Write the Xen config file for the instance. 473 474 """ 475 hvp = instance.hvparams 476 config = StringIO() 477 config.write("# this is autogenerated by Ganeti, please do not edit\n#\n") 478 479 # if bootloader is True, use bootloader instead of kernel and ramdisk 480 # parameters. 481 if hvp[constants.HV_USE_BOOTLOADER]: 482 # bootloader handling 483 bootloader_path = hvp[constants.HV_BOOTLOADER_PATH] 484 if bootloader_path: 485 config.write("bootloader = '%s'\n" % bootloader_path) 486 else: 487 raise errors.HypervisorError("Bootloader enabled, but missing" 488 " bootloader path") 489 490 bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS] 491 if bootloader_args: 492 config.write("bootargs = '%s'\n" % bootloader_args) 493 else: 494 # kernel handling 495 kpath = hvp[constants.HV_KERNEL_PATH] 496 config.write("kernel = '%s'\n" % kpath) 497 498 # initrd handling 499 initrd_path = hvp[constants.HV_INITRD_PATH] 500 if initrd_path: 501 config.write("ramdisk = '%s'\n" % initrd_path) 502 503 # rest of the settings 504 config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY]) 505 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS]) 506 config.write("name = '%s'\n" % instance.name) 507 508 vif_data = [] 509 for nic in instance.nics: 510 nic_str = "mac=%s" % (nic.mac) 511 ip = getattr(nic, "ip", None) 512 if ip is not None: 513 nic_str += ", ip=%s" % ip 514 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED: 515 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK] 516 vif_data.append("'%s'" % nic_str) 517 518 disk_data = cls._GetConfigFileDiskData(block_devices, 519 hvp[constants.HV_BLOCKDEV_PREFIX]) 520 521 config.write("vif = [%s]\n" % ",".join(vif_data)) 522 config.write("disk = [%s]\n" % ",".join(disk_data)) 523 524 if hvp[constants.HV_ROOT_PATH]: 525 config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH]) 526 config.write("on_poweroff = 'destroy'\n") 527 config.write("on_reboot = 'restart'\n") 528 config.write("on_crash = 'restart'\n") 529 config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS]) 530 # just in case it exists 531 utils.RemoveFile("/etc/xen/auto/%s" % instance.name) 532 try: 533 utils.WriteFile("/etc/xen/%s" % instance.name, data=config.getvalue()) 534 except EnvironmentError, err: 535 raise errors.HypervisorError("Cannot write Xen instance confile" 536 " file /etc/xen/%s: %s" % 537 (instance.name, err)) 538 539 return True
540
541 542 -class XenHvmHypervisor(XenHypervisor):
543 """Xen HVM hypervisor interface""" 544 545 ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [ 546 constants.VNC_PASSWORD_FILE, 547 ] 548 549 PARAMETERS = { 550 constants.HV_ACPI: hv_base.NO_CHECK, 551 constants.HV_BOOT_ORDER: (True, ) + 552 (lambda x: x and len(x.strip("acdn")) == 0, 553 "Invalid boot order specified, must be one or more of [acdn]", 554 None, None), 555 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK, 556 constants.HV_DISK_TYPE: 557 hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES), 558 constants.HV_NIC_TYPE: 559 hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES), 560 constants.HV_PAE: hv_base.NO_CHECK, 561 constants.HV_VNC_BIND_ADDRESS: 562 (False, netutils.IP4Address.IsValid, 563 "VNC bind address is not a valid IP address", None, None), 564 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK, 565 constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK, 566 constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK, 567 constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK, 568 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK, 569 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK, 570 # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar). 571 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK, 572 } 573 574 @classmethod
575 - def _WriteConfigFile(cls, instance, block_devices):
576 """Create a Xen 3.1 HVM config file. 577 578 """ 579 hvp = instance.hvparams 580 581 config = StringIO() 582 config.write("# this is autogenerated by Ganeti, please do not edit\n#\n") 583 584 # kernel handling 585 kpath = hvp[constants.HV_KERNEL_PATH] 586 config.write("kernel = '%s'\n" % kpath) 587 588 config.write("builder = 'hvm'\n") 589 config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY]) 590 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS]) 591 config.write("name = '%s'\n" % instance.name) 592 if hvp[constants.HV_PAE]: 593 config.write("pae = 1\n") 594 else: 595 config.write("pae = 0\n") 596 if hvp[constants.HV_ACPI]: 597 config.write("acpi = 1\n") 598 else: 599 config.write("acpi = 0\n") 600 config.write("apic = 1\n") 601 config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL]) 602 config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER]) 603 config.write("sdl = 0\n") 604 config.write("usb = 1\n") 605 config.write("usbdevice = 'tablet'\n") 606 config.write("vnc = 1\n") 607 if hvp[constants.HV_VNC_BIND_ADDRESS] is None: 608 config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS) 609 else: 610 config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS]) 611 612 if instance.network_port > constants.VNC_BASE_PORT: 613 display = instance.network_port - constants.VNC_BASE_PORT 614 config.write("vncdisplay = %s\n" % display) 615 config.write("vncunused = 0\n") 616 else: 617 config.write("# vncdisplay = 1\n") 618 config.write("vncunused = 1\n") 619 620 vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE] 621 try: 622 password = utils.ReadFile(vnc_pwd_file) 623 except EnvironmentError, err: 624 raise errors.HypervisorError("Failed to open VNC password file %s: %s" % 625 (vnc_pwd_file, err)) 626 627 config.write("vncpasswd = '%s'\n" % password.rstrip()) 628 629 config.write("serial = 'pty'\n") 630 if hvp[constants.HV_USE_LOCALTIME]: 631 config.write("localtime = 1\n") 632 633 vif_data = [] 634 nic_type = hvp[constants.HV_NIC_TYPE] 635 if nic_type is None: 636 # ensure old instances don't change 637 nic_type_str = ", type=ioemu" 638 elif nic_type == constants.HT_NIC_PARAVIRTUAL: 639 nic_type_str = ", type=paravirtualized" 640 else: 641 nic_type_str = ", model=%s, type=ioemu" % nic_type 642 for nic in instance.nics: 643 nic_str = "mac=%s%s" % (nic.mac, nic_type_str) 644 ip = getattr(nic, "ip", None) 645 if ip is not None: 646 nic_str += ", ip=%s" % ip 647 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED: 648 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK] 649 vif_data.append("'%s'" % nic_str) 650 651 config.write("vif = [%s]\n" % ",".join(vif_data)) 652 653 disk_data = cls._GetConfigFileDiskData(block_devices, 654 hvp[constants.HV_BLOCKDEV_PREFIX]) 655 656 iso_path = hvp[constants.HV_CDROM_IMAGE_PATH] 657 if iso_path: 658 iso = "'file:%s,hdc:cdrom,r'" % iso_path 659 disk_data.append(iso) 660 661 config.write("disk = [%s]\n" % (",".join(disk_data))) 662 663 config.write("on_poweroff = 'destroy'\n") 664 config.write("on_reboot = 'restart'\n") 665 config.write("on_crash = 'restart'\n") 666 # just in case it exists 667 utils.RemoveFile("/etc/xen/auto/%s" % instance.name) 668 try: 669 utils.WriteFile("/etc/xen/%s" % instance.name, 670 data=config.getvalue()) 671 except EnvironmentError, err: 672 raise errors.HypervisorError("Cannot write Xen instance confile" 673 " file /etc/xen/%s: %s" % 674 (instance.name, err)) 675 676 return True
677