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 force:
1001 result = self._DestroyInstanceIfAlive(name, hvparams)
1002 else:
1003 self._ShutdownInstance(name, hvparams, timeout)
1004 result = self._DestroyInstanceIfAlive(name, hvparams)
1005
1006 if result is not None and result.failed and \
1007 self.GetInstanceInfo(name, hvparams=hvparams) is not None:
1008 raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
1009 (name, result.fail_reason, result.output))
1010
1011
1012 self._RemoveConfigFile(name)
1013
1039
1040 try:
1041 utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
1042 self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
1043 except utils.RetryTimeout:
1044 raise errors.HypervisorError("Failed to reboot instance %s: instance"
1045 " did not reboot in the expected interval" %
1046 (instance.name, ))
1047
1049 """Balloon an instance memory to a certain value.
1050
1051 @type instance: L{objects.Instance}
1052 @param instance: instance to be accepted
1053 @type mem: int
1054 @param mem: actual memory size to use for instance runtime
1055
1056 """
1057 result = self._RunXen(["mem-set", instance.name, mem], instance.hvparams)
1058 if result.failed:
1059 raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" %
1060 (instance.name, result.fail_reason,
1061 result.output))
1062
1063
1064 cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem]
1065 cmd.append(self._ConfigFileName(instance.name))
1066
1067 result = utils.RunCmd(cmd)
1068 if result.failed:
1069 raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" %
1070 (instance.name, result.fail_reason,
1071 result.output))
1072
1074 """Return information about the node.
1075
1076 @see: L{_GetNodeInfo} and L{_ParseNodeInfo}
1077
1078 """
1079 result = self._RunXen(["info"], hvparams)
1080 if result.failed:
1081 logging.error("Can't retrieve xen hypervisor information (%s): %s",
1082 result.fail_reason, result.output)
1083 return None
1084
1085 instance_list = self._GetInstanceList(True, hvparams)
1086 return _GetNodeInfo(result.stdout, instance_list)
1087
1088 @classmethod
1089 - def GetInstanceConsole(cls, instance, primary_node, node_group,
1090 hvparams, beparams):
1103
1104 - def Verify(self, hvparams=None):
1105 """Verify the hypervisor.
1106
1107 For Xen, this verifies that the xend process is running.
1108
1109 @type hvparams: dict of strings
1110 @param hvparams: hypervisor parameters to be verified against
1111
1112 @return: Problem description if something is wrong, C{None} otherwise
1113
1114 """
1115 if hvparams is None:
1116 return "Could not verify the hypervisor, because no hvparams were" \
1117 " provided."
1118
1119 if constants.HV_XEN_CMD in hvparams:
1120 xen_cmd = hvparams[constants.HV_XEN_CMD]
1121 try:
1122 self._CheckToolstack(xen_cmd)
1123 except errors.HypervisorError:
1124 return "The configured xen toolstack '%s' is not available on this" \
1125 " node." % xen_cmd
1126
1127 result = self._RunXen(["info"], hvparams)
1128 if result.failed:
1129 return "Retrieving information from xen failed: %s, %s" % \
1130 (result.fail_reason, result.output)
1131
1132 return None
1133
1135 """Get instance information to perform a migration.
1136
1137 @type instance: L{objects.Instance}
1138 @param instance: instance to be migrated
1139 @rtype: string
1140 @return: content of the xen config file
1141
1142 """
1143 return self._ReadConfigFile(instance.name)
1144
1146 """Whether to start a socat daemon when accepting an instance.
1147
1148 @rtype: bool
1149
1150 """
1151 return self._GetCommand(hvparams) == constants.XEN_CMD_XL
1152
1153 @classmethod
1174
1208
1210 """Finalize an instance migration.
1211
1212 After a successful migration we write the xen config file.
1213 We do nothing on a failure, as we did not change anything at accept time.
1214
1215 @type instance: L{objects.Instance}
1216 @param instance: instance whose migration is being finalized
1217 @type info: string
1218 @param info: content of the xen config file on the source node
1219 @type success: boolean
1220 @param success: whether the migration was a success or a failure
1221
1222 """
1223 if success:
1224 self._WriteConfigFile(instance.name, info)
1225 elif self._UseMigrationDaemon(instance.hvparams):
1226 XenHypervisor._KillMigrationDaemon(instance)
1227
1229 """Migrate an instance to a target node.
1230
1231 The migration will not be attempted if the instance is not
1232 currently running.
1233
1234 @type instance: L{objects.Instance}
1235 @param instance: the instance to be migrated
1236 @type target: string
1237 @param target: ip address of the target node
1238 @type live: boolean
1239 @param live: perform a live migration
1240
1241 """
1242 port = instance.hvparams[constants.HV_MIGRATION_PORT]
1243
1244 return self._MigrateInstance(instance.name, target, port, live,
1245 instance.hvparams)
1246
1249 """Migrate an instance to a target node.
1250
1251 @see: L{MigrateInstance} for details
1252
1253 """
1254 if hvparams is None:
1255 raise errors.HypervisorError("No hvparams provided.")
1256
1257 if self.GetInstanceInfo(instance_name, hvparams=hvparams) is None:
1258 raise errors.HypervisorError("Instance not running, cannot migrate")
1259
1260 cmd = self._GetCommand(hvparams)
1261
1262 args = ["migrate"]
1263
1264 if cmd == constants.XEN_CMD_XM:
1265
1266 if not _ping_fn(target, port, live_port_needed=True):
1267 raise errors.HypervisorError("Remote host %s not listening on port"
1268 " %s, cannot migrate" % (target, port))
1269
1270 args.extend(["-p", "%d" % port])
1271 if live:
1272 args.append("-l")
1273
1274 elif cmd == constants.XEN_CMD_XL:
1275
1276
1277 args.extend([
1278 "-s", constants.XL_SOCAT_CMD % (target, port),
1279 "-C", self._ConfigFileName(instance_name),
1280 ])
1281
1282 else:
1283 raise errors.HypervisorError("Unsupported Xen command: %s" % cmd)
1284
1285 args.extend([instance_name, target])
1286
1287 result = self._RunXen(args, hvparams)
1288 if result.failed:
1289 raise errors.HypervisorError("Failed to migrate instance %s: %s" %
1290 (instance_name, result.output))
1291
1293 """Finalize the instance migration on the source node.
1294
1295 @type instance: L{objects.Instance}
1296 @param instance: the instance that was migrated
1297 @type success: bool
1298 @param success: whether the migration succeeded or not
1299 @type live: bool
1300 @param live: whether the user requested a live migration or not
1301
1302 """
1303
1304 if success:
1305
1306 try:
1307 self._RemoveConfigFile(instance.name)
1308 except EnvironmentError:
1309 logging.exception("Failure while removing instance config file")
1310
1312 """Get the migration status
1313
1314 As MigrateInstance for Xen is still blocking, if this method is called it
1315 means that MigrateInstance has completed successfully. So we can safely
1316 assume that the migration was successful and notify this fact to the client.
1317
1318 @type instance: L{objects.Instance}
1319 @param instance: the instance that is being migrated
1320 @rtype: L{objects.MigrationStatus}
1321 @return: the status of the current migration (one of
1322 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
1323 progress info that can be retrieved from the hypervisor
1324
1325 """
1326 return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
1327
1329 """Xen-specific powercycle.
1330
1331 This first does a Linux reboot (which triggers automatically a Xen
1332 reboot), and if that fails it tries to do a Xen reboot. The reason
1333 we don't try a Xen reboot first is that the xen reboot launches an
1334 external command which connects to the Xen hypervisor, and that
1335 won't work in case the root filesystem is broken and/or the xend
1336 daemon is not working.
1337
1338 @type hvparams: dict of strings
1339 @param hvparams: hypervisor params to be used on this node
1340
1341 """
1342 try:
1343 self.LinuxPowercycle()
1344 finally:
1345 xen_cmd = self._GetCommand(hvparams)
1346 utils.RunCmd([xen_cmd, "debug", "R"])
1347
1362
1371
1389
1398
1401 """Xen PVM hypervisor interface"""
1402
1403 PARAMETERS = {
1404 constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
1405 constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
1406 constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
1407 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1408 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
1409 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
1410 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
1411 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1412 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1413
1414 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1415 constants.HV_REBOOT_BEHAVIOR:
1416 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1417 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1418 constants.HV_CPU_CAP: hv_base.OPT_NONNEGATIVE_INT_CHECK,
1419 constants.HV_CPU_WEIGHT:
1420 (False, lambda x: 0 < x < 65536, "invalid weight", None, None),
1421 constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1422 constants.HV_XEN_CMD:
1423 hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1424 constants.HV_XEN_CPUID: hv_base.NO_CHECK,
1425 constants.HV_SOUNDHW: hv_base.NO_CHECK,
1426 }
1427
1428 - def _GetConfig(self, instance, startup_memory, block_devices):
1496
1499 """Xen HVM hypervisor interface"""
1500
1501 ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
1502 pathutils.VNC_PASSWORD_FILE,
1503 ]
1504 ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [
1505 pathutils.VNC_PASSWORD_FILE,
1506 ]
1507
1508 PARAMETERS = {
1509 constants.HV_ACPI: hv_base.NO_CHECK,
1510 constants.HV_BOOT_ORDER: (True, ) +
1511 (lambda x: x and len(x.strip("acdn")) == 0,
1512 "Invalid boot order specified, must be one or more of [acdn]",
1513 None, None),
1514 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
1515 constants.HV_DISK_TYPE:
1516 hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
1517 constants.HV_NIC_TYPE:
1518 hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
1519 constants.HV_PAE: hv_base.NO_CHECK,
1520 constants.HV_VNC_BIND_ADDRESS:
1521 (False, netutils.IP4Address.IsValid,
1522 "VNC bind address is not a valid IP address", None, None),
1523 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1524 constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
1525 constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
1526 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1527 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1528 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
1529
1530 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1531
1532 constants.HV_PASSTHROUGH: hv_base.NO_CHECK,
1533 constants.HV_REBOOT_BEHAVIOR:
1534 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1535 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1536 constants.HV_CPU_CAP: hv_base.NO_CHECK,
1537 constants.HV_CPU_WEIGHT:
1538 (False, lambda x: 0 < x < 65535, "invalid weight", None, None),
1539 constants.HV_VIF_TYPE:
1540 hv_base.ParamInSet(False, constants.HT_HVM_VALID_VIF_TYPES),
1541 constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1542 constants.HV_VIRIDIAN: hv_base.NO_CHECK,
1543 constants.HV_XEN_CMD:
1544 hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1545 constants.HV_XEN_CPUID: hv_base.NO_CHECK,
1546 constants.HV_SOUNDHW: hv_base.NO_CHECK,
1547 }
1548
1549 - def _GetConfig(self, instance, startup_memory, block_devices):
1650