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 """Parses the output of listing instances by xen.
120
121 @type lines: list
122 @param lines: Result of retrieving the instance list from xen
123 @type include_node: boolean
124 @param include_node: If True, return information for Dom0
125 @return: list of tuple containing (name, id, memory, vcpus, state, time
126 spent)
127
128 """
129 result = []
130
131
132 for line in lines[1:]:
133
134
135
136 data = line.split()
137 if len(data) != 6:
138 raise errors.HypervisorError("Can't parse instance list,"
139 " line: %s" % line)
140 try:
141 data[1] = int(data[1])
142 data[2] = int(data[2])
143 data[3] = int(data[3])
144 data[4] = _XenToHypervisorInstanceState(data[4])
145 data[5] = float(data[5])
146 except (TypeError, ValueError), err:
147 raise errors.HypervisorError("Can't parse instance list,"
148 " line: %s, error: %s" % (line, err))
149
150
151 if include_node or data[0] != _DOM0_NAME:
152 result.append(data)
153
154 return result
155
158 """Return the list of instances including running and shutdown.
159
160 See L{_RunInstanceList} and L{_ParseInstanceList} for parameter details.
161
162 """
163 instance_list_errors = []
164 try:
165 lines = utils.Retry(_RunInstanceList, delays, timeout,
166 args=(fn, instance_list_errors))
167 except utils.RetryTimeout:
168 if instance_list_errors:
169 instance_list_result = instance_list_errors.pop()
170
171 errmsg = ("listing instances failed, timeout exceeded (%s): %s" %
172 (instance_list_result.fail_reason, instance_list_result.output))
173 else:
174 errmsg = "listing instances failed"
175
176 raise errors.HypervisorError(errmsg)
177
178 return _ParseInstanceList(lines, include_node)
179
182 """Determine whether an instance is running.
183
184 An instance is running if it is in the following Xen states:
185 running, blocked, paused, or dying (about to be destroyed / shutdown).
186
187 For some strange reason, Xen once printed 'rb----' which does not make any
188 sense because an instance cannot be both running and blocked. Fortunately,
189 for Ganeti 'running' or 'blocked' is the same as 'running'.
190
191 A state of nothing '------' means that the domain is runnable but it is not
192 currently running. That means it is in the queue behind other domains waiting
193 to be scheduled to run.
194 http://old-list-archives.xenproject.org/xen-users/2007-06/msg00849.html
195
196 A dying instance is about to be removed, but it is still consuming resources,
197 and counts as running.
198
199 @type instance_info: string
200 @param instance_info: Information about instance, as supplied by Xen.
201 @rtype: bool
202 @return: Whether an instance is running.
203
204 """
205 return instance_info == "r-----" \
206 or instance_info == "rb----" \
207 or instance_info == "-b----" \
208 or instance_info == "-----d" \
209 or instance_info == "------"
210
213 """Determine whether the instance is shutdown.
214
215 An instance is shutdown when a user shuts it down from within, and we do not
216 remove domains to be able to detect that.
217
218 The dying state has been added as a precaution, as Xen's status reporting is
219 weird.
220
221 """
222 return instance_info == "---s--" \
223 or instance_info == "---s-d"
224
227 """Removes information about whether a Xen state is paused from the state.
228
229 As it turns out, an instance can be reported as paused in almost any
230 condition. Paused instances can be paused, running instances can be paused for
231 scheduling, and any other condition can appear to be paused as a result of
232 races or improbable conditions in Xen's status reporting.
233 As we do not use Xen's pause commands in any way at the time, we can simply
234 ignore the paused field and save ourselves a lot of trouble.
235
236 Should we ever use the pause commands, several samples would be needed before
237 we could confirm the domain as paused.
238
239 """
240 return instance_info.replace('p', '-')
241
244 """Maps Xen states to hypervisor states.
245
246 @type instance_info: string
247 @param instance_info: Information about instance, as supplied by Xen.
248 @rtype: L{hv_base.HvInstanceState}
249
250 """
251 instance_info = _IgnorePaused(instance_info)
252
253 if _IsInstanceRunning(instance_info):
254 return hv_base.HvInstanceState.RUNNING
255 elif _IsInstanceShutdown(instance_info):
256 return hv_base.HvInstanceState.SHUTDOWN
257 else:
258 raise errors.HypervisorError("hv_xen._XenToHypervisorInstanceState:"
259 " unhandled Xen instance state '%s'" %
260 instance_info)
261
264 """Return the list of running instances.
265
266 See L{_GetAllInstanceList} for parameter details.
267
268 """
269 instances = _GetAllInstanceList(fn, include_node, delays, timeout)
270 return [i for i in instances if hv_base.HvInstanceState.IsRunning(i[4])]
271
274 """Return the list of shutdown instances.
275
276 See L{_GetAllInstanceList} for parameter details.
277
278 """
279 instances = _GetAllInstanceList(fn, include_node, delays, timeout)
280 return [i for i in instances if hv_base.HvInstanceState.IsShutdown(i[4])]
281
284 """Return information about the node.
285
286 @return: a dict with the following keys (memory values in MiB):
287 - memory_total: the total memory size on the node
288 - memory_free: the available memory on the node for instances
289 - nr_cpus: total number of CPUs
290 - nr_nodes: in a NUMA system, the number of domains
291 - nr_sockets: the number of physical CPU sockets in the node
292 - hv_version: the hypervisor version in the form (major, minor)
293
294 """
295 result = {}
296 cores_per_socket = threads_per_core = nr_cpus = None
297 xen_major, xen_minor = None, None
298 memory_total = None
299 memory_free = None
300
301 for line in info.splitlines():
302 fields = line.split(":", 1)
303
304 if len(fields) < 2:
305 continue
306
307 (key, val) = map(lambda s: s.strip(), fields)
308
309
310 if key in ("memory", "total_memory"):
311 memory_total = int(val)
312 elif key == "free_memory":
313 memory_free = int(val)
314 elif key == "nr_cpus":
315 nr_cpus = result["cpu_total"] = int(val)
316 elif key == "nr_nodes":
317 result["cpu_nodes"] = int(val)
318 elif key == "cores_per_socket":
319 cores_per_socket = int(val)
320 elif key == "threads_per_core":
321 threads_per_core = int(val)
322 elif key == "xen_major":
323 xen_major = int(val)
324 elif key == "xen_minor":
325 xen_minor = int(val)
326
327 if None not in [cores_per_socket, threads_per_core, nr_cpus]:
328 result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
329
330 if memory_free is not None:
331 result["memory_free"] = memory_free
332
333 if memory_total is not None:
334 result["memory_total"] = memory_total
335
336 if not (xen_major is None or xen_minor is None):
337 result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
338
339 return result
340
343 """Updates node information from L{_ParseNodeInfo} with instance info.
344
345 @type info: dict
346 @param info: Result from L{_ParseNodeInfo}
347 @type instance_list: list of tuples
348 @param instance_list: list of instance information; one tuple per instance
349 @rtype: dict
350
351 """
352 total_instmem = 0
353
354 for (name, _, mem, vcpus, _, _) in instance_list:
355 if name == _DOM0_NAME:
356 info["memory_dom0"] = mem
357 info["cpu_dom0"] = vcpus
358
359
360 total_instmem += mem
361
362 memory_free = info.get("memory_free")
363 memory_total = info.get("memory_total")
364
365
366 if None not in [memory_total, memory_free, total_instmem]:
367 info["memory_hv"] = memory_total - memory_free - total_instmem
368
369 return info
370
373 """Combines L{_MergeInstanceInfo} and L{_ParseNodeInfo}.
374
375 @type instance_list: list of tuples
376 @param instance_list: list of instance information; one tuple per instance
377
378 """
379 return _MergeInstanceInfo(_ParseNodeInfo(info), instance_list)
380
384 """Get disk directives for Xen config file.
385
386 This method builds the xen config disk directive according to the
387 given disk_template and block_devices.
388
389 @param block_devices: list of tuples (cfdev, rldev):
390 - cfdev: dict containing ganeti config disk part
391 - rldev: ganeti.block.bdev.BlockDev object
392 @param blockdev_prefix: a string containing blockdevice prefix,
393 e.g. "sd" for /dev/sda
394
395 @return: string containing disk directive for xen instance config file
396
397 """
398 if len(block_devices) > len(_letters):
399 raise errors.HypervisorError("Too many disks")
400
401 disk_data = []
402
403 for sd_suffix, (cfdev, dev_path, _) in zip(_letters, block_devices):
404 sd_name = blockdev_prefix + sd_suffix
405
406 if cfdev.mode == constants.DISK_RDWR:
407 mode = "w"
408 else:
409 mode = "r"
410
411 if cfdev.dev_type in constants.DTS_FILEBASED:
412 driver = _FILE_DRIVER_MAP[cfdev.logical_id[0]]
413 else:
414 driver = "phy"
415
416 disk_data.append("'%s:%s,%s,%s'" % (driver, dev_path, sd_name, mode))
417
418 return disk_data
419
422 """Add quotes around the CPUID field only if necessary.
423
424 Xen CPUID fields come in two shapes: LIBXL strings, which need quotes around
425 them, and lists of XEND strings, which don't.
426
427 @param data: Either type of parameter.
428 @return: The quoted version thereof.
429
430 """
431 return "'%s'" % data if data.startswith("host") else data
432
435 """Xen generic hypervisor interface
436
437 This is the Xen base class used for both Xen PVM and HVM. It contains
438 all the functionality that is identical for both.
439
440 """
441 CAN_MIGRATE = True
442 REBOOT_RETRY_COUNT = 60
443 REBOOT_RETRY_INTERVAL = 10
444 _ROOT_DIR = pathutils.RUN_DIR + "/xen-hypervisor"
445 _NICS_DIR = _ROOT_DIR + "/nic"
446 _DIRS = [_ROOT_DIR, _NICS_DIR]
447
448 _INSTANCE_LIST_DELAYS = (0.3, 1.5, 1.0)
449 _INSTANCE_LIST_TIMEOUT = 5
450
451 ANCILLARY_FILES = [
452 XEND_CONFIG_FILE,
453 XL_CONFIG_FILE,
454 VIF_BRIDGE_SCRIPT,
455 ]
456 ANCILLARY_FILES_OPT = [
457 XL_CONFIG_FILE,
458 ]
459
460 - def __init__(self, _cfgdir=None, _run_cmd_fn=None, _cmd=None):
474
475 @staticmethod
477 """Returns the Xen command extracted from the given hvparams.
478
479 @type hvparams: dict of strings
480 @param hvparams: hypervisor parameters
481
482 """
483 if hvparams is None or constants.HV_XEN_CMD not in hvparams:
484 raise errors.HypervisorError("Cannot determine xen command.")
485 else:
486 return hvparams[constants.HV_XEN_CMD]
487
504
505 - def _RunXen(self, args, hvparams, timeout=None):
506 """Wrapper around L{utils.process.RunCmd} to run Xen command.
507
508 @type hvparams: dict of strings
509 @param hvparams: dictionary of hypervisor params
510 @type timeout: int or None
511 @param timeout: if a timeout (in seconds) is specified, the command will be
512 terminated after that number of seconds.
513 @see: L{utils.process.RunCmd}
514
515 """
516 cmd = []
517
518 if timeout is not None:
519 cmd.extend(["timeout", str(timeout)])
520
521 cmd.extend([self._GetCommand(hvparams)])
522 cmd.extend(args)
523
524 return self._run_cmd_fn(cmd)
525
527 """Get the config file name for an instance.
528
529 @param instance_name: instance name
530 @type instance_name: str
531 @return: fully qualified path to instance config file
532 @rtype: str
533
534 """
535 return utils.PathJoin(self._cfgdir, instance_name)
536
537 @classmethod
574
575 @classmethod
577 """Returns the directory holding the tap device files for a given instance.
578
579 """
580 return utils.PathJoin(cls._NICS_DIR, instance_name)
581
582 @classmethod
584 """Returns the name of the file containing the tap device for a given NIC
585
586 """
587 return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
588
589 @classmethod
590 - def _GetConfig(cls, instance, startup_memory, block_devices):
591 """Build Xen configuration for an instance.
592
593 """
594 raise NotImplementedError
595
597 """Write the Xen config file for the instance.
598
599 This version of the function just writes the config file from static data.
600
601 """
602
603 utils.RemoveFile(utils.PathJoin(self._cfgdir, "auto", instance_name))
604
605 cfg_file = self._ConfigFileName(instance_name)
606 try:
607 utils.WriteFile(cfg_file, data=data)
608 except EnvironmentError, err:
609 raise errors.HypervisorError("Cannot write Xen instance configuration"
610 " file %s: %s" % (cfg_file, err))
611
613 """Returns the contents of the instance config file.
614
615 """
616 filename = self._ConfigFileName(instance_name)
617
618 try:
619 file_content = utils.ReadFile(filename)
620 except EnvironmentError, err:
621 raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
622
623 return file_content
624
626 """Remove the xen configuration file.
627
628 """
629 utils.RemoveFile(self._ConfigFileName(instance_name))
630 try:
631 shutil.rmtree(self._InstanceNICDir(instance_name))
632 except OSError, err:
633 if err.errno != errno.ENOENT:
634 raise
635
637 """Move the Xen config file to the log directory and return its new path.
638
639 """
640 old_filename = self._ConfigFileName(instance_name)
641 base = ("%s-%s" %
642 (instance_name, utils.TimestampForFilename()))
643 new_filename = utils.PathJoin(pathutils.LOG_XEN_DIR, base)
644 utils.RenameFile(old_filename, new_filename)
645 return new_filename
646
648 """Wrapper around module level L{_GetAllInstanceList}.
649
650 @type hvparams: dict of strings
651 @param hvparams: hypervisor parameters to be used on this node
652
653 """
654 return _GetAllInstanceList(lambda: self._RunXen(["list"], hvparams),
655 include_node, delays=self._INSTANCE_LIST_DELAYS,
656 timeout=self._INSTANCE_LIST_TIMEOUT)
657
659 """Get the list of running instances.
660
661 @type hvparams: dict of strings
662 @param hvparams: the instance's hypervisor params
663
664 @rtype: list of strings
665 @return: names of running instances
666
667 """
668 instance_list = _GetRunningInstanceList(
669 lambda: self._RunXen(["list"], hvparams),
670 False, delays=self._INSTANCE_LIST_DELAYS,
671 timeout=self._INSTANCE_LIST_TIMEOUT)
672 return [info[0] for info in instance_list]
673
675 """Get instance properties.
676
677 @type instance_name: string
678 @param instance_name: the instance name
679 @type hvparams: dict of strings
680 @param hvparams: the instance's hypervisor params
681
682 @return: tuple (name, id, memory, vcpus, stat, times)
683
684 """
685 instance_list = self._GetInstanceList(instance_name == _DOM0_NAME, hvparams)
686 result = None
687 for data in instance_list:
688 if data[0] == instance_name:
689 result = data
690 break
691 return result
692
694 """Get properties of all instances.
695
696 @type hvparams: dict of strings
697 @param hvparams: hypervisor parameters
698
699 @rtype: (string, string, int, int, HypervisorInstanceState, int)
700 @return: list of tuples (name, id, memory, vcpus, state, times)
701
702 """
703 return self._GetInstanceList(False, hvparams)
704
706 """Gather configuration details and write to disk.
707
708 See L{_GetConfig} for arguments.
709
710 """
711 buf = StringIO()
712 buf.write("# Automatically generated by Ganeti. Do not edit!\n")
713 buf.write("\n")
714 buf.write(self._GetConfig(instance, startup_memory, block_devices))
715 buf.write("\n")
716
717 self._WriteConfigFile(instance.name, buf.getvalue())
718
719 - def StartInstance(self, instance, block_devices, startup_paused):
741
742 - def StopInstance(self, instance, force=False, retry=False, name=None,
743 timeout=None):
744 """Stop an instance.
745
746 A soft shutdown can be interrupted. A hard shutdown tries forever.
747
748 """
749 assert(timeout is None or force is not None)
750
751 if name is None:
752 name = instance.name
753
754 return self._StopInstance(name, force, instance.hvparams, timeout)
755
757 """Shutdown an instance if the instance is running.
758
759 The '-w' flag waits for shutdown to complete which avoids the need
760 to poll in the case where we want to destroy the domain
761 immediately after shutdown.
762
763 @type name: string
764 @param name: name of the instance to stop
765 @type hvparams: dict of string
766 @param hvparams: hypervisor parameters of the instance
767 @type timeout: int or None
768 @param timeout: a timeout after which the shutdown command should be killed,
769 or None for no timeout
770
771 """
772 instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
773
774 if instance_info is None or _IsInstanceShutdown(instance_info[4]):
775 logging.info("Failed to shutdown instance %s, not running", name)
776 return None
777
778 return self._RunXen(["shutdown", "-w", name], hvparams, timeout)
779
781 """Destroy an instance if the instance if the instance exists.
782
783 @type name: string
784 @param name: name of the instance to destroy
785 @type hvparams: dict of string
786 @param hvparams: hypervisor parameters of the instance
787
788 """
789 instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
790
791 if instance_info is None:
792 logging.info("Failed to destroy instance %s, does not exist", name)
793 return None
794
795 return self._RunXen(["destroy", name], hvparams)
796
797
798
799
800
801
802
803
804
813
815 """Stop an instance.
816
817 @type name: string
818 @param name: name of the instance to destroy
819
820 @type force: boolean
821 @param force: whether to do a "hard" stop (destroy)
822
823 @type hvparams: dict of string
824 @param hvparams: hypervisor parameters of the instance
825
826 @type timeout: int or None
827 @param timeout: a timeout after which the shutdown command should be killed,
828 or None for no timeout
829
830 """
831 instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
832
833 if instance_info is None:
834 raise errors.HypervisorError("Failed to shutdown instance %s,"
835 " not running" % name)
836
837 if force:
838 result = self._DestroyInstanceIfAlive(name, hvparams)
839 else:
840 self._ShutdownInstance(name, hvparams, timeout)
841 result = self._DestroyInstanceIfAlive(name, hvparams)
842
843 if result is not None and result.failed and \
844 self.GetInstanceInfo(name, hvparams=hvparams) is not None:
845 raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
846 (name, result.fail_reason, result.output))
847
848
849 self._RemoveConfigFile(name)
850
876
877 try:
878 utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
879 self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
880 except utils.RetryTimeout:
881 raise errors.HypervisorError("Failed to reboot instance %s: instance"
882 " did not reboot in the expected interval" %
883 (instance.name, ))
884
886 """Balloon an instance memory to a certain value.
887
888 @type instance: L{objects.Instance}
889 @param instance: instance to be accepted
890 @type mem: int
891 @param mem: actual memory size to use for instance runtime
892
893 """
894 result = self._RunXen(["mem-set", instance.name, mem], instance.hvparams)
895 if result.failed:
896 raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" %
897 (instance.name, result.fail_reason,
898 result.output))
899
900
901 cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem]
902 cmd.append(self._ConfigFileName(instance.name))
903
904 result = utils.RunCmd(cmd)
905 if result.failed:
906 raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" %
907 (instance.name, result.fail_reason,
908 result.output))
909
911 """Return information about the node.
912
913 @see: L{_GetNodeInfo} and L{_ParseNodeInfo}
914
915 """
916 result = self._RunXen(["info"], hvparams)
917 if result.failed:
918 logging.error("Can't retrieve xen hypervisor information (%s): %s",
919 result.fail_reason, result.output)
920 return None
921
922 instance_list = self._GetInstanceList(True, hvparams)
923 return _GetNodeInfo(result.stdout, instance_list)
924
925 @classmethod
940
941 - def Verify(self, hvparams=None):
942 """Verify the hypervisor.
943
944 For Xen, this verifies that the xend process is running.
945
946 @type hvparams: dict of strings
947 @param hvparams: hypervisor parameters to be verified against
948
949 @return: Problem description if something is wrong, C{None} otherwise
950
951 """
952 if hvparams is None:
953 return "Could not verify the hypervisor, because no hvparams were" \
954 " provided."
955
956 if constants.HV_XEN_CMD in hvparams:
957 xen_cmd = hvparams[constants.HV_XEN_CMD]
958 try:
959 self._CheckToolstack(xen_cmd)
960 except errors.HypervisorError:
961 return "The configured xen toolstack '%s' is not available on this" \
962 " node." % xen_cmd
963
964 result = self._RunXen(["info"], hvparams)
965 if result.failed:
966 return "Retrieving information from xen failed: %s, %s" % \
967 (result.fail_reason, result.output)
968
969 return None
970
972 """Get instance information to perform a migration.
973
974 @type instance: L{objects.Instance}
975 @param instance: instance to be migrated
976 @rtype: string
977 @return: content of the xen config file
978
979 """
980 return self._ReadConfigFile(instance.name)
981
983 """Prepare to accept an instance.
984
985 @type instance: L{objects.Instance}
986 @param instance: instance to be accepted
987 @type info: string
988 @param info: content of the xen config file on the source node
989 @type target: string
990 @param target: target host (usually ip), on this node
991
992 """
993 pass
994
996 """Finalize an instance migration.
997
998 After a successful migration we write the xen config file.
999 We do nothing on a failure, as we did not change anything at accept time.
1000
1001 @type instance: L{objects.Instance}
1002 @param instance: instance whose migration is being finalized
1003 @type info: string
1004 @param info: content of the xen config file on the source node
1005 @type success: boolean
1006 @param success: whether the migration was a success or a failure
1007
1008 """
1009 if success:
1010 self._WriteConfigFile(instance.name, info)
1011
1013 """Migrate an instance to a target node.
1014
1015 The migration will not be attempted if the instance is not
1016 currently running.
1017
1018 @type instance: L{objects.Instance}
1019 @param instance: the instance to be migrated
1020 @type target: string
1021 @param target: ip address of the target node
1022 @type live: boolean
1023 @param live: perform a live migration
1024
1025 """
1026 port = instance.hvparams[constants.HV_MIGRATION_PORT]
1027
1028 return self._MigrateInstance(cluster_name, instance.name, target, port,
1029 live, instance.hvparams)
1030
1033 """Migrate an instance to a target node.
1034
1035 @see: L{MigrateInstance} for details
1036
1037 """
1038 if hvparams is None:
1039 raise errors.HypervisorError("No hvparams provided.")
1040
1041 if self.GetInstanceInfo(instance_name, hvparams=hvparams) is None:
1042 raise errors.HypervisorError("Instance not running, cannot migrate")
1043
1044 cmd = self._GetCommand(hvparams)
1045
1046 if (cmd == constants.XEN_CMD_XM and
1047 not _ping_fn(target, port, live_port_needed=True)):
1048 raise errors.HypervisorError("Remote host %s not listening on port"
1049 " %s, cannot migrate" % (target, port))
1050
1051 args = ["migrate"]
1052
1053 if cmd == constants.XEN_CMD_XM:
1054 args.extend(["-p", "%d" % port])
1055 if live:
1056 args.append("-l")
1057
1058 elif cmd == constants.XEN_CMD_XL:
1059 args.extend([
1060 "-s", constants.XL_SSH_CMD % cluster_name,
1061 "-C", self._ConfigFileName(instance_name),
1062 ])
1063
1064 else:
1065 raise errors.HypervisorError("Unsupported Xen command: %s" % self._cmd)
1066
1067 args.extend([instance_name, target])
1068
1069 result = self._RunXen(args, hvparams)
1070 if result.failed:
1071 raise errors.HypervisorError("Failed to migrate instance %s: %s" %
1072 (instance_name, result.output))
1073
1075 """Finalize the instance migration on the source node.
1076
1077 @type instance: L{objects.Instance}
1078 @param instance: the instance that was migrated
1079 @type success: bool
1080 @param success: whether the migration succeeded or not
1081 @type live: bool
1082 @param live: whether the user requested a live migration or not
1083
1084 """
1085
1086 if success:
1087
1088 try:
1089 self._RemoveConfigFile(instance.name)
1090 except EnvironmentError:
1091 logging.exception("Failure while removing instance config file")
1092
1094 """Get the migration status
1095
1096 As MigrateInstance for Xen is still blocking, if this method is called it
1097 means that MigrateInstance has completed successfully. So we can safely
1098 assume that the migration was successful and notify this fact to the client.
1099
1100 @type instance: L{objects.Instance}
1101 @param instance: the instance that is being migrated
1102 @rtype: L{objects.MigrationStatus}
1103 @return: the status of the current migration (one of
1104 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
1105 progress info that can be retrieved from the hypervisor
1106
1107 """
1108 return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
1109
1111 """Xen-specific powercycle.
1112
1113 This first does a Linux reboot (which triggers automatically a Xen
1114 reboot), and if that fails it tries to do a Xen reboot. The reason
1115 we don't try a Xen reboot first is that the xen reboot launches an
1116 external command which connects to the Xen hypervisor, and that
1117 won't work in case the root filesystem is broken and/or the xend
1118 daemon is not working.
1119
1120 @type hvparams: dict of strings
1121 @param hvparams: hypervisor params to be used on this node
1122
1123 """
1124 try:
1125 self.LinuxPowercycle()
1126 finally:
1127 xen_cmd = self._GetCommand(hvparams)
1128 utils.RunCmd([xen_cmd, "debug", "R"])
1129
1144
1153
1171
1180
1183 """Xen PVM hypervisor interface"""
1184
1185 PARAMETERS = {
1186 constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
1187 constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
1188 constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
1189 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1190 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
1191 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
1192 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
1193 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1194 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1195
1196 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1197 constants.HV_REBOOT_BEHAVIOR:
1198 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1199 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1200 constants.HV_CPU_CAP: hv_base.OPT_NONNEGATIVE_INT_CHECK,
1201 constants.HV_CPU_WEIGHT:
1202 (False, lambda x: 0 < x < 65536, "invalid weight", None, None),
1203 constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1204 constants.HV_XEN_CMD:
1205 hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1206 constants.HV_XEN_CPUID: hv_base.NO_CHECK,
1207 constants.HV_SOUNDHW: hv_base.NO_CHECK,
1208 }
1209
1210 - def _GetConfig(self, instance, startup_memory, block_devices):
1211 """Write the Xen config file for the instance.
1212
1213 """
1214 hvp = instance.hvparams
1215 config = StringIO()
1216 config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
1217
1218
1219
1220 if hvp[constants.HV_USE_BOOTLOADER]:
1221
1222 bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
1223 if bootloader_path:
1224 config.write("bootloader = '%s'\n" % bootloader_path)
1225 else:
1226 raise errors.HypervisorError("Bootloader enabled, but missing"
1227 " bootloader path")
1228
1229 bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
1230 if bootloader_args:
1231 config.write("bootargs = '%s'\n" % bootloader_args)
1232 else:
1233
1234 kpath = hvp[constants.HV_KERNEL_PATH]
1235 config.write("kernel = '%s'\n" % kpath)
1236
1237
1238 initrd_path = hvp[constants.HV_INITRD_PATH]
1239 if initrd_path:
1240 config.write("ramdisk = '%s'\n" % initrd_path)
1241
1242
1243 config.write("memory = %d\n" % startup_memory)
1244 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1245 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1246 cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1247 if cpu_pinning:
1248 config.write("%s\n" % cpu_pinning)
1249 cpu_cap = hvp[constants.HV_CPU_CAP]
1250 if cpu_cap:
1251 config.write("cpu_cap=%d\n" % cpu_cap)
1252 cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1253 if cpu_weight:
1254 config.write("cpu_weight=%d\n" % cpu_weight)
1255
1256 config.write("name = '%s'\n" % instance.name)
1257
1258 vif_data = []
1259 for idx, nic in enumerate(instance.nics):
1260 nic_str = "mac=%s" % (nic.mac)
1261 ip = getattr(nic, "ip", None)
1262 if ip is not None:
1263 nic_str += ", ip=%s" % ip
1264 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1265 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1266 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_OVS:
1267 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1268 if nic.nicparams[constants.NIC_VLAN]:
1269 nic_str += "%s" % nic.nicparams[constants.NIC_VLAN]
1270 if hvp[constants.HV_VIF_SCRIPT]:
1271 nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
1272 vif_data.append("'%s'" % nic_str)
1273 self._WriteNICInfoFile(instance, idx, nic)
1274
1275 disk_data = \
1276 _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1277
1278 config.write("vif = [%s]\n" % ",".join(vif_data))
1279 config.write("disk = [%s]\n" % ",".join(disk_data))
1280
1281 if hvp[constants.HV_ROOT_PATH]:
1282 config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
1283
1284 WriteXenConfigEvents(config, hvp)
1285 config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
1286
1287 cpuid = hvp[constants.HV_XEN_CPUID]
1288 if cpuid:
1289 config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1290
1291 if hvp[constants.HV_SOUNDHW]:
1292 config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1293
1294 return config.getvalue()
1295
1298 """Xen HVM hypervisor interface"""
1299
1300 ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
1301 pathutils.VNC_PASSWORD_FILE,
1302 ]
1303 ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [
1304 pathutils.VNC_PASSWORD_FILE,
1305 ]
1306
1307 PARAMETERS = {
1308 constants.HV_ACPI: hv_base.NO_CHECK,
1309 constants.HV_BOOT_ORDER: (True, ) +
1310 (lambda x: x and len(x.strip("acdn")) == 0,
1311 "Invalid boot order specified, must be one or more of [acdn]",
1312 None, None),
1313 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
1314 constants.HV_DISK_TYPE:
1315 hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
1316 constants.HV_NIC_TYPE:
1317 hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
1318 constants.HV_PAE: hv_base.NO_CHECK,
1319 constants.HV_VNC_BIND_ADDRESS:
1320 (False, netutils.IP4Address.IsValid,
1321 "VNC bind address is not a valid IP address", None, None),
1322 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1323 constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
1324 constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
1325 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1326 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1327 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
1328
1329 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1330
1331 constants.HV_PASSTHROUGH: hv_base.NO_CHECK,
1332 constants.HV_REBOOT_BEHAVIOR:
1333 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1334 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1335 constants.HV_CPU_CAP: hv_base.NO_CHECK,
1336 constants.HV_CPU_WEIGHT:
1337 (False, lambda x: 0 < x < 65535, "invalid weight", None, None),
1338 constants.HV_VIF_TYPE:
1339 hv_base.ParamInSet(False, constants.HT_HVM_VALID_VIF_TYPES),
1340 constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1341 constants.HV_VIRIDIAN: hv_base.NO_CHECK,
1342 constants.HV_XEN_CMD:
1343 hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1344 constants.HV_XEN_CPUID: hv_base.NO_CHECK,
1345 constants.HV_SOUNDHW: hv_base.NO_CHECK,
1346 }
1347
1348 - def _GetConfig(self, instance, startup_memory, block_devices):
1349 """Create a Xen 3.1 HVM config file.
1350
1351 """
1352 hvp = instance.hvparams
1353
1354 config = StringIO()
1355
1356
1357 kpath = hvp[constants.HV_KERNEL_PATH]
1358 config.write("kernel = '%s'\n" % kpath)
1359
1360 config.write("builder = 'hvm'\n")
1361 config.write("memory = %d\n" % startup_memory)
1362 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1363 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1364 cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1365 if cpu_pinning:
1366 config.write("%s\n" % cpu_pinning)
1367 cpu_cap = hvp[constants.HV_CPU_CAP]
1368 if cpu_cap:
1369 config.write("cpu_cap=%d\n" % cpu_cap)
1370 cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1371 if cpu_weight:
1372 config.write("cpu_weight=%d\n" % cpu_weight)
1373
1374 config.write("name = '%s'\n" % instance.name)
1375 if hvp[constants.HV_PAE]:
1376 config.write("pae = 1\n")
1377 else:
1378 config.write("pae = 0\n")
1379 if hvp[constants.HV_ACPI]:
1380 config.write("acpi = 1\n")
1381 else:
1382 config.write("acpi = 0\n")
1383 if hvp[constants.HV_VIRIDIAN]:
1384 config.write("viridian = 1\n")
1385 else:
1386 config.write("viridian = 0\n")
1387
1388 config.write("apic = 1\n")
1389 config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
1390 config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
1391 config.write("sdl = 0\n")
1392 config.write("usb = 1\n")
1393 config.write("usbdevice = 'tablet'\n")
1394 config.write("vnc = 1\n")
1395 if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
1396 config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
1397 else:
1398 config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
1399
1400 if instance.network_port > constants.VNC_BASE_PORT:
1401 display = instance.network_port - constants.VNC_BASE_PORT
1402 config.write("vncdisplay = %s\n" % display)
1403 config.write("vncunused = 0\n")
1404 else:
1405 config.write("# vncdisplay = 1\n")
1406 config.write("vncunused = 1\n")
1407
1408 vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
1409 try:
1410 password = utils.ReadFile(vnc_pwd_file)
1411 except EnvironmentError, err:
1412 raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
1413 (vnc_pwd_file, err))
1414
1415 config.write("vncpasswd = '%s'\n" % password.rstrip())
1416
1417 config.write("serial = 'pty'\n")
1418 if hvp[constants.HV_USE_LOCALTIME]:
1419 config.write("localtime = 1\n")
1420
1421 vif_data = []
1422
1423
1424
1425 nic_type = hvp[constants.HV_NIC_TYPE]
1426
1427 if nic_type is None:
1428 vif_type_str = ""
1429 if hvp[constants.HV_VIF_TYPE]:
1430 vif_type_str = ", type=%s" % hvp[constants.HV_VIF_TYPE]
1431
1432 nic_type_str = vif_type_str
1433 elif nic_type == constants.HT_NIC_PARAVIRTUAL:
1434 nic_type_str = ", type=paravirtualized"
1435 else:
1436
1437 nic_type_str = ", model=%s, type=%s" % \
1438 (nic_type, constants.HT_HVM_VIF_IOEMU)
1439 for idx, nic in enumerate(instance.nics):
1440 nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
1441 ip = getattr(nic, "ip", None)
1442 if ip is not None:
1443 nic_str += ", ip=%s" % ip
1444 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1445 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1446 if hvp[constants.HV_VIF_SCRIPT]:
1447 nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
1448 vif_data.append("'%s'" % nic_str)
1449 self._WriteNICInfoFile(instance, idx, nic)
1450
1451 config.write("vif = [%s]\n" % ",".join(vif_data))
1452
1453 disk_data = \
1454 _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1455
1456 iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
1457 if iso_path:
1458 iso = "'file:%s,hdc:cdrom,r'" % iso_path
1459 disk_data.append(iso)
1460
1461 config.write("disk = [%s]\n" % (",".join(disk_data)))
1462
1463 pci_pass_arr = []
1464 pci_pass = hvp[constants.HV_PASSTHROUGH]
1465 if pci_pass:
1466 pci_pass_arr = pci_pass.split(";")
1467 config.write("pci = %s\n" % pci_pass_arr)
1468
1469 WriteXenConfigEvents(config, hvp)
1470
1471 cpuid = hvp[constants.HV_XEN_CPUID]
1472 if cpuid:
1473 config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1474
1475 if hvp[constants.HV_SOUNDHW]:
1476 config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1477
1478 return config.getvalue()
1479