1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 """Xen hypervisors
32
33 """
34
35 import logging
36 import errno
37 import string
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 }
65 """Create a CPU config string for Xen's config file.
66
67 """
68
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
75
76 return None
77 else:
78
79
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
91
92
93 return "cpus = [ %s ]" % ", ".join(map(_GetCPUMap, cpu_list))
94
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
115 return result.stdout.splitlines()
116
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
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
141 for line in lines[1:]:
142
143
144
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
160
161 continue
162
163
164 if include_node or data[0] != _DOM0_NAME:
165 result.append(data)
166
167 return result
168
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
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
229
230
231
232
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
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
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
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
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
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
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
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
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
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
402 total_instmem += mem
403
404 memory_free = info.get("memory_free")
405 memory_total = info.get("memory_total")
406
407
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
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
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
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
492
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"
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):
535
536 @staticmethod
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
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
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
635
636 @classmethod
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
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
658 vif_data = []
659
660
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
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
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
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
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
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
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
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
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
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
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
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
845
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
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
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
912
913
914
915
916
917
918
927
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
963 self._RemoveConfigFile(name)
964
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
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
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
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):
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
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
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
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
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
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
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
1200 if success:
1201
1202 try:
1203 self._RemoveConfigFile(instance.name)
1204 except EnvironmentError:
1205 logging.exception("Failure while removing instance config file")
1206
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
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
1258
1267
1285
1294
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
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):
1392
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
1426 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1427
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):
1546