Xen handle domain shutdown
[ganeti-github.git] / lib / hypervisor / hv_xen.py
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{_GetAllInstanceList} to retrieve the list
88 of instances 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[4] = _XenToHypervisorInstanceState(data[4])
135 data[5] = float(data[5])
136 except (TypeError, ValueError), err:
137 raise errors.HypervisorError("Can't parse instance list,"
138 " line: %s, error: %s" % (line, err))
139
140 # skip the Domain-0 (optional)
141 if include_node or data[0] != _DOM0_NAME:
142 result.append(data)
143
144 return result
145
146
147 def _GetAllInstanceList(fn, include_node, _timeout=5):
148 """Return the list of instances including running and shutdown.
149
150 See L{_RunInstanceList} and L{_ParseInstanceList} for parameter details.
151
152 """
153 instance_list_errors = []
154 try:
155 lines = utils.Retry(_RunInstanceList, (0.3, 1.5, 1.0), _timeout,
156 args=(fn, instance_list_errors))
157 except utils.RetryTimeout:
158 if instance_list_errors:
159 instance_list_result = instance_list_errors.pop()
160
161 errmsg = ("listing instances failed, timeout exceeded (%s): %s" %
162 (instance_list_result.fail_reason, instance_list_result.output))
163 else:
164 errmsg = "listing instances failed"
165
166 raise errors.HypervisorError(errmsg)
167
168 return _ParseInstanceList(lines, include_node)
169
170
171 def _IsInstanceRunning(instance_info):
172 return instance_info == "r-----" \
173 or instance_info == "-b----"
174
175
176 def _IsInstanceShutdown(instance_info):
177 return instance_info == "---s--"
178
179
180 def _XenToHypervisorInstanceState(instance_info):
181 if _IsInstanceRunning(instance_info):
182 return hv_base.HvInstanceState.RUNNING
183 elif _IsInstanceShutdown(instance_info):
184 return hv_base.HvInstanceState.SHUTDOWN
185 else:
186 raise errors.HypervisorError("hv_xen._XenToHypervisorInstanceState:"
187 " unhandled Xen instance state '%s'" %
188 instance_info)
189
190
191 def _GetRunningInstanceList(fn, include_node, _timeout=5):
192 """Return the list of running instances.
193
194 See L{_GetAllInstanceList} for parameter details.
195
196 """
197 instances = _GetAllInstanceList(fn, include_node, _timeout)
198 return [i for i in instances if hv_base.HvInstanceState.IsRunning(i[4])]
199
200
201 def _GetShutdownInstanceList(fn, include_node, _timeout=5):
202 """Return the list of shutdown instances.
203
204 See L{_GetAllInstanceList} for parameter details.
205
206 """
207 instances = _GetAllInstanceList(fn, include_node, _timeout)
208 return [i for i in instances if hv_base.HvInstanceState.IsShutdown(i[4])]
209
210
211 def _ParseNodeInfo(info):
212 """Return information about the node.
213
214 @return: a dict with the following keys (memory values in MiB):
215 - memory_total: the total memory size on the node
216 - memory_free: the available memory on the node for instances
217 - nr_cpus: total number of CPUs
218 - nr_nodes: in a NUMA system, the number of domains
219 - nr_sockets: the number of physical CPU sockets in the node
220 - hv_version: the hypervisor version in the form (major, minor)
221
222 """
223 result = {}
224 cores_per_socket = threads_per_core = nr_cpus = None
225 xen_major, xen_minor = None, None
226 memory_total = None
227 memory_free = None
228
229 for line in info.splitlines():
230 fields = line.split(":", 1)
231
232 if len(fields) < 2:
233 continue
234
235 (key, val) = map(lambda s: s.strip(), fields)
236
237 # Note: in Xen 3, memory has changed to total_memory
238 if key in ("memory", "total_memory"):
239 memory_total = int(val)
240 elif key == "free_memory":
241 memory_free = int(val)
242 elif key == "nr_cpus":
243 nr_cpus = result["cpu_total"] = int(val)
244 elif key == "nr_nodes":
245 result["cpu_nodes"] = int(val)
246 elif key == "cores_per_socket":
247 cores_per_socket = int(val)
248 elif key == "threads_per_core":
249 threads_per_core = int(val)
250 elif key == "xen_major":
251 xen_major = int(val)
252 elif key == "xen_minor":
253 xen_minor = int(val)
254
255 if None not in [cores_per_socket, threads_per_core, nr_cpus]:
256 result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
257
258 if memory_free is not None:
259 result["memory_free"] = memory_free
260
261 if memory_total is not None:
262 result["memory_total"] = memory_total
263
264 if not (xen_major is None or xen_minor is None):
265 result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
266
267 return result
268
269
270 def _MergeInstanceInfo(info, instance_list):
271 """Updates node information from L{_ParseNodeInfo} with instance info.
272
273 @type info: dict
274 @param info: Result from L{_ParseNodeInfo}
275 @type instance_list: list of tuples
276 @param instance_list: list of instance information; one tuple per instance
277 @rtype: dict
278
279 """
280 total_instmem = 0
281
282 for (name, _, mem, vcpus, _, _) in instance_list:
283 if name == _DOM0_NAME:
284 info["memory_dom0"] = mem
285 info["cpu_dom0"] = vcpus
286
287 # Include Dom0 in total memory usage
288 total_instmem += mem
289
290 memory_free = info.get("memory_free")
291 memory_total = info.get("memory_total")
292
293 # Calculate memory used by hypervisor
294 if None not in [memory_total, memory_free, total_instmem]:
295 info["memory_hv"] = memory_total - memory_free - total_instmem
296
297 return info
298
299
300 def _GetNodeInfo(info, instance_list):
301 """Combines L{_MergeInstanceInfo} and L{_ParseNodeInfo}.
302
303 @type instance_list: list of tuples
304 @param instance_list: list of instance information; one tuple per instance
305
306 """
307 return _MergeInstanceInfo(_ParseNodeInfo(info), instance_list)
308
309
310 def _GetConfigFileDiskData(block_devices, blockdev_prefix,
311 _letters=_DISK_LETTERS):
312 """Get disk directives for Xen config file.
313
314 This method builds the xen config disk directive according to the
315 given disk_template and block_devices.
316
317 @param block_devices: list of tuples (cfdev, rldev):
318 - cfdev: dict containing ganeti config disk part
319 - rldev: ganeti.block.bdev.BlockDev object
320 @param blockdev_prefix: a string containing blockdevice prefix,
321 e.g. "sd" for /dev/sda
322
323 @return: string containing disk directive for xen instance config file
324
325 """
326 if len(block_devices) > len(_letters):
327 raise errors.HypervisorError("Too many disks")
328
329 disk_data = []
330
331 for sd_suffix, (cfdev, dev_path, _) in zip(_letters, block_devices):
332 sd_name = blockdev_prefix + sd_suffix
333
334 if cfdev.mode == constants.DISK_RDWR:
335 mode = "w"
336 else:
337 mode = "r"
338
339 if cfdev.dev_type in constants.DTS_FILEBASED:
340 driver = _FILE_DRIVER_MAP[cfdev.logical_id[0]]
341 else:
342 driver = "phy"
343
344 disk_data.append("'%s:%s,%s,%s'" % (driver, dev_path, sd_name, mode))
345
346 return disk_data
347
348
349 def _QuoteCpuidField(data):
350 """Add quotes around the CPUID field only if necessary.
351
352 Xen CPUID fields come in two shapes: LIBXL strings, which need quotes around
353 them, and lists of XEND strings, which don't.
354
355 @param data: Either type of parameter.
356 @return: The quoted version thereof.
357
358 """
359 return "'%s'" % data if data.startswith("host") else data
360
361
362 class XenHypervisor(hv_base.BaseHypervisor):
363 """Xen generic hypervisor interface
364
365 This is the Xen base class used for both Xen PVM and HVM. It contains
366 all the functionality that is identical for both.
367
368 """
369 CAN_MIGRATE = True
370 REBOOT_RETRY_COUNT = 60
371 REBOOT_RETRY_INTERVAL = 10
372 _ROOT_DIR = pathutils.RUN_DIR + "/xen-hypervisor"
373 _NICS_DIR = _ROOT_DIR + "/nic" # contains NICs' info
374 _DIRS = [_ROOT_DIR, _NICS_DIR]
375
376 ANCILLARY_FILES = [
377 XEND_CONFIG_FILE,
378 XL_CONFIG_FILE,
379 VIF_BRIDGE_SCRIPT,
380 ]
381 ANCILLARY_FILES_OPT = [
382 XL_CONFIG_FILE,
383 ]
384
385 def __init__(self, _cfgdir=None, _run_cmd_fn=None, _cmd=None):
386 hv_base.BaseHypervisor.__init__(self)
387
388 if _cfgdir is None:
389 self._cfgdir = pathutils.XEN_CONFIG_DIR
390 else:
391 self._cfgdir = _cfgdir
392
393 if _run_cmd_fn is None:
394 self._run_cmd_fn = utils.RunCmd
395 else:
396 self._run_cmd_fn = _run_cmd_fn
397
398 self._cmd = _cmd
399
400 def _GetCommand(self, hvparams):
401 """Returns Xen command to use.
402
403 @type hvparams: dict of strings
404 @param hvparams: hypervisor parameters
405
406 """
407 if self._cmd is None:
408 if hvparams is None or constants.HV_XEN_CMD not in hvparams:
409 raise errors.HypervisorError("Cannot determine xen command.")
410 else:
411 cmd = hvparams[constants.HV_XEN_CMD]
412 else:
413 cmd = self._cmd
414
415 if cmd not in constants.KNOWN_XEN_COMMANDS:
416 raise errors.ProgrammerError("Unknown Xen command '%s'" % cmd)
417
418 return cmd
419
420 def _RunXen(self, args, hvparams):
421 """Wrapper around L{utils.process.RunCmd} to run Xen command.
422
423 @type hvparams: dict of strings
424 @param hvparams: dictionary of hypervisor params
425 @see: L{utils.process.RunCmd}
426
427 """
428 cmd = [self._GetCommand(hvparams)]
429 cmd.extend(args)
430
431 return self._run_cmd_fn(cmd)
432
433 def _ConfigFileName(self, instance_name):
434 """Get the config file name for an instance.
435
436 @param instance_name: instance name
437 @type instance_name: str
438 @return: fully qualified path to instance config file
439 @rtype: str
440
441 """
442 return utils.PathJoin(self._cfgdir, instance_name)
443
444 @classmethod
445 def _WriteNICInfoFile(cls, instance_name, idx, nic):
446 """Write the Xen config file for the instance.
447
448 This version of the function just writes the config file from static data.
449
450 """
451 dirs = [(dname, constants.RUN_DIRS_MODE)
452 for dname in cls._DIRS + [cls._InstanceNICDir(instance_name)]]
453 utils.EnsureDirs(dirs)
454
455 cfg_file = cls._InstanceNICFile(instance_name, idx)
456 data = StringIO()
457
458 if nic.netinfo:
459 netinfo = objects.Network.FromDict(nic.netinfo)
460 data.write("NETWORK_NAME=%s\n" % netinfo.name)
461 if netinfo.network:
462 data.write("NETWORK_SUBNET=%s\n" % netinfo.network)
463 if netinfo.gateway:
464 data.write("NETWORK_GATEWAY=%s\n" % netinfo.gateway)
465 if netinfo.network6:
466 data.write("NETWORK_SUBNET6=%s\n" % netinfo.network6)
467 if netinfo.gateway6:
468 data.write("NETWORK_GATEWAY6=%s\n" % netinfo.gateway6)
469 if netinfo.mac_prefix:
470 data.write("NETWORK_MAC_PREFIX=%s\n" % netinfo.mac_prefix)
471 if netinfo.tags:
472 data.write("NETWORK_TAGS=%s\n" % r"\ ".join(netinfo.tags))
473
474 data.write("MAC=%s\n" % nic.mac)
475 data.write("IP=%s\n" % nic.ip)
476 data.write("MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
477 data.write("LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
478
479 try:
480 utils.WriteFile(cfg_file, data=data.getvalue())
481 except EnvironmentError, err:
482 raise errors.HypervisorError("Cannot write Xen instance configuration"
483 " file %s: %s" % (cfg_file, err))
484
485 @classmethod
486 def _InstanceNICDir(cls, instance_name):
487 """Returns the directory holding the tap device files for a given instance.
488
489 """
490 return utils.PathJoin(cls._NICS_DIR, instance_name)
491
492 @classmethod
493 def _InstanceNICFile(cls, instance_name, seq):
494 """Returns the name of the file containing the tap device for a given NIC
495
496 """
497 return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
498
499 @classmethod
500 def _GetConfig(cls, instance, startup_memory, block_devices):
501 """Build Xen configuration for an instance.
502
503 """
504 raise NotImplementedError
505
506 def _WriteConfigFile(self, instance_name, data):
507 """Write the Xen config file for the instance.
508
509 This version of the function just writes the config file from static data.
510
511 """
512 # just in case it exists
513 utils.RemoveFile(utils.PathJoin(self._cfgdir, "auto", instance_name))
514
515 cfg_file = self._ConfigFileName(instance_name)
516 try:
517 utils.WriteFile(cfg_file, data=data)
518 except EnvironmentError, err:
519 raise errors.HypervisorError("Cannot write Xen instance configuration"
520 " file %s: %s" % (cfg_file, err))
521
522 def _ReadConfigFile(self, instance_name):
523 """Returns the contents of the instance config file.
524
525 """
526 filename = self._ConfigFileName(instance_name)
527
528 try:
529 file_content = utils.ReadFile(filename)
530 except EnvironmentError, err:
531 raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
532
533 return file_content
534
535 def _RemoveConfigFile(self, instance_name):
536 """Remove the xen configuration file.
537
538 """
539 utils.RemoveFile(self._ConfigFileName(instance_name))
540 try:
541 shutil.rmtree(self._InstanceNICDir(instance_name))
542 except OSError, err:
543 if err.errno != errno.ENOENT:
544 raise
545
546 def _StashConfigFile(self, instance_name):
547 """Move the Xen config file to the log directory and return its new path.
548
549 """
550 old_filename = self._ConfigFileName(instance_name)
551 base = ("%s-%s" %
552 (instance_name, utils.TimestampForFilename()))
553 new_filename = utils.PathJoin(pathutils.LOG_XEN_DIR, base)
554 utils.RenameFile(old_filename, new_filename)
555 return new_filename
556
557 def _GetInstanceList(self, include_node, hvparams):
558 """Wrapper around module level L{_GetAllInstanceList}.
559
560 @type hvparams: dict of strings
561 @param hvparams: hypervisor parameters to be used on this node
562
563 """
564 return _GetAllInstanceList(lambda: self._RunXen(["list"], hvparams),
565 include_node)
566
567 def ListInstances(self, hvparams=None):
568 """Get the list of running instances.
569
570 """
571 instance_list = self._GetInstanceList(False, hvparams)
572 names = [info[0] for info in instance_list]
573 return names
574
575 def GetInstanceInfo(self, instance_name, hvparams=None):
576 """Get instance properties.
577
578 @type instance_name: string
579 @param instance_name: the instance name
580 @type hvparams: dict of strings
581 @param hvparams: the instance's hypervisor params
582
583 @return: tuple (name, id, memory, vcpus, stat, times)
584
585 """
586 instance_list = self._GetInstanceList(instance_name == _DOM0_NAME, hvparams)
587 result = None
588 for data in instance_list:
589 if data[0] == instance_name:
590 result = data
591 break
592 return result
593
594 def GetAllInstancesInfo(self, hvparams=None):
595 """Get properties of all instances.
596
597 @type hvparams: dict of strings
598 @param hvparams: hypervisor parameters
599
600 @rtype: (string, string, int, int, HypervisorInstanceState, int)
601 @return: list of tuples (name, id, memory, vcpus, state, times)
602
603 """
604 return self._GetInstanceList(False, hvparams)
605
606 def _MakeConfigFile(self, instance, startup_memory, block_devices):
607 """Gather configuration details and write to disk.
608
609 See L{_GetConfig} for arguments.
610
611 """
612 buf = StringIO()
613 buf.write("# Automatically generated by Ganeti. Do not edit!\n")
614 buf.write("\n")
615 buf.write(self._GetConfig(instance, startup_memory, block_devices))
616 buf.write("\n")
617
618 self._WriteConfigFile(instance.name, buf.getvalue())
619
620 def StartInstance(self, instance, block_devices, startup_paused):
621 """Start an instance.
622
623 """
624 startup_memory = self._InstanceStartupMemory(instance,
625 hvparams=instance.hvparams)
626
627 self._MakeConfigFile(instance, startup_memory, block_devices)
628
629 cmd = ["create"]
630 if startup_paused:
631 cmd.append("-p")
632 cmd.append(self._ConfigFileName(instance.name))
633
634 result = self._RunXen(cmd, instance.hvparams)
635 if result.failed:
636 # Move the Xen configuration file to the log directory to avoid
637 # leaving a stale config file behind.
638 stashed_config = self._StashConfigFile(instance.name)
639 raise errors.HypervisorError("Failed to start instance %s: %s (%s). Moved"
640 " config file to %s" %
641 (instance.name, result.fail_reason,
642 result.output, stashed_config))
643
644 def StopInstance(self, instance, force=False, retry=False, name=None):
645 """Stop an instance.
646
647 """
648 if name is None:
649 name = instance.name
650
651 return self._StopInstance(name, force, instance.hvparams)
652
653 def _ShutdownInstance(self, name, hvparams, instance_info):
654 # The '-w' flag waits for shutdown to complete
655 #
656 # In the case of shutdown, we want to wait until the shutdown
657 # process is complete because then we want to also destroy the
658 # domain, and we do not want to destroy the domain while it is
659 # shutting down.
660 if hv_base.HvInstanceState.IsShutdown(instance_info):
661 logging.info("Instance '%s' is already shutdown, skipping shutdown"
662 " command", name)
663 else:
664 result = self._RunXen(["shutdown", "-w", name], hvparams)
665 if result.failed:
666 raise errors.HypervisorError("Failed to shutdown instance %s: %s, %s" %
667 (name, result.fail_reason, result.output))
668
669 def _DestroyInstance(self, name, hvparams):
670 result = self._RunXen(["destroy", name], hvparams)
671 if result.failed:
672 raise errors.HypervisorError("Failed to destroy instance %s: %s, %s" %
673 (name, result.fail_reason, result.output))
674
675 def _StopInstance(self, name, force, hvparams):
676 """Stop an instance.
677
678 @type name: string
679 @param name: name of the instance to be shutdown
680 @type force: boolean
681 @param force: flag specifying whether shutdown should be forced
682 @type hvparams: dict of string
683 @param hvparams: hypervisor parameters of the instance
684
685 """
686 instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
687
688 if instance_info is None:
689 raise errors.HypervisorError("Failed to shutdown instance %s,"
690 " not running" % name)
691
692 if force:
693 self._DestroyInstance(name, hvparams)
694 else:
695 self._ShutdownInstance(name, hvparams, instance_info[4])
696 self._DestroyInstance(name, hvparams)
697
698 # Remove configuration file if stopping/starting instance was successful
699 self._RemoveConfigFile(name)
700
701 def RebootInstance(self, instance):
702 """Reboot an instance.
703
704 """
705 ini_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
706
707 if ini_info is None:
708 raise errors.HypervisorError("Failed to reboot instance %s,"
709 " not running" % instance.name)
710
711 result = self._RunXen(["reboot", instance.name], instance.hvparams)
712 if result.failed:
713 raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
714 (instance.name, result.fail_reason,
715 result.output))
716
717 def _CheckInstance():
718 new_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
719
720 # check if the domain ID has changed or the run time has decreased
721 if (new_info is not None and
722 (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])):
723 return
724
725 raise utils.RetryAgain()
726
727 try:
728 utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
729 self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
730 except utils.RetryTimeout:
731 raise errors.HypervisorError("Failed to reboot instance %s: instance"
732 " did not reboot in the expected interval" %
733 (instance.name, ))
734
735 def BalloonInstanceMemory(self, instance, mem):
736 """Balloon an instance memory to a certain value.
737
738 @type instance: L{objects.Instance}
739 @param instance: instance to be accepted
740 @type mem: int
741 @param mem: actual memory size to use for instance runtime
742
743 """
744 result = self._RunXen(["mem-set", instance.name, mem], instance.hvparams)
745 if result.failed:
746 raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" %
747 (instance.name, result.fail_reason,
748 result.output))
749
750 # Update configuration file
751 cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem]
752 cmd.append(self._ConfigFileName(instance.name))
753
754 result = utils.RunCmd(cmd)
755 if result.failed:
756 raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" %
757 (instance.name, result.fail_reason,
758 result.output))
759
760 def GetNodeInfo(self, hvparams=None):
761 """Return information about the node.
762
763 @see: L{_GetNodeInfo} and L{_ParseNodeInfo}
764
765 """
766 result = self._RunXen(["info"], hvparams)
767 if result.failed:
768 logging.error("Can't retrieve xen hypervisor information (%s): %s",
769 result.fail_reason, result.output)
770 return None
771
772 instance_list = self._GetInstanceList(True, hvparams)
773 return _GetNodeInfo(result.stdout, instance_list)
774
775 def GetInstanceConsole(self, instance, primary_node, hvparams, beparams):
776 """Return a command for connecting to the console of an instance.
777
778 """
779 xen_cmd = self._GetCommand(hvparams)
780 return objects.InstanceConsole(instance=instance.name,
781 kind=constants.CONS_SSH,
782 host=primary_node.name,
783 user=constants.SSH_CONSOLE_USER,
784 command=[pathutils.XEN_CONSOLE_WRAPPER,
785 xen_cmd, instance.name])
786
787 def Verify(self, hvparams=None):
788 """Verify the hypervisor.
789
790 For Xen, this verifies that the xend process is running.
791
792 @type hvparams: dict of strings
793 @param hvparams: hypervisor parameters to be verified against
794
795 @return: Problem description if something is wrong, C{None} otherwise
796
797 """
798 if hvparams is None:
799 return "Could not verify the hypervisor, because no hvparams were" \
800 " provided."
801
802 if constants.HV_XEN_CMD in hvparams:
803 xen_cmd = hvparams[constants.HV_XEN_CMD]
804 try:
805 self._CheckToolstack(xen_cmd)
806 except errors.HypervisorError:
807 return "The configured xen toolstack '%s' is not available on this" \
808 " node." % xen_cmd
809
810 result = self._RunXen(["info"], hvparams)
811 if result.failed:
812 return "Retrieving information from xen failed: %s, %s" % \
813 (result.fail_reason, result.output)
814
815 return None
816
817 def MigrationInfo(self, instance):
818 """Get instance information to perform a migration.
819
820 @type instance: L{objects.Instance}
821 @param instance: instance to be migrated
822 @rtype: string
823 @return: content of the xen config file
824
825 """
826 return self._ReadConfigFile(instance.name)
827
828 def AcceptInstance(self, instance, info, target):
829 """Prepare to accept an instance.
830
831 @type instance: L{objects.Instance}
832 @param instance: instance to be accepted
833 @type info: string
834 @param info: content of the xen config file on the source node
835 @type target: string
836 @param target: target host (usually ip), on this node
837
838 """
839 pass
840
841 def FinalizeMigrationDst(self, instance, info, success):
842 """Finalize an instance migration.
843
844 After a successful migration we write the xen config file.
845 We do nothing on a failure, as we did not change anything at accept time.
846
847 @type instance: L{objects.Instance}
848 @param instance: instance whose migration is being finalized
849 @type info: string
850 @param info: content of the xen config file on the source node
851 @type success: boolean
852 @param success: whether the migration was a success or a failure
853
854 """
855 if success:
856 self._WriteConfigFile(instance.name, info)
857
858 def MigrateInstance(self, cluster_name, instance, target, live):
859 """Migrate an instance to a target node.
860
861 The migration will not be attempted if the instance is not
862 currently running.
863
864 @type instance: L{objects.Instance}
865 @param instance: the instance to be migrated
866 @type target: string
867 @param target: ip address of the target node
868 @type live: boolean
869 @param live: perform a live migration
870
871 """
872 port = instance.hvparams[constants.HV_MIGRATION_PORT]
873
874 return self._MigrateInstance(cluster_name, instance.name, target, port,
875 live, instance.hvparams)
876
877 def _MigrateInstance(self, cluster_name, instance_name, target, port, live,
878 hvparams, _ping_fn=netutils.TcpPing):
879 """Migrate an instance to a target node.
880
881 @see: L{MigrateInstance} for details
882
883 """
884 if hvparams is None:
885 raise errors.HypervisorError("No hvparams provided.")
886
887 if self.GetInstanceInfo(instance_name, hvparams=hvparams) is None:
888 raise errors.HypervisorError("Instance not running, cannot migrate")
889
890 cmd = self._GetCommand(hvparams)
891
892 if (cmd == constants.XEN_CMD_XM and
893 not _ping_fn(target, port, live_port_needed=True)):
894 raise errors.HypervisorError("Remote host %s not listening on port"
895 " %s, cannot migrate" % (target, port))
896
897 args = ["migrate"]
898
899 if cmd == constants.XEN_CMD_XM:
900 args.extend(["-p", "%d" % port])
901 if live:
902 args.append("-l")
903
904 elif cmd == constants.XEN_CMD_XL:
905 args.extend([
906 "-s", constants.XL_SSH_CMD % cluster_name,
907 "-C", self._ConfigFileName(instance_name),
908 ])
909
910 else:
911 raise errors.HypervisorError("Unsupported Xen command: %s" % self._cmd)
912
913 args.extend([instance_name, target])
914
915 result = self._RunXen(args, hvparams)
916 if result.failed:
917 raise errors.HypervisorError("Failed to migrate instance %s: %s" %
918 (instance_name, result.output))
919
920 def FinalizeMigrationSource(self, instance, success, live):
921 """Finalize the instance migration on the source node.
922
923 @type instance: L{objects.Instance}
924 @param instance: the instance that was migrated
925 @type success: bool
926 @param success: whether the migration succeeded or not
927 @type live: bool
928 @param live: whether the user requested a live migration or not
929
930 """
931 # pylint: disable=W0613
932 if success:
933 # remove old xen file after migration succeeded
934 try:
935 self._RemoveConfigFile(instance.name)
936 except EnvironmentError:
937 logging.exception("Failure while removing instance config file")
938
939 def GetMigrationStatus(self, instance):
940 """Get the migration status
941
942 As MigrateInstance for Xen is still blocking, if this method is called it
943 means that MigrateInstance has completed successfully. So we can safely
944 assume that the migration was successful and notify this fact to the client.
945
946 @type instance: L{objects.Instance}
947 @param instance: the instance that is being migrated
948 @rtype: L{objects.MigrationStatus}
949 @return: the status of the current migration (one of
950 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
951 progress info that can be retrieved from the hypervisor
952
953 """
954 return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
955
956 def PowercycleNode(self, hvparams=None):
957 """Xen-specific powercycle.
958
959 This first does a Linux reboot (which triggers automatically a Xen
960 reboot), and if that fails it tries to do a Xen reboot. The reason
961 we don't try a Xen reboot first is that the xen reboot launches an
962 external command which connects to the Xen hypervisor, and that
963 won't work in case the root filesystem is broken and/or the xend
964 daemon is not working.
965
966 @type hvparams: dict of strings
967 @param hvparams: hypervisor params to be used on this node
968
969 """
970 try:
971 self.LinuxPowercycle()
972 finally:
973 xen_cmd = self._GetCommand(hvparams)
974 utils.RunCmd([xen_cmd, "debug", "R"])
975
976 def _CheckToolstack(self, xen_cmd):
977 """Check whether the given toolstack is available on the node.
978
979 @type xen_cmd: string
980 @param xen_cmd: xen command (e.g. 'xm' or 'xl')
981
982 """
983 binary_found = self._CheckToolstackBinary(xen_cmd)
984 if not binary_found:
985 raise errors.HypervisorError("No '%s' binary found on node." % xen_cmd)
986 elif xen_cmd == constants.XEN_CMD_XL:
987 if not self._CheckToolstackXlConfigured():
988 raise errors.HypervisorError("Toolstack '%s' is not enabled on this"
989 "node." % xen_cmd)
990
991 def _CheckToolstackBinary(self, xen_cmd):
992 """Checks whether the xen command's binary is found on the machine.
993
994 """
995 if xen_cmd not in constants.KNOWN_XEN_COMMANDS:
996 raise errors.HypervisorError("Unknown xen command '%s'." % xen_cmd)
997 result = self._run_cmd_fn(["which", xen_cmd])
998 return not result.failed
999
1000 def _CheckToolstackXlConfigured(self):
1001 """Checks whether xl is enabled on an xl-capable node.
1002
1003 @rtype: bool
1004 @returns: C{True} if 'xl' is enabled, C{False} otherwise
1005
1006 """
1007 result = self._run_cmd_fn([constants.XEN_CMD_XL, "help"])
1008 if not result.failed:
1009 return True
1010 elif result.failed:
1011 if "toolstack" in result.stderr:
1012 return False
1013 # xl fails for some other reason than the toolstack
1014 else:
1015 raise errors.HypervisorError("Cannot run xen ('%s'). Error: %s."
1016 % (constants.XEN_CMD_XL, result.stderr))
1017
1018
1019 def WriteXenConfigEvents(config, hvp):
1020 config.write("on_poweroff = 'preserve'\n")
1021 if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
1022 config.write("on_reboot = 'restart'\n")
1023 else:
1024 config.write("on_reboot = 'destroy'\n")
1025 config.write("on_crash = 'restart'\n")
1026
1027
1028 class XenPvmHypervisor(XenHypervisor):
1029 """Xen PVM hypervisor interface"""
1030
1031 PARAMETERS = {
1032 constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
1033 constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
1034 constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
1035 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1036 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
1037 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
1038 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
1039 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1040 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1041 # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
1042 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1043 constants.HV_REBOOT_BEHAVIOR:
1044 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1045 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1046 constants.HV_CPU_CAP: hv_base.OPT_NONNEGATIVE_INT_CHECK,
1047 constants.HV_CPU_WEIGHT:
1048 (False, lambda x: 0 < x < 65536, "invalid weight", None, None),
1049 constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1050 constants.HV_XEN_CMD:
1051 hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1052 constants.HV_XEN_CPUID: hv_base.NO_CHECK,
1053 constants.HV_SOUNDHW: hv_base.NO_CHECK,
1054 }
1055
1056 def _GetConfig(self, instance, startup_memory, block_devices):
1057 """Write the Xen config file for the instance.
1058
1059 """
1060 hvp = instance.hvparams
1061 config = StringIO()
1062 config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
1063
1064 # if bootloader is True, use bootloader instead of kernel and ramdisk
1065 # parameters.
1066 if hvp[constants.HV_USE_BOOTLOADER]:
1067 # bootloader handling
1068 bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
1069 if bootloader_path:
1070 config.write("bootloader = '%s'\n" % bootloader_path)
1071 else:
1072 raise errors.HypervisorError("Bootloader enabled, but missing"
1073 " bootloader path")
1074
1075 bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
1076 if bootloader_args:
1077 config.write("bootargs = '%s'\n" % bootloader_args)
1078 else:
1079 # kernel handling
1080 kpath = hvp[constants.HV_KERNEL_PATH]
1081 config.write("kernel = '%s'\n" % kpath)
1082
1083 # initrd handling
1084 initrd_path = hvp[constants.HV_INITRD_PATH]
1085 if initrd_path:
1086 config.write("ramdisk = '%s'\n" % initrd_path)
1087
1088 # rest of the settings
1089 config.write("memory = %d\n" % startup_memory)
1090 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1091 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1092 cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1093 if cpu_pinning:
1094 config.write("%s\n" % cpu_pinning)
1095 cpu_cap = hvp[constants.HV_CPU_CAP]
1096 if cpu_cap:
1097 config.write("cpu_cap=%d\n" % cpu_cap)
1098 cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1099 if cpu_weight:
1100 config.write("cpu_weight=%d\n" % cpu_weight)
1101
1102 config.write("name = '%s'\n" % instance.name)
1103
1104 vif_data = []
1105 for idx, nic in enumerate(instance.nics):
1106 nic_str = "mac=%s" % (nic.mac)
1107 ip = getattr(nic, "ip", None)
1108 if ip is not None:
1109 nic_str += ", ip=%s" % ip
1110 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1111 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1112 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_OVS:
1113 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1114 if nic.nicparams[constants.NIC_VLAN]:
1115 nic_str += "%s" % nic.nicparams[constants.NIC_VLAN]
1116 if hvp[constants.HV_VIF_SCRIPT]:
1117 nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
1118 vif_data.append("'%s'" % nic_str)
1119 self._WriteNICInfoFile(instance.name, idx, nic)
1120
1121 disk_data = \
1122 _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1123
1124 config.write("vif = [%s]\n" % ",".join(vif_data))
1125 config.write("disk = [%s]\n" % ",".join(disk_data))
1126
1127 if hvp[constants.HV_ROOT_PATH]:
1128 config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
1129
1130 WriteXenConfigEvents(config, hvp)
1131 config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
1132
1133 cpuid = hvp[constants.HV_XEN_CPUID]
1134 if cpuid:
1135 config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1136
1137 if hvp[constants.HV_SOUNDHW]:
1138 config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1139
1140 return config.getvalue()
1141
1142
1143 class XenHvmHypervisor(XenHypervisor):
1144 """Xen HVM hypervisor interface"""
1145
1146 ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
1147 pathutils.VNC_PASSWORD_FILE,
1148 ]
1149 ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [
1150 pathutils.VNC_PASSWORD_FILE,
1151 ]
1152
1153 PARAMETERS = {
1154 constants.HV_ACPI: hv_base.NO_CHECK,
1155 constants.HV_BOOT_ORDER: (True, ) +
1156 (lambda x: x and len(x.strip("acdn")) == 0,
1157 "Invalid boot order specified, must be one or more of [acdn]",
1158 None, None),
1159 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
1160 constants.HV_DISK_TYPE:
1161 hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
1162 constants.HV_NIC_TYPE:
1163 hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
1164 constants.HV_PAE: hv_base.NO_CHECK,
1165 constants.HV_VNC_BIND_ADDRESS:
1166 (False, netutils.IP4Address.IsValid,
1167 "VNC bind address is not a valid IP address", None, None),
1168 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1169 constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
1170 constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
1171 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1172 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1173 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
1174 # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
1175 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1176 # Add PCI passthrough
1177 constants.HV_PASSTHROUGH: hv_base.NO_CHECK,
1178 constants.HV_REBOOT_BEHAVIOR:
1179 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1180 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1181 constants.HV_CPU_CAP: hv_base.NO_CHECK,
1182 constants.HV_CPU_WEIGHT:
1183 (False, lambda x: 0 < x < 65535, "invalid weight", None, None),
1184 constants.HV_VIF_TYPE:
1185 hv_base.ParamInSet(False, constants.HT_HVM_VALID_VIF_TYPES),
1186 constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1187 constants.HV_VIRIDIAN: hv_base.NO_CHECK,
1188 constants.HV_XEN_CMD:
1189 hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1190 constants.HV_XEN_CPUID: hv_base.NO_CHECK,
1191 constants.HV_SOUNDHW: hv_base.NO_CHECK,
1192 }
1193
1194 def _GetConfig(self, instance, startup_memory, block_devices):
1195 """Create a Xen 3.1 HVM config file.
1196
1197 """
1198 hvp = instance.hvparams
1199
1200 config = StringIO()
1201
1202 # kernel handling
1203 kpath = hvp[constants.HV_KERNEL_PATH]
1204 config.write("kernel = '%s'\n" % kpath)
1205
1206 config.write("builder = 'hvm'\n")
1207 config.write("memory = %d\n" % startup_memory)
1208 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1209 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1210 cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1211 if cpu_pinning:
1212 config.write("%s\n" % cpu_pinning)
1213 cpu_cap = hvp[constants.HV_CPU_CAP]
1214 if cpu_cap:
1215 config.write("cpu_cap=%d\n" % cpu_cap)
1216 cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1217 if cpu_weight:
1218 config.write("cpu_weight=%d\n" % cpu_weight)
1219
1220 config.write("name = '%s'\n" % instance.name)
1221 if hvp[constants.HV_PAE]:
1222 config.write("pae = 1\n")
1223 else:
1224 config.write("pae = 0\n")
1225 if hvp[constants.HV_ACPI]:
1226 config.write("acpi = 1\n")
1227 else:
1228 config.write("acpi = 0\n")
1229 if hvp[constants.HV_VIRIDIAN]:
1230 config.write("viridian = 1\n")
1231 else:
1232 config.write("viridian = 0\n")
1233
1234 config.write("apic = 1\n")
1235 config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
1236 config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
1237 config.write("sdl = 0\n")
1238 config.write("usb = 1\n")
1239 config.write("usbdevice = 'tablet'\n")
1240 config.write("vnc = 1\n")
1241 if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
1242 config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
1243 else:
1244 config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
1245
1246 if instance.network_port > constants.VNC_BASE_PORT:
1247 display = instance.network_port - constants.VNC_BASE_PORT
1248 config.write("vncdisplay = %s\n" % display)
1249 config.write("vncunused = 0\n")
1250 else:
1251 config.write("# vncdisplay = 1\n")
1252 config.write("vncunused = 1\n")
1253
1254 vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
1255 try:
1256 password = utils.ReadFile(vnc_pwd_file)
1257 except EnvironmentError, err:
1258 raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
1259 (vnc_pwd_file, err))
1260
1261 config.write("vncpasswd = '%s'\n" % password.rstrip())
1262
1263 config.write("serial = 'pty'\n")
1264 if hvp[constants.HV_USE_LOCALTIME]:
1265 config.write("localtime = 1\n")
1266
1267 vif_data = []
1268 # Note: what is called 'nic_type' here, is used as value for the xen nic
1269 # vif config parameter 'model'. For the xen nic vif parameter 'type', we use
1270 # the 'vif_type' to avoid a clash of notation.
1271 nic_type = hvp[constants.HV_NIC_TYPE]
1272
1273 if nic_type is None:
1274 vif_type_str = ""
1275 if hvp[constants.HV_VIF_TYPE]:
1276 vif_type_str = ", type=%s" % hvp[constants.HV_VIF_TYPE]
1277 # ensure old instances don't change
1278 nic_type_str = vif_type_str
1279 elif nic_type == constants.HT_NIC_PARAVIRTUAL:
1280 nic_type_str = ", type=paravirtualized"
1281 else:
1282 # parameter 'model' is only valid with type 'ioemu'
1283 nic_type_str = ", model=%s, type=%s" % \
1284 (nic_type, constants.HT_HVM_VIF_IOEMU)
1285 for idx, nic in enumerate(instance.nics):
1286 nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
1287 ip = getattr(nic, "ip", None)
1288 if ip is not None:
1289 nic_str += ", ip=%s" % ip
1290 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1291 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1292 if hvp[constants.HV_VIF_SCRIPT]:
1293 nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
1294 vif_data.append("'%s'" % nic_str)
1295 self._WriteNICInfoFile(instance.name, idx, nic)
1296
1297 config.write("vif = [%s]\n" % ",".join(vif_data))
1298
1299 disk_data = \
1300 _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1301
1302 iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
1303 if iso_path:
1304 iso = "'file:%s,hdc:cdrom,r'" % iso_path
1305 disk_data.append(iso)
1306
1307 config.write("disk = [%s]\n" % (",".join(disk_data)))
1308 # Add PCI passthrough
1309 pci_pass_arr = []
1310 pci_pass = hvp[constants.HV_PASSTHROUGH]
1311 if pci_pass:
1312 pci_pass_arr = pci_pass.split(";")
1313 config.write("pci = %s\n" % pci_pass_arr)
1314
1315 WriteXenConfigEvents(config, hvp)
1316
1317 cpuid = hvp[constants.HV_XEN_CPUID]
1318 if cpuid:
1319 config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1320
1321 if hvp[constants.HV_SOUNDHW]:
1322 config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1323
1324 return config.getvalue()