Fix faulty comments / indentation
[ganeti-github.git] / lib / hypervisor / hv_xen.py
1 #
2 #
3
4 # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Google Inc.
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are
9 # met:
10 #
11 # 1. Redistributions of source code must retain the above copyright notice,
12 # this list of conditions and the following disclaimer.
13 #
14 # 2. Redistributions in binary form must reproduce the above copyright
15 # notice, this list of conditions and the following disclaimer in the
16 # documentation and/or other materials provided with the distribution.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
19 # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
22 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30
31 """Xen hypervisors
32
33 """
34
35 import logging
36 import errno
37 import string # pylint: disable=W0402
38 import shutil
39 from cStringIO import StringIO
40
41 from ganeti import constants
42 from ganeti import errors
43 from ganeti import utils
44 from ganeti.hypervisor import hv_base
45 from ganeti import netutils
46 from ganeti import objects
47 from ganeti import pathutils
48
49
50 XEND_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xend-config.sxp")
51 XL_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xen/xl.conf")
52 VIF_BRIDGE_SCRIPT = utils.PathJoin(pathutils.XEN_CONFIG_DIR,
53 "scripts/vif-bridge")
54 _DOM0_NAME = "Domain-0"
55 _DISK_LETTERS = string.ascii_lowercase
56
57 _FILE_DRIVER_MAP = {
58 constants.FD_LOOP: "file",
59 constants.FD_BLKTAP: "tap:aio",
60 constants.FD_BLKTAP2: "tap2:tapdisk:aio",
61 }
62
63
64 def _CreateConfigCpus(cpu_mask):
65 """Create a CPU config string for Xen's config file.
66
67 """
68 # Convert the string CPU mask to a list of list of int's
69 cpu_list = utils.ParseMultiCpuMask(cpu_mask)
70
71 if len(cpu_list) == 1:
72 all_cpu_mapping = cpu_list[0]
73 if all_cpu_mapping == constants.CPU_PINNING_OFF:
74 # If CPU pinning has 1 entry that's "all", then remove the
75 # parameter from the config file
76 return None
77 else:
78 # If CPU pinning has one non-all entry, mapping all vCPUS (the entire
79 # VM) to one physical CPU, using format 'cpu = "C"'
80 return "cpu = \"%s\"" % ",".join(map(str, all_cpu_mapping))
81 else:
82
83 def _GetCPUMap(vcpu):
84 if vcpu[0] == constants.CPU_PINNING_ALL_VAL:
85 cpu_map = constants.CPU_PINNING_ALL_XEN
86 else:
87 cpu_map = ",".join(map(str, vcpu))
88 return "\"%s\"" % cpu_map
89
90 # build the result string in format 'cpus = [ "c", "c", "c" ]',
91 # where each c is a physical CPU number, a range, a list, or any
92 # combination
93 return "cpus = [ %s ]" % ", ".join(map(_GetCPUMap, cpu_list))
94
95
96 def _RunInstanceList(fn, instance_list_errors):
97 """Helper function for L{_GetAllInstanceList} to retrieve the list
98 of instances from xen.
99
100 @type fn: callable
101 @param fn: Function to query xen for the list of instances
102 @type instance_list_errors: list
103 @param instance_list_errors: Error list
104 @rtype: list
105
106 """
107 result = fn()
108 if result.failed:
109 logging.error("Retrieving the instance list from xen failed (%s): %s",
110 result.fail_reason, result.output)
111 instance_list_errors.append(result)
112 raise utils.RetryAgain()
113
114 # skip over the heading
115 return result.stdout.splitlines()
116
117
118 class _InstanceCrashed(errors.GenericError):
119 """Instance has reached a violent ending.
120
121 This is raised within the Xen hypervisor only, and should not be seen or used
122 outside.
123
124 """
125
126
127 def _ParseInstanceList(lines, include_node):
128 """Parses the output of listing instances by xen.
129
130 @type lines: list
131 @param lines: Result of retrieving the instance list from xen
132 @type include_node: boolean
133 @param include_node: If True, return information for Dom0
134 @return: list of tuple containing (name, id, memory, vcpus, state, time
135 spent)
136
137 """
138 result = []
139
140 # Iterate through all lines while ignoring header
141 for line in lines[1:]:
142 # The format of lines is:
143 # Name ID Mem(MiB) VCPUs State Time(s)
144 # Domain-0 0 3418 4 r----- 266.2
145 data = line.split()
146 if len(data) != 6:
147 raise errors.HypervisorError("Can't parse instance list,"
148 " line: %s" % line)
149 try:
150 data[1] = int(data[1])
151 data[2] = int(data[2])
152 data[3] = int(data[3])
153 data[4] = _XenToHypervisorInstanceState(data[4])
154 data[5] = float(data[5])
155 except (TypeError, ValueError), err:
156 raise errors.HypervisorError("Can't parse instance list,"
157 " line: %s, error: %s" % (line, err))
158 except _InstanceCrashed:
159 # The crashed instance can be interpreted as being down, so we omit it
160 # from the instance list.
161 continue
162
163 # skip the Domain-0 (optional)
164 if include_node or data[0] != _DOM0_NAME:
165 result.append(data)
166
167 return result
168
169
170 def _GetAllInstanceList(fn, include_node, delays, timeout):
171 """Return the list of instances including running and shutdown.
172
173 See L{_RunInstanceList} and L{_ParseInstanceList} for parameter details.
174
175 """
176 instance_list_errors = []
177 try:
178 lines = utils.Retry(_RunInstanceList, delays, timeout,
179 args=(fn, instance_list_errors))
180 except utils.RetryTimeout:
181 if instance_list_errors:
182 instance_list_result = instance_list_errors.pop()
183
184 errmsg = ("listing instances failed, timeout exceeded (%s): %s" %
185 (instance_list_result.fail_reason, instance_list_result.output))
186 else:
187 errmsg = "listing instances failed"
188
189 raise errors.HypervisorError(errmsg)
190
191 return _ParseInstanceList(lines, include_node)
192
193
194 def _IsInstanceRunning(instance_info):
195 """Determine whether an instance is running.
196
197 An instance is running if it is in the following Xen states:
198 running, blocked, paused, or dying (about to be destroyed / shutdown).
199
200 For some strange reason, Xen once printed 'rb----' which does not make any
201 sense because an instance cannot be both running and blocked. Fortunately,
202 for Ganeti 'running' or 'blocked' is the same as 'running'.
203
204 A state of nothing '------' means that the domain is runnable but it is not
205 currently running. That means it is in the queue behind other domains waiting
206 to be scheduled to run.
207 http://old-list-archives.xenproject.org/xen-users/2007-06/msg00849.html
208
209 A dying instance is about to be removed, but it is still consuming resources,
210 and counts as running.
211
212 @type instance_info: string
213 @param instance_info: Information about instance, as supplied by Xen.
214 @rtype: bool
215 @return: Whether an instance is running.
216
217 """
218 allowable_running_prefixes = [
219 "r--",
220 "rb-",
221 "-b-",
222 "---",
223 ]
224
225 def _RunningWithSuffix(suffix):
226 return map(lambda x: x + suffix, allowable_running_prefixes)
227
228 # The shutdown suspend ("ss") state is encountered during migration, where
229 # the instance is still considered to be running.
230 # The shutdown restart ("sr") is probably encountered during restarts - still
231 # running.
232 # See Xen commit e1475a6693aac8cddc4bdd456548aa05a625556b
233 return instance_info in _RunningWithSuffix("---") \
234 or instance_info in _RunningWithSuffix("ss-") \
235 or instance_info in _RunningWithSuffix("sr-") \
236 or instance_info == "-----d"
237
238
239 def _IsInstanceShutdown(instance_info):
240 """Determine whether the instance is shutdown.
241
242 An instance is shutdown when a user shuts it down from within, and we do not
243 remove domains to be able to detect that.
244
245 The dying state has been added as a precaution, as Xen's status reporting is
246 weird.
247
248 """
249 return instance_info == "---s--" \
250 or instance_info == "---s-d"
251
252
253 def _IgnorePaused(instance_info):
254 """Removes information about whether a Xen state is paused from the state.
255
256 As it turns out, an instance can be reported as paused in almost any
257 condition. Paused instances can be paused, running instances can be paused for
258 scheduling, and any other condition can appear to be paused as a result of
259 races or improbable conditions in Xen's status reporting.
260 As we do not use Xen's pause commands in any way at the time, we can simply
261 ignore the paused field and save ourselves a lot of trouble.
262
263 Should we ever use the pause commands, several samples would be needed before
264 we could confirm the domain as paused.
265
266 """
267 return instance_info.replace('p', '-')
268
269
270 def _IsCrashed(instance_info):
271 """Returns whether an instance is in the crashed Xen state.
272
273 When a horrible misconfiguration happens to a Xen domain, it can crash,
274 meaning that it encounters a violent ending. While this state usually flashes
275 only temporarily before the domain is restarted, being able to check for it
276 allows Ganeti not to act confused and do something about it.
277
278 """
279 return instance_info.count('c') > 0
280
281
282 def _XenToHypervisorInstanceState(instance_info):
283 """Maps Xen states to hypervisor states.
284
285 @type instance_info: string
286 @param instance_info: Information about instance, as supplied by Xen.
287 @rtype: L{hv_base.HvInstanceState}
288
289 """
290 instance_info = _IgnorePaused(instance_info)
291
292 if _IsCrashed(instance_info):
293 raise _InstanceCrashed("Instance detected as crashed, should be omitted")
294
295 if _IsInstanceRunning(instance_info):
296 return hv_base.HvInstanceState.RUNNING
297 elif _IsInstanceShutdown(instance_info):
298 return hv_base.HvInstanceState.SHUTDOWN
299 else:
300 raise errors.HypervisorError("hv_xen._XenToHypervisorInstanceState:"
301 " unhandled Xen instance state '%s'" %
302 instance_info)
303
304
305 def _GetRunningInstanceList(fn, include_node, delays, timeout):
306 """Return the list of running instances.
307
308 See L{_GetAllInstanceList} for parameter details.
309
310 """
311 instances = _GetAllInstanceList(fn, include_node, delays, timeout)
312 return [i for i in instances if hv_base.HvInstanceState.IsRunning(i[4])]
313
314
315 def _GetShutdownInstanceList(fn, include_node, delays, timeout):
316 """Return the list of shutdown instances.
317
318 See L{_GetAllInstanceList} for parameter details.
319
320 """
321 instances = _GetAllInstanceList(fn, include_node, delays, timeout)
322 return [i for i in instances if hv_base.HvInstanceState.IsShutdown(i[4])]
323
324
325 def _ParseNodeInfo(info):
326 """Return information about the node.
327
328 @return: a dict with the following keys (memory values in MiB):
329 - memory_total: the total memory size on the node
330 - memory_free: the available memory on the node for instances
331 - nr_cpus: total number of CPUs
332 - nr_nodes: in a NUMA system, the number of domains
333 - nr_sockets: the number of physical CPU sockets in the node
334 - hv_version: the hypervisor version in the form (major, minor)
335
336 """
337 result = {}
338 cores_per_socket = threads_per_core = nr_cpus = None
339 xen_major, xen_minor = None, None
340 memory_total = None
341 memory_free = None
342
343 for line in info.splitlines():
344 fields = line.split(":", 1)
345
346 if len(fields) < 2:
347 continue
348
349 (key, val) = map(lambda s: s.strip(), fields)
350
351 # Note: in Xen 3, memory has changed to total_memory
352 if key in ("memory", "total_memory"):
353 memory_total = int(val)
354 elif key == "free_memory":
355 memory_free = int(val)
356 elif key == "nr_cpus":
357 nr_cpus = result["cpu_total"] = int(val)
358 elif key == "nr_nodes":
359 result["cpu_nodes"] = int(val)
360 elif key == "cores_per_socket":
361 cores_per_socket = int(val)
362 elif key == "threads_per_core":
363 threads_per_core = int(val)
364 elif key == "xen_major":
365 xen_major = int(val)
366 elif key == "xen_minor":
367 xen_minor = int(val)
368
369 if None not in [cores_per_socket, threads_per_core, nr_cpus]:
370 result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
371
372 if memory_free is not None:
373 result["memory_free"] = memory_free
374
375 if memory_total is not None:
376 result["memory_total"] = memory_total
377
378 if not (xen_major is None or xen_minor is None):
379 result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
380
381 return result
382
383
384 def _MergeInstanceInfo(info, instance_list):
385 """Updates node information from L{_ParseNodeInfo} with instance info.
386
387 @type info: dict
388 @param info: Result from L{_ParseNodeInfo}
389 @type instance_list: list of tuples
390 @param instance_list: list of instance information; one tuple per instance
391 @rtype: dict
392
393 """
394 total_instmem = 0
395
396 for (name, _, mem, vcpus, _, _) in instance_list:
397 if name == _DOM0_NAME:
398 info["memory_dom0"] = mem
399 info["cpu_dom0"] = vcpus
400
401 # Include Dom0 in total memory usage
402 total_instmem += mem
403
404 memory_free = info.get("memory_free")
405 memory_total = info.get("memory_total")
406
407 # Calculate memory used by hypervisor
408 if None not in [memory_total, memory_free, total_instmem]:
409 info["memory_hv"] = memory_total - memory_free - total_instmem
410
411 return info
412
413
414 def _GetNodeInfo(info, instance_list):
415 """Combines L{_MergeInstanceInfo} and L{_ParseNodeInfo}.
416
417 @type instance_list: list of tuples
418 @param instance_list: list of instance information; one tuple per instance
419
420 """
421 return _MergeInstanceInfo(_ParseNodeInfo(info), instance_list)
422
423
424 def _GetConfigFileDiskData(block_devices, blockdev_prefix,
425 _letters=_DISK_LETTERS):
426 """Get disk directives for Xen config file.
427
428 This method builds the xen config disk directive according to the
429 given disk_template and block_devices.
430
431 @param block_devices: list of tuples (cfdev, rldev):
432 - cfdev: dict containing ganeti config disk part
433 - rldev: ganeti.block.bdev.BlockDev object
434 @param blockdev_prefix: a string containing blockdevice prefix,
435 e.g. "sd" for /dev/sda
436
437 @return: string containing disk directive for xen instance config file
438
439 """
440 if len(block_devices) > len(_letters):
441 raise errors.HypervisorError("Too many disks")
442
443 disk_data = []
444
445 for sd_suffix, (cfdev, dev_path, _) in zip(_letters, block_devices):
446 sd_name = blockdev_prefix + sd_suffix
447
448 if cfdev.mode == constants.DISK_RDWR:
449 mode = "w"
450 else:
451 mode = "r"
452
453 if cfdev.dev_type in constants.DTS_FILEBASED:
454 driver = _FILE_DRIVER_MAP[cfdev.logical_id[0]]
455 else:
456 driver = "phy"
457
458 disk_data.append("'%s:%s,%s,%s'" % (driver, dev_path, sd_name, mode))
459
460 return disk_data
461
462
463 def _QuoteCpuidField(data):
464 """Add quotes around the CPUID field only if necessary.
465
466 Xen CPUID fields come in two shapes: LIBXL strings, which need quotes around
467 them, and lists of XEND strings, which don't.
468
469 @param data: Either type of parameter.
470 @return: The quoted version thereof.
471
472 """
473 return "'%s'" % data if data.startswith("host") else data
474
475
476 def _ConfigureNIC(instance, seq, nic, tap):
477 """Run the network configuration script for a specified NIC
478
479 See L{hv_base.ConfigureNIC}.
480
481 @type instance: instance object
482 @param instance: instance we're acting on
483 @type seq: int
484 @param seq: nic sequence number
485 @type nic: nic object
486 @param nic: nic we're acting on
487 @type tap: str
488 @param tap: the host's tap interface this NIC corresponds to
489
490 """
491 hv_base.ConfigureNIC(pathutils.XEN_IFUP_OS, instance, seq, nic, tap)
492
493
494 class XenHypervisor(hv_base.BaseHypervisor):
495 """Xen generic hypervisor interface
496
497 This is the Xen base class used for both Xen PVM and HVM. It contains
498 all the functionality that is identical for both.
499
500 """
501 CAN_MIGRATE = True
502 REBOOT_RETRY_COUNT = 60
503 REBOOT_RETRY_INTERVAL = 10
504 _ROOT_DIR = pathutils.RUN_DIR + "/xen-hypervisor"
505 _NICS_DIR = _ROOT_DIR + "/nic" # contains NICs' info
506 _DIRS = [_ROOT_DIR, _NICS_DIR]
507
508 _INSTANCE_LIST_DELAYS = (0.3, 1.5, 1.0)
509 _INSTANCE_LIST_TIMEOUT = 5
510
511 ANCILLARY_FILES = [
512 XEND_CONFIG_FILE,
513 XL_CONFIG_FILE,
514 VIF_BRIDGE_SCRIPT,
515 ]
516 ANCILLARY_FILES_OPT = [
517 XEND_CONFIG_FILE,
518 XL_CONFIG_FILE,
519 ]
520
521 def __init__(self, _cfgdir=None, _run_cmd_fn=None, _cmd=None):
522 hv_base.BaseHypervisor.__init__(self)
523
524 if _cfgdir is None:
525 self._cfgdir = pathutils.XEN_CONFIG_DIR
526 else:
527 self._cfgdir = _cfgdir
528
529 if _run_cmd_fn is None:
530 self._run_cmd_fn = utils.RunCmd
531 else:
532 self._run_cmd_fn = _run_cmd_fn
533
534 self._cmd = _cmd
535
536 @staticmethod
537 def _GetCommandFromHvparams(hvparams):
538 """Returns the Xen command extracted from the given hvparams.
539
540 @type hvparams: dict of strings
541 @param hvparams: hypervisor parameters
542
543 """
544 if hvparams is None or constants.HV_XEN_CMD not in hvparams:
545 raise errors.HypervisorError("Cannot determine xen command.")
546 else:
547 return hvparams[constants.HV_XEN_CMD]
548
549 def _GetCommand(self, hvparams):
550 """Returns Xen command to use.
551
552 @type hvparams: dict of strings
553 @param hvparams: hypervisor parameters
554
555 """
556 if self._cmd is None:
557 cmd = XenHypervisor._GetCommandFromHvparams(hvparams)
558 else:
559 cmd = self._cmd
560
561 if cmd not in constants.KNOWN_XEN_COMMANDS:
562 raise errors.ProgrammerError("Unknown Xen command '%s'" % cmd)
563
564 return cmd
565
566 def _RunXen(self, args, hvparams, timeout=None):
567 """Wrapper around L{utils.process.RunCmd} to run Xen command.
568
569 @type hvparams: dict of strings
570 @param hvparams: dictionary of hypervisor params
571 @type timeout: int or None
572 @param timeout: if a timeout (in seconds) is specified, the command will be
573 terminated after that number of seconds.
574 @see: L{utils.process.RunCmd}
575
576 """
577 cmd = []
578
579 if timeout is not None:
580 cmd.extend(["timeout", str(timeout)])
581
582 cmd.extend([self._GetCommand(hvparams)])
583 cmd.extend(args)
584
585 return self._run_cmd_fn(cmd)
586
587 def _ConfigFileName(self, instance_name):
588 """Get the config file name for an instance.
589
590 @param instance_name: instance name
591 @type instance_name: str
592 @return: fully qualified path to instance config file
593 @rtype: str
594
595 """
596 return utils.PathJoin(self._cfgdir, instance_name)
597
598 @classmethod
599 def _WriteNICInfoFile(cls, instance, idx, nic):
600 """Write the Xen config file for the instance.
601
602 This version of the function just writes the config file from static data.
603
604 """
605 instance_name = instance.name
606 dirs = [(dname, constants.RUN_DIRS_MODE)
607 for dname in cls._DIRS + [cls._InstanceNICDir(instance_name)]]
608 utils.EnsureDirs(dirs)
609
610 cfg_file = cls._InstanceNICFile(instance_name, idx)
611 data = StringIO()
612
613 data.write("TAGS=%s\n" % r"\ ".join(instance.GetTags()))
614 if nic.netinfo:
615 netinfo = objects.Network.FromDict(nic.netinfo)
616 for k, v in netinfo.HooksDict().iteritems():
617 data.write("%s=%s\n" % (k, v))
618
619 data.write("MAC=%s\n" % nic.mac)
620 if nic.ip:
621 data.write("IP=%s\n" % nic.ip)
622 data.write("INTERFACE_INDEX=%s\n" % str(idx))
623 if nic.name:
624 data.write("INTERFACE_NAME=%s\n" % nic.name)
625 data.write("INTERFACE_UUID=%s\n" % nic.uuid)
626 data.write("MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
627 data.write("LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
628 data.write("VLAN=%s\n" % nic.nicparams[constants.NIC_VLAN])
629
630 try:
631 utils.WriteFile(cfg_file, data=data.getvalue())
632 except EnvironmentError, err:
633 raise errors.HypervisorError("Cannot write Xen instance configuration"
634 " file %s: %s" % (cfg_file, err))
635
636 @classmethod
637 def _InstanceNICDir(cls, instance_name):
638 """Returns the directory holding the tap device files for a given instance.
639
640 """
641 return utils.PathJoin(cls._NICS_DIR, instance_name)
642
643 @classmethod
644 def _InstanceNICFile(cls, instance_name, seq):
645 """Returns the name of the file containing the tap device for a given NIC
646
647 """
648 return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
649
650 @classmethod
651 def _GetConfig(cls, instance, startup_memory, block_devices):
652 """Build Xen configuration for an instance.
653
654 """
655 raise NotImplementedError
656
657 def _WriteNicConfig(self, config, instance, hvp):
658 vif_data = []
659
660 # only XenHvmHypervisor has these hvparams
661 nic_type = hvp.get(constants.HV_NIC_TYPE, None)
662 vif_type = hvp.get(constants.HV_VIF_TYPE, None)
663 nic_type_str = ""
664 if nic_type or vif_type:
665 if nic_type is None:
666 if vif_type:
667 nic_type_str = ", type=%s" % vif_type
668 elif nic_type == constants.HT_NIC_PARAVIRTUAL:
669 nic_type_str = ", type=paravirtualized"
670 else:
671 # parameter 'model' is only valid with type 'ioemu'
672 nic_type_str = ", model=%s, type=%s" % \
673 (nic_type, constants.HT_HVM_VIF_IOEMU)
674
675 for idx, nic in enumerate(instance.nics):
676 nic_args = {}
677 nic_args["mac"] = "%s%s" % (nic.mac, nic_type_str)
678
679 if nic.name and \
680 nic.name.startswith(constants.INSTANCE_COMMUNICATION_NIC_PREFIX):
681 tap = hv_base.GenerateTapName()
682 nic_args["vifname"] = tap
683 nic_args["script"] = pathutils.XEN_VIF_METAD_SETUP
684 nic.name = tap
685 else:
686 ip = getattr(nic, "ip", None)
687 if ip is not None:
688 nic_args["ip"] = ip
689
690 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
691 nic_args["bridge"] = nic.nicparams[constants.NIC_LINK]
692 elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_OVS:
693 nic_args["bridge"] = nic.nicparams[constants.NIC_LINK]
694 if nic.nicparams[constants.NIC_VLAN]:
695 nic_args["bridge"] += nic.nicparams[constants.NIC_VLAN]
696
697 if hvp[constants.HV_VIF_SCRIPT]:
698 nic_args["script"] = hvp[constants.HV_VIF_SCRIPT]
699
700 nic_str = ", ".join(["%s=%s" % p for p in nic_args.items()])
701 vif_data.append("'%s'" % (nic_str, ))
702 self._WriteNICInfoFile(instance, idx, nic)
703
704 config.write("vif = [%s]\n" % ",".join(vif_data))
705
706 def _WriteConfigFile(self, instance_name, data):
707 """Write the Xen config file for the instance.
708
709 This version of the function just writes the config file from static data.
710
711 """
712 # just in case it exists
713 utils.RemoveFile(utils.PathJoin(self._cfgdir, "auto", instance_name))
714
715 cfg_file = self._ConfigFileName(instance_name)
716 try:
717 utils.WriteFile(cfg_file, data=data)
718 except EnvironmentError, err:
719 raise errors.HypervisorError("Cannot write Xen instance configuration"
720 " file %s: %s" % (cfg_file, err))
721
722 def _ReadConfigFile(self, instance_name):
723 """Returns the contents of the instance config file.
724
725 """
726 filename = self._ConfigFileName(instance_name)
727
728 try:
729 file_content = utils.ReadFile(filename)
730 except EnvironmentError, err:
731 raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
732
733 return file_content
734
735 def _RemoveConfigFile(self, instance_name):
736 """Remove the xen configuration file.
737
738 """
739 utils.RemoveFile(self._ConfigFileName(instance_name))
740 try:
741 shutil.rmtree(self._InstanceNICDir(instance_name))
742 except OSError, err:
743 if err.errno != errno.ENOENT:
744 raise
745
746 def _StashConfigFile(self, instance_name):
747 """Move the Xen config file to the log directory and return its new path.
748
749 """
750 old_filename = self._ConfigFileName(instance_name)
751 base = ("%s-%s" %
752 (instance_name, utils.TimestampForFilename()))
753 new_filename = utils.PathJoin(pathutils.LOG_XEN_DIR, base)
754 utils.RenameFile(old_filename, new_filename)
755 return new_filename
756
757 def _GetInstanceList(self, include_node, hvparams):
758 """Wrapper around module level L{_GetAllInstanceList}.
759
760 @type hvparams: dict of strings
761 @param hvparams: hypervisor parameters to be used on this node
762
763 """
764 return _GetAllInstanceList(lambda: self._RunXen(["list"], hvparams),
765 include_node, delays=self._INSTANCE_LIST_DELAYS,
766 timeout=self._INSTANCE_LIST_TIMEOUT)
767
768 def ListInstances(self, hvparams=None):
769 """Get the list of running instances.
770
771 @type hvparams: dict of strings
772 @param hvparams: the instance's hypervisor params
773
774 @rtype: list of strings
775 @return: names of running instances
776
777 """
778 instance_list = _GetRunningInstanceList(
779 lambda: self._RunXen(["list"], hvparams),
780 False, delays=self._INSTANCE_LIST_DELAYS,
781 timeout=self._INSTANCE_LIST_TIMEOUT)
782 return [info[0] for info in instance_list]
783
784 def GetInstanceInfo(self, instance_name, hvparams=None):
785 """Get instance properties.
786
787 @type instance_name: string
788 @param instance_name: the instance name
789 @type hvparams: dict of strings
790 @param hvparams: the instance's hypervisor params
791
792 @return: tuple (name, id, memory, vcpus, stat, times)
793
794 """
795 instance_list = self._GetInstanceList(instance_name == _DOM0_NAME, hvparams)
796 result = None
797 for data in instance_list:
798 if data[0] == instance_name:
799 result = data
800 break
801 return result
802
803 def GetAllInstancesInfo(self, hvparams=None):
804 """Get properties of all instances.
805
806 @type hvparams: dict of strings
807 @param hvparams: hypervisor parameters
808
809 @rtype: (string, string, int, int, HypervisorInstanceState, int)
810 @return: list of tuples (name, id, memory, vcpus, state, times)
811
812 """
813 return self._GetInstanceList(False, hvparams)
814
815 def _MakeConfigFile(self, instance, startup_memory, block_devices):
816 """Gather configuration details and write to disk.
817
818 See L{_GetConfig} for arguments.
819
820 """
821 buf = StringIO()
822 buf.write("# Automatically generated by Ganeti. Do not edit!\n")
823 buf.write("\n")
824 buf.write(self._GetConfig(instance, startup_memory, block_devices))
825 buf.write("\n")
826
827 self._WriteConfigFile(instance.name, buf.getvalue())
828
829 def StartInstance(self, instance, block_devices, startup_paused):
830 """Start an instance.
831
832 """
833 startup_memory = self._InstanceStartupMemory(instance)
834
835 self._MakeConfigFile(instance, startup_memory, block_devices)
836
837 cmd = ["create"]
838 if startup_paused:
839 cmd.append("-p")
840 cmd.append(self._ConfigFileName(instance.name))
841
842 result = self._RunXen(cmd, instance.hvparams)
843 if result.failed:
844 # Move the Xen configuration file to the log directory to avoid
845 # leaving a stale config file behind.
846 stashed_config = self._StashConfigFile(instance.name)
847 raise errors.HypervisorError("Failed to start instance %s: %s (%s). Moved"
848 " config file to %s" %
849 (instance.name, result.fail_reason,
850 result.output, stashed_config))
851
852 for nic_seq, nic in enumerate(instance.nics):
853 if nic.name and nic.name.startswith("gnt.com."):
854 _ConfigureNIC(instance, nic_seq, nic, nic.name)
855
856 def StopInstance(self, instance, force=False, retry=False, name=None,
857 timeout=None):
858 """Stop an instance.
859
860 A soft shutdown can be interrupted. A hard shutdown tries forever.
861
862 """
863 assert(timeout is None or force is not None)
864
865 if name is None:
866 name = instance.name
867
868 return self._StopInstance(name, force, instance.hvparams, timeout)
869
870 def _ShutdownInstance(self, name, hvparams, timeout):
871 """Shutdown an instance if the instance is running.
872
873 The '-w' flag waits for shutdown to complete which avoids the need
874 to poll in the case where we want to destroy the domain
875 immediately after shutdown.
876
877 @type name: string
878 @param name: name of the instance to stop
879 @type hvparams: dict of string
880 @param hvparams: hypervisor parameters of the instance
881 @type timeout: int or None
882 @param timeout: a timeout after which the shutdown command should be killed,
883 or None for no timeout
884
885 """
886 instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
887
888 if instance_info is None or _IsInstanceShutdown(instance_info[4]):
889 logging.info("Failed to shutdown instance %s, not running", name)
890 return None
891
892 return self._RunXen(["shutdown", "-w", name], hvparams, timeout)
893
894 def _DestroyInstance(self, name, hvparams):
895 """Destroy an instance if the instance if the instance exists.
896
897 @type name: string
898 @param name: name of the instance to destroy
899 @type hvparams: dict of string
900 @param hvparams: hypervisor parameters of the instance
901
902 """
903 instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
904
905 if instance_info is None:
906 logging.info("Failed to destroy instance %s, does not exist", name)
907 return None
908
909 return self._RunXen(["destroy", name], hvparams)
910
911 # Destroy a domain only if necessary
912 #
913 # This method checks if the domain has already been destroyed before
914 # issuing the 'destroy' command. This step is necessary to handle
915 # domains created by other versions of Ganeti. For example, an
916 # instance created with 2.10 will be destroy by the
917 # '_ShutdownInstance', thus not requiring an additional destroy,
918 # which would cause an error if issued. See issue 619.
919 def _DestroyInstanceIfAlive(self, name, hvparams):
920 instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
921
922 if instance_info is None:
923 raise errors.HypervisorError("Failed to destroy instance %s, already"
924 " destroyed" % name)
925 else:
926 self._DestroyInstance(name, hvparams)
927
928 def _StopInstance(self, name, force, hvparams, timeout):
929 """Stop an instance.
930
931 @type name: string
932 @param name: name of the instance to destroy
933
934 @type force: boolean
935 @param force: whether to do a "hard" stop (destroy)
936
937 @type hvparams: dict of string
938 @param hvparams: hypervisor parameters of the instance
939
940 @type timeout: int or None
941 @param timeout: a timeout after which the shutdown command should be killed,
942 or None for no timeout
943
944 """
945 instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
946
947 if instance_info is None:
948 raise errors.HypervisorError("Failed to shutdown instance %s,"
949 " not running" % name)
950
951 if force:
952 result = self._DestroyInstanceIfAlive(name, hvparams)
953 else:
954 self._ShutdownInstance(name, hvparams, timeout)
955 result = self._DestroyInstanceIfAlive(name, hvparams)
956
957 if result is not None and result.failed and \
958 self.GetInstanceInfo(name, hvparams=hvparams) is not None:
959 raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
960 (name, result.fail_reason, result.output))
961
962 # Remove configuration file if stopping/starting instance was successful
963 self._RemoveConfigFile(name)
964
965 def RebootInstance(self, instance):
966 """Reboot an instance.
967
968 """
969 ini_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
970
971 if ini_info is None:
972 raise errors.HypervisorError("Failed to reboot instance %s,"
973 " not running" % instance.name)
974
975 result = self._RunXen(["reboot", instance.name], instance.hvparams)
976 if result.failed:
977 raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
978 (instance.name, result.fail_reason,
979 result.output))
980
981 def _CheckInstance():
982 new_info = self.GetInstanceInfo(instance.name, hvparams=instance.hvparams)
983
984 # check if the domain ID has changed or the run time has decreased
985 if (new_info is not None and
986 (new_info[1] != ini_info[1] or new_info[5] < ini_info[5])):
987 return
988
989 raise utils.RetryAgain()
990
991 try:
992 utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
993 self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
994 except utils.RetryTimeout:
995 raise errors.HypervisorError("Failed to reboot instance %s: instance"
996 " did not reboot in the expected interval" %
997 (instance.name, ))
998
999 def BalloonInstanceMemory(self, instance, mem):
1000 """Balloon an instance memory to a certain value.
1001
1002 @type instance: L{objects.Instance}
1003 @param instance: instance to be accepted
1004 @type mem: int
1005 @param mem: actual memory size to use for instance runtime
1006
1007 """
1008 result = self._RunXen(["mem-set", instance.name, mem], instance.hvparams)
1009 if result.failed:
1010 raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" %
1011 (instance.name, result.fail_reason,
1012 result.output))
1013
1014 # Update configuration file
1015 cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem]
1016 cmd.append(self._ConfigFileName(instance.name))
1017
1018 result = utils.RunCmd(cmd)
1019 if result.failed:
1020 raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" %
1021 (instance.name, result.fail_reason,
1022 result.output))
1023
1024 def GetNodeInfo(self, hvparams=None):
1025 """Return information about the node.
1026
1027 @see: L{_GetNodeInfo} and L{_ParseNodeInfo}
1028
1029 """
1030 result = self._RunXen(["info"], hvparams)
1031 if result.failed:
1032 logging.error("Can't retrieve xen hypervisor information (%s): %s",
1033 result.fail_reason, result.output)
1034 return None
1035
1036 instance_list = self._GetInstanceList(True, hvparams)
1037 return _GetNodeInfo(result.stdout, instance_list)
1038
1039 @classmethod
1040 def GetInstanceConsole(cls, instance, primary_node, node_group,
1041 hvparams, beparams):
1042 """Return a command for connecting to the console of an instance.
1043
1044 """
1045 xen_cmd = XenHypervisor._GetCommandFromHvparams(hvparams)
1046 ndparams = node_group.FillND(primary_node)
1047 return objects.InstanceConsole(instance=instance.name,
1048 kind=constants.CONS_SSH,
1049 host=primary_node.name,
1050 port=ndparams.get(constants.ND_SSH_PORT),
1051 user=constants.SSH_CONSOLE_USER,
1052 command=[pathutils.XEN_CONSOLE_WRAPPER,
1053 xen_cmd, instance.name])
1054
1055 def Verify(self, hvparams=None):
1056 """Verify the hypervisor.
1057
1058 For Xen, this verifies that the xend process is running.
1059
1060 @type hvparams: dict of strings
1061 @param hvparams: hypervisor parameters to be verified against
1062
1063 @return: Problem description if something is wrong, C{None} otherwise
1064
1065 """
1066 if hvparams is None:
1067 return "Could not verify the hypervisor, because no hvparams were" \
1068 " provided."
1069
1070 if constants.HV_XEN_CMD in hvparams:
1071 xen_cmd = hvparams[constants.HV_XEN_CMD]
1072 try:
1073 self._CheckToolstack(xen_cmd)
1074 except errors.HypervisorError:
1075 return "The configured xen toolstack '%s' is not available on this" \
1076 " node." % xen_cmd
1077
1078 result = self._RunXen(["info"], hvparams)
1079 if result.failed:
1080 return "Retrieving information from xen failed: %s, %s" % \
1081 (result.fail_reason, result.output)
1082
1083 return None
1084
1085 def MigrationInfo(self, instance):
1086 """Get instance information to perform a migration.
1087
1088 @type instance: L{objects.Instance}
1089 @param instance: instance to be migrated
1090 @rtype: string
1091 @return: content of the xen config file
1092
1093 """
1094 return self._ReadConfigFile(instance.name)
1095
1096 def AcceptInstance(self, instance, info, target):
1097 """Prepare to accept an instance.
1098
1099 @type instance: L{objects.Instance}
1100 @param instance: instance to be accepted
1101 @type info: string
1102 @param info: content of the xen config file on the source node
1103 @type target: string
1104 @param target: target host (usually ip), on this node
1105
1106 """
1107 pass
1108
1109 def FinalizeMigrationDst(self, instance, info, success):
1110 """Finalize an instance migration.
1111
1112 After a successful migration we write the xen config file.
1113 We do nothing on a failure, as we did not change anything at accept time.
1114
1115 @type instance: L{objects.Instance}
1116 @param instance: instance whose migration is being finalized
1117 @type info: string
1118 @param info: content of the xen config file on the source node
1119 @type success: boolean
1120 @param success: whether the migration was a success or a failure
1121
1122 """
1123 if success:
1124 self._WriteConfigFile(instance.name, info)
1125
1126 def MigrateInstance(self, cluster_name, instance, target, live):
1127 """Migrate an instance to a target node.
1128
1129 The migration will not be attempted if the instance is not
1130 currently running.
1131
1132 @type instance: L{objects.Instance}
1133 @param instance: the instance to be migrated
1134 @type target: string
1135 @param target: ip address of the target node
1136 @type live: boolean
1137 @param live: perform a live migration
1138
1139 """
1140 port = instance.hvparams[constants.HV_MIGRATION_PORT]
1141
1142 return self._MigrateInstance(cluster_name, instance.name, target, port,
1143 live, instance.hvparams)
1144
1145 def _MigrateInstance(self, cluster_name, instance_name, target, port, live,
1146 hvparams, _ping_fn=netutils.TcpPing):
1147 """Migrate an instance to a target node.
1148
1149 @see: L{MigrateInstance} for details
1150
1151 """
1152 if hvparams is None:
1153 raise errors.HypervisorError("No hvparams provided.")
1154
1155 if self.GetInstanceInfo(instance_name, hvparams=hvparams) is None:
1156 raise errors.HypervisorError("Instance not running, cannot migrate")
1157
1158 cmd = self._GetCommand(hvparams)
1159
1160 if (cmd == constants.XEN_CMD_XM and
1161 not _ping_fn(target, port, live_port_needed=True)):
1162 raise errors.HypervisorError("Remote host %s not listening on port"
1163 " %s, cannot migrate" % (target, port))
1164
1165 args = ["migrate"]
1166
1167 if cmd == constants.XEN_CMD_XM:
1168 args.extend(["-p", "%d" % port])
1169 if live:
1170 args.append("-l")
1171
1172 elif cmd == constants.XEN_CMD_XL:
1173 args.extend([
1174 "-s", constants.XL_SSH_CMD % cluster_name,
1175 "-C", self._ConfigFileName(instance_name),
1176 ])
1177
1178 else:
1179 raise errors.HypervisorError("Unsupported Xen command: %s" % self._cmd)
1180
1181 args.extend([instance_name, target])
1182
1183 result = self._RunXen(args, hvparams)
1184 if result.failed:
1185 raise errors.HypervisorError("Failed to migrate instance %s: %s" %
1186 (instance_name, result.output))
1187
1188 def FinalizeMigrationSource(self, instance, success, live):
1189 """Finalize the instance migration on the source node.
1190
1191 @type instance: L{objects.Instance}
1192 @param instance: the instance that was migrated
1193 @type success: bool
1194 @param success: whether the migration succeeded or not
1195 @type live: bool
1196 @param live: whether the user requested a live migration or not
1197
1198 """
1199 # pylint: disable=W0613
1200 if success:
1201 # remove old xen file after migration succeeded
1202 try:
1203 self._RemoveConfigFile(instance.name)
1204 except EnvironmentError:
1205 logging.exception("Failure while removing instance config file")
1206
1207 def GetMigrationStatus(self, instance):
1208 """Get the migration status
1209
1210 As MigrateInstance for Xen is still blocking, if this method is called it
1211 means that MigrateInstance has completed successfully. So we can safely
1212 assume that the migration was successful and notify this fact to the client.
1213
1214 @type instance: L{objects.Instance}
1215 @param instance: the instance that is being migrated
1216 @rtype: L{objects.MigrationStatus}
1217 @return: the status of the current migration (one of
1218 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
1219 progress info that can be retrieved from the hypervisor
1220
1221 """
1222 return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
1223
1224 def PowercycleNode(self, hvparams=None):
1225 """Xen-specific powercycle.
1226
1227 This first does a Linux reboot (which triggers automatically a Xen
1228 reboot), and if that fails it tries to do a Xen reboot. The reason
1229 we don't try a Xen reboot first is that the xen reboot launches an
1230 external command which connects to the Xen hypervisor, and that
1231 won't work in case the root filesystem is broken and/or the xend
1232 daemon is not working.
1233
1234 @type hvparams: dict of strings
1235 @param hvparams: hypervisor params to be used on this node
1236
1237 """
1238 try:
1239 self.LinuxPowercycle()
1240 finally:
1241 xen_cmd = self._GetCommand(hvparams)
1242 utils.RunCmd([xen_cmd, "debug", "R"])
1243
1244 def _CheckToolstack(self, xen_cmd):
1245 """Check whether the given toolstack is available on the node.
1246
1247 @type xen_cmd: string
1248 @param xen_cmd: xen command (e.g. 'xm' or 'xl')
1249
1250 """
1251 binary_found = self._CheckToolstackBinary(xen_cmd)
1252 if not binary_found:
1253 raise errors.HypervisorError("No '%s' binary found on node." % xen_cmd)
1254 elif xen_cmd == constants.XEN_CMD_XL:
1255 if not self._CheckToolstackXlConfigured():
1256 raise errors.HypervisorError("Toolstack '%s' is not enabled on this"
1257 "node." % xen_cmd)
1258
1259 def _CheckToolstackBinary(self, xen_cmd):
1260 """Checks whether the xen command's binary is found on the machine.
1261
1262 """
1263 if xen_cmd not in constants.KNOWN_XEN_COMMANDS:
1264 raise errors.HypervisorError("Unknown xen command '%s'." % xen_cmd)
1265 result = self._run_cmd_fn(["which", xen_cmd])
1266 return not result.failed
1267
1268 def _CheckToolstackXlConfigured(self):
1269 """Checks whether xl is enabled on an xl-capable node.
1270
1271 @rtype: bool
1272 @returns: C{True} if 'xl' is enabled, C{False} otherwise
1273
1274 """
1275 result = self._run_cmd_fn([constants.XEN_CMD_XL, "help"])
1276 if not result.failed:
1277 return True
1278 elif result.failed:
1279 if "toolstack" in result.stderr:
1280 return False
1281 # xl fails for some other reason than the toolstack
1282 else:
1283 raise errors.HypervisorError("Cannot run xen ('%s'). Error: %s."
1284 % (constants.XEN_CMD_XL, result.stderr))
1285
1286
1287 def WriteXenConfigEvents(config, hvp):
1288 config.write("on_poweroff = 'preserve'\n")
1289 if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
1290 config.write("on_reboot = 'restart'\n")
1291 else:
1292 config.write("on_reboot = 'destroy'\n")
1293 config.write("on_crash = 'restart'\n")
1294
1295
1296 class XenPvmHypervisor(XenHypervisor):
1297 """Xen PVM hypervisor interface"""
1298
1299 PARAMETERS = {
1300 constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
1301 constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
1302 constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
1303 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1304 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
1305 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
1306 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
1307 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1308 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1309 # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
1310 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1311 constants.HV_REBOOT_BEHAVIOR:
1312 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1313 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1314 constants.HV_CPU_CAP: hv_base.OPT_NONNEGATIVE_INT_CHECK,
1315 constants.HV_CPU_WEIGHT:
1316 (False, lambda x: 0 < x < 65536, "invalid weight", None, None),
1317 constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1318 constants.HV_XEN_CMD:
1319 hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1320 constants.HV_XEN_CPUID: hv_base.NO_CHECK,
1321 constants.HV_SOUNDHW: hv_base.NO_CHECK,
1322 }
1323
1324 def _GetConfig(self, instance, startup_memory, block_devices):
1325 """Write the Xen config file for the instance.
1326
1327 """
1328 hvp = instance.hvparams
1329 config = StringIO()
1330 config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
1331
1332 # if bootloader is True, use bootloader instead of kernel and ramdisk
1333 # parameters.
1334 if hvp[constants.HV_USE_BOOTLOADER]:
1335 # bootloader handling
1336 bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
1337 if bootloader_path:
1338 config.write("bootloader = '%s'\n" % bootloader_path)
1339 else:
1340 raise errors.HypervisorError("Bootloader enabled, but missing"
1341 " bootloader path")
1342
1343 bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
1344 if bootloader_args:
1345 config.write("bootargs = '%s'\n" % bootloader_args)
1346 else:
1347 # kernel handling
1348 kpath = hvp[constants.HV_KERNEL_PATH]
1349 config.write("kernel = '%s'\n" % kpath)
1350
1351 # initrd handling
1352 initrd_path = hvp[constants.HV_INITRD_PATH]
1353 if initrd_path:
1354 config.write("ramdisk = '%s'\n" % initrd_path)
1355
1356 # rest of the settings
1357 config.write("memory = %d\n" % startup_memory)
1358 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1359 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1360 cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1361 if cpu_pinning:
1362 config.write("%s\n" % cpu_pinning)
1363 cpu_cap = hvp[constants.HV_CPU_CAP]
1364 if cpu_cap:
1365 config.write("cpu_cap=%d\n" % cpu_cap)
1366 cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1367 if cpu_weight:
1368 config.write("cpu_weight=%d\n" % cpu_weight)
1369
1370 config.write("name = '%s'\n" % instance.name)
1371
1372 self._WriteNicConfig(config, instance, hvp)
1373
1374 disk_data = \
1375 _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1376 config.write("disk = [%s]\n" % ",".join(disk_data))
1377
1378 if hvp[constants.HV_ROOT_PATH]:
1379 config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
1380
1381 WriteXenConfigEvents(config, hvp)
1382 config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
1383
1384 cpuid = hvp[constants.HV_XEN_CPUID]
1385 if cpuid:
1386 config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1387
1388 if hvp[constants.HV_SOUNDHW]:
1389 config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1390
1391 return config.getvalue()
1392
1393
1394 class XenHvmHypervisor(XenHypervisor):
1395 """Xen HVM hypervisor interface"""
1396
1397 ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
1398 pathutils.VNC_PASSWORD_FILE,
1399 ]
1400 ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [
1401 pathutils.VNC_PASSWORD_FILE,
1402 ]
1403
1404 PARAMETERS = {
1405 constants.HV_ACPI: hv_base.NO_CHECK,
1406 constants.HV_BOOT_ORDER: (True, ) +
1407 (lambda x: x and len(x.strip("acdn")) == 0,
1408 "Invalid boot order specified, must be one or more of [acdn]",
1409 None, None),
1410 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
1411 constants.HV_DISK_TYPE:
1412 hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
1413 constants.HV_NIC_TYPE:
1414 hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
1415 constants.HV_PAE: hv_base.NO_CHECK,
1416 constants.HV_VNC_BIND_ADDRESS:
1417 (False, netutils.IP4Address.IsValid,
1418 "VNC bind address is not a valid IP address", None, None),
1419 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1420 constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
1421 constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
1422 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1423 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1424 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
1425 # TODO: Add a check for the blockdev prefix (matching [a-z:] or similar).
1426 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1427 # Add PCI passthrough
1428 constants.HV_PASSTHROUGH: hv_base.NO_CHECK,
1429 constants.HV_REBOOT_BEHAVIOR:
1430 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1431 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1432 constants.HV_CPU_CAP: hv_base.NO_CHECK,
1433 constants.HV_CPU_WEIGHT:
1434 (False, lambda x: 0 < x < 65535, "invalid weight", None, None),
1435 constants.HV_VIF_TYPE:
1436 hv_base.ParamInSet(False, constants.HT_HVM_VALID_VIF_TYPES),
1437 constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1438 constants.HV_VIRIDIAN: hv_base.NO_CHECK,
1439 constants.HV_XEN_CMD:
1440 hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1441 constants.HV_XEN_CPUID: hv_base.NO_CHECK,
1442 constants.HV_SOUNDHW: hv_base.NO_CHECK,
1443 }
1444
1445 def _GetConfig(self, instance, startup_memory, block_devices):
1446 """Create a Xen 3.1 HVM config file.
1447
1448 """
1449 hvp = instance.hvparams
1450
1451 config = StringIO()
1452
1453 # kernel handling
1454 kpath = hvp[constants.HV_KERNEL_PATH]
1455 config.write("kernel = '%s'\n" % kpath)
1456
1457 config.write("builder = 'hvm'\n")
1458 config.write("memory = %d\n" % startup_memory)
1459 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1460 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1461 cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1462 if cpu_pinning:
1463 config.write("%s\n" % cpu_pinning)
1464 cpu_cap = hvp[constants.HV_CPU_CAP]
1465 if cpu_cap:
1466 config.write("cpu_cap=%d\n" % cpu_cap)
1467 cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1468 if cpu_weight:
1469 config.write("cpu_weight=%d\n" % cpu_weight)
1470
1471 config.write("name = '%s'\n" % instance.name)
1472 if hvp[constants.HV_PAE]:
1473 config.write("pae = 1\n")
1474 else:
1475 config.write("pae = 0\n")
1476 if hvp[constants.HV_ACPI]:
1477 config.write("acpi = 1\n")
1478 else:
1479 config.write("acpi = 0\n")
1480 if hvp[constants.HV_VIRIDIAN]:
1481 config.write("viridian = 1\n")
1482 else:
1483 config.write("viridian = 0\n")
1484
1485 config.write("apic = 1\n")
1486 config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
1487 config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
1488 config.write("sdl = 0\n")
1489 config.write("usb = 1\n")
1490 config.write("usbdevice = 'tablet'\n")
1491 config.write("vnc = 1\n")
1492 if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
1493 config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
1494 else:
1495 config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
1496
1497 if instance.network_port > constants.VNC_BASE_PORT:
1498 display = instance.network_port - constants.VNC_BASE_PORT
1499 config.write("vncdisplay = %s\n" % display)
1500 config.write("vncunused = 0\n")
1501 else:
1502 config.write("# vncdisplay = 1\n")
1503 config.write("vncunused = 1\n")
1504
1505 vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
1506 try:
1507 password = utils.ReadFile(vnc_pwd_file)
1508 except EnvironmentError, err:
1509 raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
1510 (vnc_pwd_file, err))
1511
1512 config.write("vncpasswd = '%s'\n" % password.rstrip())
1513
1514 config.write("serial = 'pty'\n")
1515 if hvp[constants.HV_USE_LOCALTIME]:
1516 config.write("localtime = 1\n")
1517
1518 self._WriteNicConfig(config, instance, hvp)
1519
1520 disk_data = \
1521 _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1522
1523 iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
1524 if iso_path:
1525 iso = "'file:%s,hdc:cdrom,r'" % iso_path
1526 disk_data.append(iso)
1527
1528 config.write("disk = [%s]\n" % (",".join(disk_data)))
1529 # Add PCI passthrough
1530 pci_pass_arr = []
1531 pci_pass = hvp[constants.HV_PASSTHROUGH]
1532 if pci_pass:
1533 pci_pass_arr = pci_pass.split(";")
1534 config.write("pci = %s\n" % pci_pass_arr)
1535
1536 WriteXenConfigEvents(config, hvp)
1537
1538 cpuid = hvp[constants.HV_XEN_CPUID]
1539 if cpuid:
1540 config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1541
1542 if hvp[constants.HV_SOUNDHW]:
1543 config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1544
1545 return config.getvalue()