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 import time
40 from cStringIO import StringIO
41
42 from ganeti import constants
43 from ganeti import errors
44 from ganeti import utils
45 from ganeti.hypervisor import hv_base
46 from ganeti import netutils
47 from ganeti import objects
48 from ganeti import pathutils
49
50
51 XEND_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xend-config.sxp")
52 XL_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xen/xl.conf")
53 VIF_BRIDGE_SCRIPT = utils.PathJoin(pathutils.XEN_CONFIG_DIR,
54 "scripts/vif-bridge")
55 _DOM0_NAME = "Domain-0"
56 _DISK_LETTERS = string.ascii_lowercase
57
58 _FILE_DRIVER_MAP = {
59 constants.FD_LOOP: "file",
60 constants.FD_BLKTAP: "tap:aio",
61 constants.FD_BLKTAP2: "tap2:tapdisk:aio",
62 }
66 """Create a CPU config string for Xen's config file.
67
68 """
69
70 cpu_list = utils.ParseMultiCpuMask(cpu_mask)
71
72 if len(cpu_list) == 1:
73 all_cpu_mapping = cpu_list[0]
74 if all_cpu_mapping == constants.CPU_PINNING_OFF:
75
76
77 return None
78 else:
79
80
81 return "cpu = \"%s\"" % ",".join(map(str, all_cpu_mapping))
82 else:
83
84 def _GetCPUMap(vcpu):
85 if vcpu[0] == constants.CPU_PINNING_ALL_VAL:
86 cpu_map = constants.CPU_PINNING_ALL_XEN
87 else:
88 cpu_map = ",".join(map(str, vcpu))
89 return "\"%s\"" % cpu_map
90
91
92
93
94 return "cpus = [ %s ]" % ", ".join(map(_GetCPUMap, cpu_list))
95
98 """Helper function for L{_GetAllInstanceList} to retrieve the list
99 of instances from xen.
100
101 @type fn: callable
102 @param fn: Function to query xen for the list of instances
103 @type instance_list_errors: list
104 @param instance_list_errors: Error list
105 @rtype: list
106
107 """
108 result = fn()
109 if result.failed:
110 logging.error("Retrieving the instance list from xen failed (%s): %s",
111 result.fail_reason, result.output)
112 instance_list_errors.append(result)
113 raise utils.RetryAgain()
114
115
116 return result.stdout.splitlines()
117
120 """Instance has reached a violent ending.
121
122 This is raised within the Xen hypervisor only, and should not be seen or used
123 outside.
124
125 """
126
129 """Parses the output of listing instances by xen.
130
131 @type lines: list
132 @param lines: Result of retrieving the instance list from xen
133 @type include_node: boolean
134 @param include_node: If True, return information for Dom0
135 @return: list of tuple containing (name, id, memory, vcpus, state, time
136 spent)
137
138 """
139 result = []
140
141
142 for line in lines[1:]:
143
144
145
146 data = line.split()
147 if len(data) != 6:
148 raise errors.HypervisorError("Can't parse instance list,"
149 " line: %s" % line)
150 try:
151 data[1] = int(data[1])
152 data[2] = int(data[2])
153 data[3] = int(data[3])
154 data[4] = _XenToHypervisorInstanceState(data[4])
155 data[5] = float(data[5])
156 except (TypeError, ValueError), err:
157 raise errors.HypervisorError("Can't parse instance list,"
158 " line: %s, error: %s" % (line, err))
159 except _InstanceCrashed:
160
161
162 continue
163
164
165 if include_node or data[0] != _DOM0_NAME:
166 result.append(data)
167
168 return result
169
172 """Return the list of instances including running and shutdown.
173
174 See L{_RunInstanceList} and L{_ParseInstanceList} for parameter details.
175
176 """
177 instance_list_errors = []
178 try:
179 lines = utils.Retry(_RunInstanceList, delays, timeout,
180 args=(fn, instance_list_errors))
181 except utils.RetryTimeout:
182 if instance_list_errors:
183 instance_list_result = instance_list_errors.pop()
184
185 errmsg = ("listing instances failed, timeout exceeded (%s): %s" %
186 (instance_list_result.fail_reason, instance_list_result.output))
187 else:
188 errmsg = "listing instances failed"
189
190 raise errors.HypervisorError(errmsg)
191
192 return _ParseInstanceList(lines, include_node)
193
196 """Determine whether an instance is running.
197
198 An instance is running if it is in the following Xen states:
199 running, blocked, paused, or dying (about to be destroyed / shutdown).
200
201 For some strange reason, Xen once printed 'rb----' which does not make any
202 sense because an instance cannot be both running and blocked. Fortunately,
203 for Ganeti 'running' or 'blocked' is the same as 'running'.
204
205 A state of nothing '------' means that the domain is runnable but it is not
206 currently running. That means it is in the queue behind other domains waiting
207 to be scheduled to run.
208 http://old-list-archives.xenproject.org/xen-users/2007-06/msg00849.html
209
210 A dying instance is about to be removed, but it is still consuming resources,
211 and counts as running.
212
213 @type instance_info: string
214 @param instance_info: Information about instance, as supplied by Xen.
215 @rtype: bool
216 @return: Whether an instance is running.
217
218 """
219 allowable_running_prefixes = [
220 "r--",
221 "rb-",
222 "-b-",
223 "---",
224 ]
225
226 def _RunningWithSuffix(suffix):
227 return map(lambda x: x + suffix, allowable_running_prefixes)
228
229
230
231
232
233
234 return instance_info in _RunningWithSuffix("---") \
235 or instance_info in _RunningWithSuffix("ss-") \
236 or instance_info in _RunningWithSuffix("sr-") \
237 or instance_info == "-----d"
238
241 """Determine whether the instance is shutdown.
242
243 An instance is shutdown when a user shuts it down from within, and we do not
244 remove domains to be able to detect that.
245
246 The dying state has been added as a precaution, as Xen's status reporting is
247 weird.
248
249 """
250 return instance_info == "---s--" \
251 or instance_info == "---s-d"
252
255 """Removes information about whether a Xen state is paused from the state.
256
257 As it turns out, an instance can be reported as paused in almost any
258 condition. Paused instances can be paused, running instances can be paused for
259 scheduling, and any other condition can appear to be paused as a result of
260 races or improbable conditions in Xen's status reporting.
261 As we do not use Xen's pause commands in any way at the time, we can simply
262 ignore the paused field and save ourselves a lot of trouble.
263
264 Should we ever use the pause commands, several samples would be needed before
265 we could confirm the domain as paused.
266
267 """
268 return instance_info.replace('p', '-')
269
272 """Returns whether an instance is in the crashed Xen state.
273
274 When a horrible misconfiguration happens to a Xen domain, it can crash,
275 meaning that it encounters a violent ending. While this state usually flashes
276 only temporarily before the domain is restarted, being able to check for it
277 allows Ganeti not to act confused and do something about it.
278
279 """
280 return instance_info.count('c') > 0
281
284 """Maps Xen states to hypervisor states.
285
286 @type instance_info: string
287 @param instance_info: Information about instance, as supplied by Xen.
288 @rtype: L{hv_base.HvInstanceState}
289
290 """
291 instance_info = _IgnorePaused(instance_info)
292
293 if _IsCrashed(instance_info):
294 raise _InstanceCrashed("Instance detected as crashed, should be omitted")
295
296 if _IsInstanceRunning(instance_info):
297 return hv_base.HvInstanceState.RUNNING
298 elif _IsInstanceShutdown(instance_info):
299 return hv_base.HvInstanceState.SHUTDOWN
300 else:
301 raise errors.HypervisorError("hv_xen._XenToHypervisorInstanceState:"
302 " unhandled Xen instance state '%s'" %
303 instance_info)
304
307 """Return the list of running instances.
308
309 See L{_GetAllInstanceList} for parameter details.
310
311 """
312 instances = _GetAllInstanceList(fn, include_node, delays, timeout)
313 return [i for i in instances if hv_base.HvInstanceState.IsRunning(i[4])]
314
317 """Return the list of shutdown instances.
318
319 See L{_GetAllInstanceList} for parameter details.
320
321 """
322 instances = _GetAllInstanceList(fn, include_node, delays, timeout)
323 return [i for i in instances if hv_base.HvInstanceState.IsShutdown(i[4])]
324
327 """Return information about the node.
328
329 @return: a dict with the following keys (memory values in MiB):
330 - memory_total: the total memory size on the node
331 - memory_free: the available memory on the node for instances
332 - nr_cpus: total number of CPUs
333 - nr_nodes: in a NUMA system, the number of domains
334 - nr_sockets: the number of physical CPU sockets in the node
335 - hv_version: the hypervisor version in the form (major, minor)
336
337 """
338 result = {}
339 cores_per_socket = threads_per_core = nr_cpus = None
340 xen_major, xen_minor = None, None
341 memory_total = None
342 memory_free = None
343
344 for line in info.splitlines():
345 fields = line.split(":", 1)
346
347 if len(fields) < 2:
348 continue
349
350 (key, val) = map(lambda s: s.strip(), fields)
351
352
353 if key in ("memory", "total_memory"):
354 memory_total = int(val)
355 elif key == "free_memory":
356 memory_free = int(val)
357 elif key == "nr_cpus":
358 nr_cpus = result["cpu_total"] = int(val)
359 elif key == "nr_nodes":
360 result["cpu_nodes"] = int(val)
361 elif key == "cores_per_socket":
362 cores_per_socket = int(val)
363 elif key == "threads_per_core":
364 threads_per_core = int(val)
365 elif key == "xen_major":
366 xen_major = int(val)
367 elif key == "xen_minor":
368 xen_minor = int(val)
369
370 if None not in [cores_per_socket, threads_per_core, nr_cpus]:
371 result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
372
373 if memory_free is not None:
374 result["memory_free"] = memory_free
375
376 if memory_total is not None:
377 result["memory_total"] = memory_total
378
379 if not (xen_major is None or xen_minor is None):
380 result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
381
382 return result
383
386 """Updates node information from L{_ParseNodeInfo} with instance info.
387
388 @type info: dict
389 @param info: Result from L{_ParseNodeInfo}
390 @type instance_list: list of tuples
391 @param instance_list: list of instance information; one tuple per instance
392 @rtype: dict
393
394 """
395 total_instmem = 0
396
397 for (name, _, mem, vcpus, _, _) in instance_list:
398 if name == _DOM0_NAME:
399 info["memory_dom0"] = mem
400 info["cpu_dom0"] = vcpus
401
402
403 total_instmem += mem
404
405 memory_free = info.get("memory_free")
406 memory_total = info.get("memory_total")
407
408
409 if None not in [memory_total, memory_free, total_instmem]:
410 info["memory_hv"] = memory_total - memory_free - total_instmem
411
412 return info
413
416 """Combines L{_MergeInstanceInfo} and L{_ParseNodeInfo}.
417
418 @type instance_list: list of tuples
419 @param instance_list: list of instance information; one tuple per instance
420
421 """
422 return _MergeInstanceInfo(_ParseNodeInfo(info), instance_list)
423
427 """Get disk directives for Xen config file.
428
429 This method builds the xen config disk directive according to the
430 given disk_template and block_devices.
431
432 @param block_devices: list of tuples (cfdev, rldev):
433 - cfdev: dict containing ganeti config disk part
434 - rldev: ganeti.block.bdev.BlockDev object
435 @param blockdev_prefix: a string containing blockdevice prefix,
436 e.g. "sd" for /dev/sda
437
438 @return: string containing disk directive for xen instance config file
439
440 """
441 if len(block_devices) > len(_letters):
442 raise errors.HypervisorError("Too many disks")
443
444 disk_data = []
445
446 for sd_suffix, (cfdev, dev_path, _) in zip(_letters, block_devices):
447 sd_name = blockdev_prefix + sd_suffix
448
449 if cfdev.mode == constants.DISK_RDWR:
450 mode = "w"
451 else:
452 mode = "r"
453
454 if cfdev.dev_type in constants.DTS_FILEBASED:
455 driver = _FILE_DRIVER_MAP[cfdev.logical_id[0]]
456 else:
457 driver = "phy"
458
459 disk_data.append("'%s:%s,%s,%s'" % (driver, dev_path, sd_name, mode))
460
461 return disk_data
462
465 """Add quotes around the CPUID field only if necessary.
466
467 Xen CPUID fields come in two shapes: LIBXL strings, which need quotes around
468 them, and lists of XEND strings, which don't.
469
470 @param data: Either type of parameter.
471 @return: The quoted version thereof.
472
473 """
474 return "'%s'" % data if data.startswith("host") else data
475
493
496 """Xen generic hypervisor interface
497
498 This is the Xen base class used for both Xen PVM and HVM. It contains
499 all the functionality that is identical for both.
500
501 """
502 CAN_MIGRATE = True
503 REBOOT_RETRY_COUNT = 60
504 REBOOT_RETRY_INTERVAL = 10
505 _ROOT_DIR = pathutils.RUN_DIR + "/xen-hypervisor"
506
507 _NICS_DIR = _ROOT_DIR + "/nic"
508
509 _MIGRATION_DIR = _ROOT_DIR + "/migration"
510 _DIRS = [_ROOT_DIR, _NICS_DIR, _MIGRATION_DIR]
511
512 _INSTANCE_LIST_DELAYS = (0.3, 1.5, 1.0)
513 _INSTANCE_LIST_TIMEOUT = 5
514
515 ANCILLARY_FILES = [
516 XEND_CONFIG_FILE,
517 XL_CONFIG_FILE,
518 VIF_BRIDGE_SCRIPT,
519 ]
520 ANCILLARY_FILES_OPT = [
521 XEND_CONFIG_FILE,
522 XL_CONFIG_FILE,
523 ]
524
525 - def __init__(self, _cfgdir=None, _run_cmd_fn=None, _cmd=None):
539
540 @staticmethod
542 """Returns the Xen command extracted from the given hvparams.
543
544 @type hvparams: dict of strings
545 @param hvparams: hypervisor parameters
546
547 """
548 if hvparams is None or constants.HV_XEN_CMD not in hvparams:
549 raise errors.HypervisorError("Cannot determine xen command.")
550 else:
551 return hvparams[constants.HV_XEN_CMD]
552
569
570 - def _RunXen(self, args, hvparams, timeout=None):
571 """Wrapper around L{utils.process.RunCmd} to run Xen command.
572
573 @type hvparams: dict of strings
574 @param hvparams: dictionary of hypervisor params
575 @type timeout: int or None
576 @param timeout: if a timeout (in seconds) is specified, the command will be
577 terminated after that number of seconds.
578 @see: L{utils.process.RunCmd}
579
580 """
581 cmd = []
582
583 if timeout is not None:
584 cmd.extend(["timeout", str(timeout)])
585
586 cmd.extend([self._GetCommand(hvparams)])
587 cmd.extend(args)
588
589 return self._run_cmd_fn(cmd)
590
592 """Get the config file name for an instance.
593
594 @param instance_name: instance name
595 @type instance_name: str
596 @return: fully qualified path to instance config file
597 @rtype: str
598
599 """
600 return utils.PathJoin(self._cfgdir, instance_name)
601
602 @classmethod
604 """Makes sure that the directories needed by the hypervisor exist.
605
606 @type extra_dirs: list of string or None
607 @param extra_dirs: Additional directories which ought to exist.
608
609 """
610 if extra_dirs is None:
611 extra_dirs = []
612 dirs = [(dname, constants.RUN_DIRS_MODE) for dname in
613 (cls._DIRS + extra_dirs)]
614 utils.EnsureDirs(dirs)
615
616 @classmethod
651
652 @staticmethod
672
673 @classmethod
675 """Returns the directory holding the tap device files for a given instance.
676
677 """
678 return utils.PathJoin(cls._NICS_DIR, instance_name)
679
680 @classmethod
682 """Returns the name of the file containing the tap device for a given NIC
683
684 """
685 return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
686
687 @classmethod
698
699 @classmethod
700 - def _GetConfig(cls, instance, startup_memory, block_devices):
701 """Build Xen configuration for an instance.
702
703 """
704 raise NotImplementedError
705
707 vif_data = []
708
709
710 nic_type = hvp.get(constants.HV_NIC_TYPE, None)
711 vif_type = hvp.get(constants.HV_VIF_TYPE, None)
712 nic_type_str = ""
713 if nic_type or vif_type:
714 if nic_type is None:
715 if vif_type:
716 nic_type_str = ", type=%s" % vif_type
717 elif nic_type == constants.HT_NIC_PARAVIRTUAL:
718 nic_type_str = ", type=paravirtualized"
719 else:
720
721 nic_type_str = ", model=%s, type=%s" % \
722 (nic_type, constants.HT_HVM_VIF_IOEMU)
723
724 for idx, nic in enumerate(instance.nics):
725 nic_args = {}
726 nic_args["mac"] = "%s%s" % (nic.mac, nic_type_str)
727
728 if nic.name and \
729 nic.name.startswith(constants.INSTANCE_COMMUNICATION_NIC_PREFIX):
730 tap = hv_base.GenerateTapName()
731 nic_args["vifname"] = tap
732 nic_args["script"] = pathutils.XEN_VIF_METAD_SETUP
733 nic.name = tap
734 else:
735 ip = getattr(nic, "ip", None)
736 if ip is not None:
737 nic_args["ip"] = ip
738
739 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
740 nic_args["bridge"] = nic.nicparams[constants.NIC_LINK]
741 elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_OVS:
742 nic_args["bridge"] = nic.nicparams[constants.NIC_LINK]
743 if nic.nicparams[constants.NIC_VLAN]:
744 nic_args["bridge"] += nic.nicparams[constants.NIC_VLAN]
745
746 if hvp[constants.HV_VIF_SCRIPT]:
747 nic_args["script"] = hvp[constants.HV_VIF_SCRIPT]
748
749 nic_str = ", ".join(["%s=%s" % p for p in nic_args.items()])
750 vif_data.append("'%s'" % (nic_str, ))
751 self._WriteNICInfoFile(instance, idx, nic)
752
753 config.write("vif = [%s]\n" % ",".join(vif_data))
754
756 """Write the Xen config file for the instance.
757
758 This version of the function just writes the config file from static data.
759
760 """
761
762 utils.RemoveFile(utils.PathJoin(self._cfgdir, "auto", instance_name))
763
764 cfg_file = self._ConfigFileName(instance_name)
765 try:
766 utils.WriteFile(cfg_file, data=data)
767 except EnvironmentError, err:
768 raise errors.HypervisorError("Cannot write Xen instance configuration"
769 " file %s: %s" % (cfg_file, err))
770
772 """Returns the contents of the instance config file.
773
774 """
775 filename = self._ConfigFileName(instance_name)
776
777 try:
778 file_content = utils.ReadFile(filename)
779 except EnvironmentError, err:
780 raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
781
782 return file_content
783
785 """Remove the xen configuration file.
786
787 """
788 utils.RemoveFile(self._ConfigFileName(instance_name))
789 try:
790 shutil.rmtree(self._InstanceNICDir(instance_name))
791 except OSError, err:
792 if err.errno != errno.ENOENT:
793 raise
794
796 """Move the Xen config file to the log directory and return its new path.
797
798 """
799 old_filename = self._ConfigFileName(instance_name)
800 base = ("%s-%s" %
801 (instance_name, utils.TimestampForFilename()))
802 new_filename = utils.PathJoin(pathutils.LOG_XEN_DIR, base)
803 utils.RenameFile(old_filename, new_filename)
804 return new_filename
805
807 """Wrapper around module level L{_GetAllInstanceList}.
808
809 @type hvparams: dict of strings
810 @param hvparams: hypervisor parameters to be used on this node
811
812 """
813 return _GetAllInstanceList(lambda: self._RunXen(["list"], hvparams),
814 include_node, delays=self._INSTANCE_LIST_DELAYS,
815 timeout=self._INSTANCE_LIST_TIMEOUT)
816
818 """Get the list of running instances.
819
820 @type hvparams: dict of strings
821 @param hvparams: the instance's hypervisor params
822
823 @rtype: list of strings
824 @return: names of running instances
825
826 """
827 instance_list = _GetRunningInstanceList(
828 lambda: self._RunXen(["list"], hvparams),
829 False, delays=self._INSTANCE_LIST_DELAYS,
830 timeout=self._INSTANCE_LIST_TIMEOUT)
831 return [info[0] for info in instance_list]
832
834 """Get instance properties.
835
836 @type instance_name: string
837 @param instance_name: the instance name
838 @type hvparams: dict of strings
839 @param hvparams: the instance's hypervisor params
840
841 @return: tuple (name, id, memory, vcpus, stat, times)
842
843 """
844 instance_list = self._GetInstanceList(instance_name == _DOM0_NAME, hvparams)
845 result = None
846 for data in instance_list:
847 if data[0] == instance_name:
848 result = data
849 break
850 return result
851
853 """Get properties of all instances.
854
855 @type hvparams: dict of strings
856 @param hvparams: hypervisor parameters
857
858 @rtype: (string, string, int, int, HypervisorInstanceState, int)
859 @return: list of tuples (name, id, memory, vcpus, state, times)
860
861 """
862 return self._GetInstanceList(False, hvparams)
863
865 """Gather configuration details and write to disk.
866
867 See L{_GetConfig} for arguments.
868
869 """
870 buf = StringIO()
871 buf.write("# Automatically generated by Ganeti. Do not edit!\n")
872 buf.write("\n")
873 buf.write(self._GetConfig(instance, startup_memory, block_devices))
874 buf.write("\n")
875
876 self._WriteConfigFile(instance.name, buf.getvalue())
877
878 - def StartInstance(self, instance, block_devices, startup_paused):
879 """Start an instance.
880
881 """
882 startup_memory = self._InstanceStartupMemory(instance)
883
884 self._MakeConfigFile(instance, startup_memory, block_devices)
885
886 cmd = ["create"]
887 if startup_paused:
888 cmd.append("-p")
889 cmd.append(self._ConfigFileName(instance.name))
890
891 result = self._RunXen(cmd, instance.hvparams)
892 if result.failed:
893
894
895 stashed_config = self._StashConfigFile(instance.name)
896 raise errors.HypervisorError("Failed to start instance %s: %s (%s). Moved"
897 " config file to %s" %
898 (instance.name, result.fail_reason,
899 result.output, stashed_config))
900
901 for nic_seq, nic in enumerate(instance.nics):
902 if nic.name and nic.name.startswith("gnt.com."):
903 _ConfigureNIC(instance, nic_seq, nic, nic.name)
904
905 - def StopInstance(self, instance, force=False, retry=False, name=None,
906 timeout=None):
907 """Stop an instance.
908
909 A soft shutdown can be interrupted. A hard shutdown tries forever.
910
911 """
912 assert(timeout is None or force is not None)
913
914 if name is None:
915 name = instance.name
916
917 return self._StopInstance(name, force, instance.hvparams, timeout)
918
920 """Shutdown an instance if the instance is running.
921
922 The '-w' flag waits for shutdown to complete which avoids the need
923 to poll in the case where we want to destroy the domain
924 immediately after shutdown.
925
926 @type name: string
927 @param name: name of the instance to stop
928 @type hvparams: dict of string
929 @param hvparams: hypervisor parameters of the instance
930 @type timeout: int or None
931 @param timeout: a timeout after which the shutdown command should be killed,
932 or None for no timeout
933
934 """
935 instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
936
937 if instance_info is None or _IsInstanceShutdown(instance_info[4]):
938 logging.info("Failed to shutdown instance %s, not running", name)
939 return None
940
941 return self._RunXen(["shutdown", "-w", name], hvparams, timeout)
942
944 """Destroy an instance if the instance if the instance exists.
945
946 @type name: string
947 @param name: name of the instance to destroy
948 @type hvparams: dict of string
949 @param hvparams: hypervisor parameters of the instance
950
951 """
952 instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
953
954 if instance_info is None:
955 logging.info("Failed to destroy instance %s, does not exist", name)
956 return None
957
958 return self._RunXen(["destroy", name], hvparams)
959
960
961
962
963
964
965
966
967
976
978 """Stop an instance.
979
980 @type name: string
981 @param name: name of the instance to destroy
982
983 @type force: boolean
984 @param force: whether to do a "hard" stop (destroy)
985
986 @type hvparams: dict of string
987 @param hvparams: hypervisor parameters of the instance
988
989 @type timeout: int or None
990 @param timeout: a timeout after which the shutdown command should be killed,
991 or None for no timeout
992
993 """
994 instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
995
996 if instance_info is None:
997 raise errors.HypervisorError("Failed to shutdown instance %s,"
998 " not running" % name)
999
1000 if not force:
1001 self._ShutdownInstance(name, hvparams, timeout)
1002
1003
1004
1005
1006
1007 result = self._DestroyInstanceIfAlive(name, hvparams)
1008 if result is not None and result.failed and \
1009 self.GetInstanceInfo(name, hvparams=hvparams) is not None:
1010 raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
1011 (name, result.fail_reason, result.output))
1012
1013
1014 self._RemoveConfigFile(name)
1015
1041
1042 try:
1043 utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
1044 self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
1045 except utils.RetryTimeout:
1046 raise errors.HypervisorError("Failed to reboot instance %s: instance"
1047 " did not reboot in the expected interval" %
1048 (instance.name, ))
1049
1051 """Balloon an instance memory to a certain value.
1052
1053 @type instance: L{objects.Instance}
1054 @param instance: instance to be accepted
1055 @type mem: int
1056 @param mem: actual memory size to use for instance runtime
1057
1058 """
1059 result = self._RunXen(["mem-set", instance.name, mem], instance.hvparams)
1060 if result.failed:
1061 raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" %
1062 (instance.name, result.fail_reason,
1063 result.output))
1064
1065
1066 cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem]
1067 cmd.append(self._ConfigFileName(instance.name))
1068
1069 result = utils.RunCmd(cmd)
1070 if result.failed:
1071 raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" %
1072 (instance.name, result.fail_reason,
1073 result.output))
1074
1076 """Return information about the node.
1077
1078 @see: L{_GetNodeInfo} and L{_ParseNodeInfo}
1079
1080 """
1081 result = self._RunXen(["info"], hvparams)
1082 if result.failed:
1083 logging.error("Can't retrieve xen hypervisor information (%s): %s",
1084 result.fail_reason, result.output)
1085 return None
1086
1087 instance_list = self._GetInstanceList(True, hvparams)
1088 return _GetNodeInfo(result.stdout, instance_list)
1089
1090 @classmethod
1091 - def GetInstanceConsole(cls, instance, primary_node, node_group,
1092 hvparams, beparams):
1105
1106 - def Verify(self, hvparams=None):
1107 """Verify the hypervisor.
1108
1109 For Xen, this verifies that the xend process is running.
1110
1111 @type hvparams: dict of strings
1112 @param hvparams: hypervisor parameters to be verified against
1113
1114 @return: Problem description if something is wrong, C{None} otherwise
1115
1116 """
1117 if hvparams is None:
1118 return "Could not verify the hypervisor, because no hvparams were" \
1119 " provided."
1120
1121 if constants.HV_XEN_CMD in hvparams:
1122 xen_cmd = hvparams[constants.HV_XEN_CMD]
1123 try:
1124 self._CheckToolstack(xen_cmd)
1125 except errors.HypervisorError:
1126 return "The configured xen toolstack '%s' is not available on this" \
1127 " node." % xen_cmd
1128
1129 result = self._RunXen(["info"], hvparams)
1130 if result.failed:
1131 return "Retrieving information from xen failed: %s, %s" % \
1132 (result.fail_reason, result.output)
1133
1134 return None
1135
1137 """Get instance information to perform a migration.
1138
1139 @type instance: L{objects.Instance}
1140 @param instance: instance to be migrated
1141 @rtype: string
1142 @return: content of the xen config file
1143
1144 """
1145 return self._ReadConfigFile(instance.name)
1146
1148 """Whether to start a socat daemon when accepting an instance.
1149
1150 @rtype: bool
1151
1152 """
1153 return self._GetCommand(hvparams) == constants.XEN_CMD_XL
1154
1155 @classmethod
1176
1210
1212 """Finalize an instance migration.
1213
1214 After a successful migration we write the xen config file.
1215 We do nothing on a failure, as we did not change anything at accept time.
1216
1217 @type instance: L{objects.Instance}
1218 @param instance: instance whose migration is being finalized
1219 @type info: string
1220 @param info: content of the xen config file on the source node
1221 @type success: boolean
1222 @param success: whether the migration was a success or a failure
1223
1224 """
1225 if success:
1226 self._WriteConfigFile(instance.name, info)
1227 elif self._UseMigrationDaemon(instance.hvparams):
1228 XenHypervisor._KillMigrationDaemon(instance)
1229
1231 """Migrate an instance to a target node.
1232
1233 The migration will not be attempted if the instance is not
1234 currently running.
1235
1236 @type instance: L{objects.Instance}
1237 @param instance: the instance to be migrated
1238 @type target: string
1239 @param target: ip address of the target node
1240 @type live: boolean
1241 @param live: perform a live migration
1242
1243 """
1244 port = instance.hvparams[constants.HV_MIGRATION_PORT]
1245
1246 return self._MigrateInstance(instance.name, target, port, live,
1247 instance.hvparams)
1248
1251 """Migrate an instance to a target node.
1252
1253 @see: L{MigrateInstance} for details
1254
1255 """
1256 if hvparams is None:
1257 raise errors.HypervisorError("No hvparams provided.")
1258
1259 if self.GetInstanceInfo(instance_name, hvparams=hvparams) is None:
1260 raise errors.HypervisorError("Instance not running, cannot migrate")
1261
1262 cmd = self._GetCommand(hvparams)
1263
1264 args = ["migrate"]
1265
1266 if cmd == constants.XEN_CMD_XM:
1267
1268 if not _ping_fn(target, port, live_port_needed=True):
1269 raise errors.HypervisorError("Remote host %s not listening on port"
1270 " %s, cannot migrate" % (target, port))
1271
1272 args.extend(["-p", "%d" % port])
1273 if live:
1274 args.append("-l")
1275
1276 elif cmd == constants.XEN_CMD_XL:
1277
1278
1279 args.extend([
1280 "-s", constants.XL_SOCAT_CMD % (target, port),
1281 "-C", self._ConfigFileName(instance_name),
1282 ])
1283
1284 else:
1285 raise errors.HypervisorError("Unsupported Xen command: %s" % cmd)
1286
1287 args.extend([instance_name, target])
1288
1289 result = self._RunXen(args, hvparams)
1290 if result.failed:
1291 raise errors.HypervisorError("Failed to migrate instance %s: %s" %
1292 (instance_name, result.output))
1293
1295 """Finalize the instance migration on the source node.
1296
1297 @type instance: L{objects.Instance}
1298 @param instance: the instance that was migrated
1299 @type success: bool
1300 @param success: whether the migration succeeded or not
1301 @type live: bool
1302 @param live: whether the user requested a live migration or not
1303
1304 """
1305
1306 if success:
1307
1308 try:
1309 self._RemoveConfigFile(instance.name)
1310 except EnvironmentError:
1311 logging.exception("Failure while removing instance config file")
1312
1314 """Get the migration status
1315
1316 As MigrateInstance for Xen is still blocking, if this method is called it
1317 means that MigrateInstance has completed successfully. So we can safely
1318 assume that the migration was successful and notify this fact to the client.
1319
1320 @type instance: L{objects.Instance}
1321 @param instance: the instance that is being migrated
1322 @rtype: L{objects.MigrationStatus}
1323 @return: the status of the current migration (one of
1324 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
1325 progress info that can be retrieved from the hypervisor
1326
1327 """
1328 return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
1329
1331 """Xen-specific powercycle.
1332
1333 This first does a Linux reboot (which triggers automatically a Xen
1334 reboot), and if that fails it tries to do a Xen reboot. The reason
1335 we don't try a Xen reboot first is that the xen reboot launches an
1336 external command which connects to the Xen hypervisor, and that
1337 won't work in case the root filesystem is broken and/or the xend
1338 daemon is not working.
1339
1340 @type hvparams: dict of strings
1341 @param hvparams: hypervisor params to be used on this node
1342
1343 """
1344 try:
1345 self.LinuxPowercycle()
1346 finally:
1347 xen_cmd = self._GetCommand(hvparams)
1348 utils.RunCmd([xen_cmd, "debug", "R"])
1349
1364
1373
1391
1400
1403 """Xen PVM hypervisor interface"""
1404
1405 PARAMETERS = {
1406 constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
1407 constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
1408 constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
1409 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1410 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
1411 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
1412 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
1413 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1414 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1415
1416 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1417 constants.HV_REBOOT_BEHAVIOR:
1418 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1419 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1420 constants.HV_CPU_CAP: hv_base.OPT_NONNEGATIVE_INT_CHECK,
1421 constants.HV_CPU_WEIGHT:
1422 (False, lambda x: 0 < x < 65536, "invalid weight", None, None),
1423 constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1424 constants.HV_XEN_CMD:
1425 hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1426 constants.HV_XEN_CPUID: hv_base.NO_CHECK,
1427 constants.HV_SOUNDHW: hv_base.NO_CHECK,
1428 }
1429
1430 - def _GetConfig(self, instance, startup_memory, block_devices):
1498
1501 """Xen HVM hypervisor interface"""
1502
1503 ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
1504 pathutils.VNC_PASSWORD_FILE,
1505 ]
1506 ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [
1507 pathutils.VNC_PASSWORD_FILE,
1508 ]
1509
1510 PARAMETERS = {
1511 constants.HV_ACPI: hv_base.NO_CHECK,
1512 constants.HV_BOOT_ORDER: (True, ) +
1513 (lambda x: x and len(x.strip("acdn")) == 0,
1514 "Invalid boot order specified, must be one or more of [acdn]",
1515 None, None),
1516 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
1517 constants.HV_DISK_TYPE:
1518 hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
1519 constants.HV_NIC_TYPE:
1520 hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
1521 constants.HV_PAE: hv_base.NO_CHECK,
1522 constants.HV_VNC_BIND_ADDRESS:
1523 (False, netutils.IP4Address.IsValid,
1524 "VNC bind address is not a valid IP address", None, None),
1525 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1526 constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
1527 constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
1528 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1529 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1530 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
1531
1532 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1533
1534 constants.HV_PASSTHROUGH: hv_base.NO_CHECK,
1535 constants.HV_REBOOT_BEHAVIOR:
1536 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1537 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1538 constants.HV_CPU_CAP: hv_base.NO_CHECK,
1539 constants.HV_CPU_WEIGHT:
1540 (False, lambda x: 0 < x < 65535, "invalid weight", None, None),
1541 constants.HV_VIF_TYPE:
1542 hv_base.ParamInSet(False, constants.HT_HVM_VALID_VIF_TYPES),
1543 constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1544 constants.HV_VIRIDIAN: hv_base.NO_CHECK,
1545 constants.HV_XEN_CMD:
1546 hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1547 constants.HV_XEN_CPUID: hv_base.NO_CHECK,
1548 constants.HV_SOUNDHW: hv_base.NO_CHECK,
1549 }
1550
1551 - def _GetConfig(self, instance, startup_memory, block_devices):
1652