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{_GetInstanceList} to retrieve the list of instances
98 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[5] = float(data[5])
145 except (TypeError, ValueError), err:
146 raise errors.HypervisorError("Can't parse instance list,"
147 " line: %s, error: %s" % (line, err))
148
149
150 if include_node or data[0] != _DOM0_NAME:
151 result.append(data)
152
153 return result
154
157 """Return the list of running instances.
158
159 See L{_RunInstanceList} and L{_ParseInstanceList} for parameter details.
160
161 """
162 instance_list_errors = []
163 try:
164 lines = utils.Retry(_RunInstanceList, (0.3, 1.5, 1.0), _timeout,
165 args=(fn, instance_list_errors))
166 except utils.RetryTimeout:
167 if instance_list_errors:
168 instance_list_result = instance_list_errors.pop()
169
170 errmsg = ("listing instances failed, timeout exceeded (%s): %s" %
171 (instance_list_result.fail_reason, instance_list_result.output))
172 else:
173 errmsg = "listing instances failed"
174
175 raise errors.HypervisorError(errmsg)
176
177 return _ParseInstanceList(lines, include_node)
178
181 """Determine whether an instance is running.
182
183 An instance is running if it is in the following Xen states:
184 running, blocked, or paused.
185
186 For some strange reason, Xen once printed 'rb----' which does not make any
187 sense because an instance cannot be both running and blocked. Fortunately,
188 for Ganeti 'running' or 'blocked' is the same as 'running'.
189
190 A state of nothing '------' means that the domain is runnable but it is not
191 currently running. That means it is in the queue behind other domains waiting
192 to be scheduled to run.
193 http://old-list-archives.xenproject.org/xen-users/2007-06/msg00849.html
194
195 @type instance_info: string
196 @param instance_info: Information about instance, as supplied by Xen.
197 @rtype: bool
198 @return: Whether an instance is running.
199
200 """
201 return instance_info == "r-----" \
202 or instance_info == "rb----" \
203 or instance_info == "-b----" \
204 or instance_info == "--p---" \
205 or instance_info == "------"
206
209 return instance_info == "---s--"
210
213 """Return information about the node.
214
215 @return: a dict with the following keys (memory values in MiB):
216 - memory_total: the total memory size on the node
217 - memory_free: the available memory on the node for instances
218 - nr_cpus: total number of CPUs
219 - nr_nodes: in a NUMA system, the number of domains
220 - nr_sockets: the number of physical CPU sockets in the node
221 - hv_version: the hypervisor version in the form (major, minor)
222
223 """
224 result = {}
225 cores_per_socket = threads_per_core = nr_cpus = None
226 xen_major, xen_minor = None, None
227 memory_total = None
228 memory_free = None
229
230 for line in info.splitlines():
231 fields = line.split(":", 1)
232
233 if len(fields) < 2:
234 continue
235
236 (key, val) = map(lambda s: s.strip(), fields)
237
238
239 if key in ("memory", "total_memory"):
240 memory_total = int(val)
241 elif key == "free_memory":
242 memory_free = int(val)
243 elif key == "nr_cpus":
244 nr_cpus = result["cpu_total"] = int(val)
245 elif key == "nr_nodes":
246 result["cpu_nodes"] = int(val)
247 elif key == "cores_per_socket":
248 cores_per_socket = int(val)
249 elif key == "threads_per_core":
250 threads_per_core = int(val)
251 elif key == "xen_major":
252 xen_major = int(val)
253 elif key == "xen_minor":
254 xen_minor = int(val)
255
256 if None not in [cores_per_socket, threads_per_core, nr_cpus]:
257 result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
258
259 if memory_free is not None:
260 result["memory_free"] = memory_free
261
262 if memory_total is not None:
263 result["memory_total"] = memory_total
264
265 if not (xen_major is None or xen_minor is None):
266 result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
267
268 return result
269
272 """Updates node information from L{_ParseNodeInfo} with instance info.
273
274 @type info: dict
275 @param info: Result from L{_ParseNodeInfo}
276 @type instance_list: list of tuples
277 @param instance_list: list of instance information; one tuple per instance
278 @rtype: dict
279
280 """
281 total_instmem = 0
282
283 for (name, _, mem, vcpus, _, _) in instance_list:
284 if name == _DOM0_NAME:
285 info["memory_dom0"] = mem
286 info["cpu_dom0"] = vcpus
287
288
289 total_instmem += mem
290
291 memory_free = info.get("memory_free")
292 memory_total = info.get("memory_total")
293
294
295 if None not in [memory_total, memory_free, total_instmem]:
296 info["memory_hv"] = memory_total - memory_free - total_instmem
297
298 return info
299
302 """Combines L{_MergeInstanceInfo} and L{_ParseNodeInfo}.
303
304 @type instance_list: list of tuples
305 @param instance_list: list of instance information; one tuple per instance
306
307 """
308 return _MergeInstanceInfo(_ParseNodeInfo(info), instance_list)
309
313 """Get disk directives for Xen config file.
314
315 This method builds the xen config disk directive according to the
316 given disk_template and block_devices.
317
318 @param block_devices: list of tuples (cfdev, rldev):
319 - cfdev: dict containing ganeti config disk part
320 - rldev: ganeti.block.bdev.BlockDev object
321 @param blockdev_prefix: a string containing blockdevice prefix,
322 e.g. "sd" for /dev/sda
323
324 @return: string containing disk directive for xen instance config file
325
326 """
327 if len(block_devices) > len(_letters):
328 raise errors.HypervisorError("Too many disks")
329
330 disk_data = []
331
332 for sd_suffix, (cfdev, dev_path, _) in zip(_letters, block_devices):
333 sd_name = blockdev_prefix + sd_suffix
334
335 if cfdev.mode == constants.DISK_RDWR:
336 mode = "w"
337 else:
338 mode = "r"
339
340 if cfdev.dev_type in [constants.DT_FILE, constants.DT_SHARED_FILE]:
341 driver = _FILE_DRIVER_MAP[cfdev.logical_id[0]]
342 else:
343 driver = "phy"
344
345 disk_data.append("'%s:%s,%s,%s'" % (driver, dev_path, sd_name, mode))
346
347 return disk_data
348
351 """Add quotes around the CPUID field only if necessary.
352
353 Xen CPUID fields come in two shapes: LIBXL strings, which need quotes around
354 them, and lists of XEND strings, which don't.
355
356 @param data: Either type of parameter.
357 @return: The quoted version thereof.
358
359 """
360 return "'%s'" % data if data.startswith("host") else data
361
364 """Xen generic hypervisor interface
365
366 This is the Xen base class used for both Xen PVM and HVM. It contains
367 all the functionality that is identical for both.
368
369 """
370 CAN_MIGRATE = True
371 REBOOT_RETRY_COUNT = 60
372 REBOOT_RETRY_INTERVAL = 10
373 _ROOT_DIR = pathutils.RUN_DIR + "/xen-hypervisor"
374 _NICS_DIR = _ROOT_DIR + "/nic"
375 _DIRS = [_ROOT_DIR, _NICS_DIR]
376
377 ANCILLARY_FILES = [
378 XEND_CONFIG_FILE,
379 XL_CONFIG_FILE,
380 VIF_BRIDGE_SCRIPT,
381 ]
382 ANCILLARY_FILES_OPT = [
383 XL_CONFIG_FILE,
384 ]
385
386 - def __init__(self, _cfgdir=None, _run_cmd_fn=None, _cmd=None):
400
401 @staticmethod
403 """Returns the Xen command extracted from the given hvparams.
404
405 @type hvparams: dict of strings
406 @param hvparams: hypervisor parameters
407
408 """
409 if hvparams is None or constants.HV_XEN_CMD not in hvparams:
410 raise errors.HypervisorError("Cannot determine xen command.")
411 else:
412 return hvparams[constants.HV_XEN_CMD]
413
430
431 - def _RunXen(self, args, hvparams, timeout=None):
432 """Wrapper around L{utils.process.RunCmd} to run Xen command.
433
434 @type hvparams: dict of strings
435 @param hvparams: dictionary of hypervisor params
436 @type timeout: int or None
437 @param timeout: if a timeout (in seconds) is specified, the command will be
438 terminated after that number of seconds.
439 @see: L{utils.process.RunCmd}
440
441 """
442 cmd = []
443
444 if timeout is not None:
445 cmd.extend(["timeout", str(timeout)])
446
447 cmd.extend([self._GetCommand(hvparams)])
448 cmd.extend(args)
449
450 return self._run_cmd_fn(cmd)
451
453 """Get the config file name for an instance.
454
455 @param instance_name: instance name
456 @type instance_name: str
457 @return: fully qualified path to instance config file
458 @rtype: str
459
460 """
461 return utils.PathJoin(self._cfgdir, instance_name)
462
463 @classmethod
500
501 @classmethod
503 """Returns the directory holding the tap device files for a given instance.
504
505 """
506 return utils.PathJoin(cls._NICS_DIR, instance_name)
507
508 @classmethod
510 """Returns the name of the file containing the tap device for a given NIC
511
512 """
513 return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
514
515 @classmethod
516 - def _GetConfig(cls, instance, startup_memory, block_devices):
517 """Build Xen configuration for an instance.
518
519 """
520 raise NotImplementedError
521
523 """Write the Xen config file for the instance.
524
525 This version of the function just writes the config file from static data.
526
527 """
528
529 utils.RemoveFile(utils.PathJoin(self._cfgdir, "auto", instance_name))
530
531 cfg_file = self._ConfigFileName(instance_name)
532 try:
533 utils.WriteFile(cfg_file, data=data)
534 except EnvironmentError, err:
535 raise errors.HypervisorError("Cannot write Xen instance configuration"
536 " file %s: %s" % (cfg_file, err))
537
539 """Returns the contents of the instance config file.
540
541 """
542 filename = self._ConfigFileName(instance_name)
543
544 try:
545 file_content = utils.ReadFile(filename)
546 except EnvironmentError, err:
547 raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
548
549 return file_content
550
552 """Remove the xen configuration file.
553
554 """
555 utils.RemoveFile(self._ConfigFileName(instance_name))
556 try:
557 shutil.rmtree(self._InstanceNICDir(instance_name))
558 except OSError, err:
559 if err.errno != errno.ENOENT:
560 raise
561
563 """Move the Xen config file to the log directory and return its new path.
564
565 """
566 old_filename = self._ConfigFileName(instance_name)
567 base = ("%s-%s" %
568 (instance_name, utils.TimestampForFilename()))
569 new_filename = utils.PathJoin(pathutils.LOG_XEN_DIR, base)
570 utils.RenameFile(old_filename, new_filename)
571 return new_filename
572
574 """Wrapper around module level L{_GetInstanceList}.
575
576 @type hvparams: dict of strings
577 @param hvparams: hypervisor parameters to be used on this node
578
579 """
580 return _GetInstanceList(lambda: self._RunXen(["list"], hvparams),
581 include_node)
582
584 """Get the list of running instances.
585
586 """
587 instance_list = self._GetInstanceList(False, hvparams)
588 names = [info[0] for info in instance_list]
589 return names
590
592 """Get instance properties.
593
594 @type instance_name: string
595 @param instance_name: the instance name
596 @type hvparams: dict of strings
597 @param hvparams: the instance's hypervisor params
598
599 @return: tuple (name, id, memory, vcpus, stat, times)
600
601 """
602 instance_list = self._GetInstanceList(instance_name == _DOM0_NAME, hvparams)
603 result = None
604 for data in instance_list:
605 if data[0] == instance_name:
606 result = data
607 break
608 return result
609
611 """Get properties of all instances.
612
613 @type hvparams: dict of strings
614 @param hvparams: hypervisor parameters
615 @return: list of tuples (name, id, memory, vcpus, stat, times)
616
617 """
618 return self._GetInstanceList(False, hvparams)
619
621 """Gather configuration details and write to disk.
622
623 See L{_GetConfig} for arguments.
624
625 """
626 buf = StringIO()
627 buf.write("# Automatically generated by Ganeti. Do not edit!\n")
628 buf.write("\n")
629 buf.write(self._GetConfig(instance, startup_memory, block_devices))
630 buf.write("\n")
631
632 self._WriteConfigFile(instance.name, buf.getvalue())
633
634 - def StartInstance(self, instance, block_devices, startup_paused):
656
657 - def StopInstance(self, instance, force=False, retry=False, name=None,
658 timeout=None):
659 """Stop an instance.
660
661 A soft shutdown can be interrupted. A hard shutdown tries forever.
662
663 """
664 assert(timeout is None or force is not None)
665
666 if name is None:
667 name = instance.name
668
669 return self._StopInstance(name, force, instance.hvparams, timeout)
670
672 """Shutdown an instance if the instance is running.
673
674 The '-w' flag waits for shutdown to complete which avoids the need
675 to poll in the case where we want to destroy the domain
676 immediately after shutdown.
677
678 @type name: string
679 @param name: name of the instance to stop
680 @type hvparams: dict of string
681 @param hvparams: hypervisor parameters of the instance
682 @type timeout: int or None
683 @param timeout: a timeout after which the shutdown command should be killed,
684 or None for no timeout
685
686 """
687 instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
688
689 if instance_info is None or _IsInstanceShutdown(instance_info[4]):
690 logging.info("Failed to shutdown instance %s, not running", name)
691 return None
692
693 return self._RunXen(["shutdown", "-w", name], hvparams, timeout)
694
696 """Destroy an instance if the instance if the instance exists.
697
698 @type name: string
699 @param name: name of the instance to destroy
700 @type hvparams: dict of string
701 @param hvparams: hypervisor parameters of the instance
702
703 """
704 instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
705
706 if instance_info is None:
707 logging.info("Failed to destroy instance %s, does not exist", name)
708 return None
709
710 return self._RunXen(["destroy", name], hvparams)
711
713 """Stop an instance.
714
715 @type name: string
716 @param name: name of the instance to destroy
717
718 @type force: boolean
719 @param force: whether to do a "hard" stop (destroy)
720
721 @type hvparams: dict of string
722 @param hvparams: hypervisor parameters of the instance
723
724 @type timeout: int or None
725 @param timeout: a timeout after which the shutdown command should be killed,
726 or None for no timeout
727
728 """
729 if force:
730 result = self._DestroyInstance(name, hvparams)
731 else:
732 self._ShutdownInstance(name, hvparams, timeout)
733 result = self._DestroyInstance(name, hvparams)
734
735 if result is not None and result.failed and \
736 self.GetInstanceInfo(name, hvparams=hvparams) is not None:
737 raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
738 (name, result.fail_reason, result.output))
739
740
741 self._RemoveConfigFile(name)
742
768
769 try:
770 utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
771 self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
772 except utils.RetryTimeout:
773 raise errors.HypervisorError("Failed to reboot instance %s: instance"
774 " did not reboot in the expected interval" %
775 (instance.name, ))
776
778 """Balloon an instance memory to a certain value.
779
780 @type instance: L{objects.Instance}
781 @param instance: instance to be accepted
782 @type mem: int
783 @param mem: actual memory size to use for instance runtime
784
785 """
786 result = self._RunXen(["mem-set", instance.name, mem], instance.hvparams)
787 if result.failed:
788 raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" %
789 (instance.name, result.fail_reason,
790 result.output))
791
792
793 cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem]
794 cmd.append(self._ConfigFileName(instance.name))
795
796 result = utils.RunCmd(cmd)
797 if result.failed:
798 raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" %
799 (instance.name, result.fail_reason,
800 result.output))
801
803 """Return information about the node.
804
805 @see: L{_GetNodeInfo} and L{_ParseNodeInfo}
806
807 """
808 result = self._RunXen(["info"], hvparams)
809 if result.failed:
810 logging.error("Can't retrieve xen hypervisor information (%s): %s",
811 result.fail_reason, result.output)
812 return None
813
814 instance_list = self._GetInstanceList(True, hvparams)
815 return _GetNodeInfo(result.stdout, instance_list)
816
817 @classmethod
829
830 - def Verify(self, hvparams=None):
831 """Verify the hypervisor.
832
833 For Xen, this verifies that the xend process is running.
834
835 @type hvparams: dict of strings
836 @param hvparams: hypervisor parameters to be verified against
837
838 @return: Problem description if something is wrong, C{None} otherwise
839
840 """
841 if hvparams is None:
842 return "Could not verify the hypervisor, because no hvparams were" \
843 " provided."
844
845 if constants.HV_XEN_CMD in hvparams:
846 xen_cmd = hvparams[constants.HV_XEN_CMD]
847 try:
848 self._CheckToolstack(xen_cmd)
849 except errors.HypervisorError:
850 return "The configured xen toolstack '%s' is not available on this" \
851 " node." % xen_cmd
852
853 result = self._RunXen(["info"], hvparams)
854 if result.failed:
855 return "Retrieving information from xen failed: %s, %s" % \
856 (result.fail_reason, result.output)
857
858 return None
859
861 """Get instance information to perform a migration.
862
863 @type instance: L{objects.Instance}
864 @param instance: instance to be migrated
865 @rtype: string
866 @return: content of the xen config file
867
868 """
869 return self._ReadConfigFile(instance.name)
870
872 """Prepare to accept an instance.
873
874 @type instance: L{objects.Instance}
875 @param instance: instance to be accepted
876 @type info: string
877 @param info: content of the xen config file on the source node
878 @type target: string
879 @param target: target host (usually ip), on this node
880
881 """
882 pass
883
885 """Finalize an instance migration.
886
887 After a successful migration we write the xen config file.
888 We do nothing on a failure, as we did not change anything at accept time.
889
890 @type instance: L{objects.Instance}
891 @param instance: instance whose migration is being finalized
892 @type info: string
893 @param info: content of the xen config file on the source node
894 @type success: boolean
895 @param success: whether the migration was a success or a failure
896
897 """
898 if success:
899 self._WriteConfigFile(instance.name, info)
900
902 """Migrate an instance to a target node.
903
904 The migration will not be attempted if the instance is not
905 currently running.
906
907 @type instance: L{objects.Instance}
908 @param instance: the instance to be migrated
909 @type target: string
910 @param target: ip address of the target node
911 @type live: boolean
912 @param live: perform a live migration
913
914 """
915 port = instance.hvparams[constants.HV_MIGRATION_PORT]
916
917 return self._MigrateInstance(cluster_name, instance.name, target, port,
918 live, instance.hvparams)
919
922 """Migrate an instance to a target node.
923
924 @see: L{MigrateInstance} for details
925
926 """
927 if hvparams is None:
928 raise errors.HypervisorError("No hvparams provided.")
929
930 if self.GetInstanceInfo(instance_name, hvparams=hvparams) is None:
931 raise errors.HypervisorError("Instance not running, cannot migrate")
932
933 cmd = self._GetCommand(hvparams)
934
935 if (cmd == constants.XEN_CMD_XM and
936 not _ping_fn(target, port, live_port_needed=True)):
937 raise errors.HypervisorError("Remote host %s not listening on port"
938 " %s, cannot migrate" % (target, port))
939
940 args = ["migrate"]
941
942 if cmd == constants.XEN_CMD_XM:
943 args.extend(["-p", "%d" % port])
944 if live:
945 args.append("-l")
946
947 elif cmd == constants.XEN_CMD_XL:
948 args.extend([
949 "-s", constants.XL_SSH_CMD % cluster_name,
950 "-C", self._ConfigFileName(instance_name),
951 ])
952
953 else:
954 raise errors.HypervisorError("Unsupported Xen command: %s" % self._cmd)
955
956 args.extend([instance_name, target])
957
958 result = self._RunXen(args, hvparams)
959 if result.failed:
960 raise errors.HypervisorError("Failed to migrate instance %s: %s" %
961 (instance_name, result.output))
962
964 """Finalize the instance migration on the source node.
965
966 @type instance: L{objects.Instance}
967 @param instance: the instance that was migrated
968 @type success: bool
969 @param success: whether the migration succeeded or not
970 @type live: bool
971 @param live: whether the user requested a live migration or not
972
973 """
974
975 if success:
976
977 try:
978 self._RemoveConfigFile(instance.name)
979 except EnvironmentError:
980 logging.exception("Failure while removing instance config file")
981
983 """Get the migration status
984
985 As MigrateInstance for Xen is still blocking, if this method is called it
986 means that MigrateInstance has completed successfully. So we can safely
987 assume that the migration was successful and notify this fact to the client.
988
989 @type instance: L{objects.Instance}
990 @param instance: the instance that is being migrated
991 @rtype: L{objects.MigrationStatus}
992 @return: the status of the current migration (one of
993 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
994 progress info that can be retrieved from the hypervisor
995
996 """
997 return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
998
1000 """Xen-specific powercycle.
1001
1002 This first does a Linux reboot (which triggers automatically a Xen
1003 reboot), and if that fails it tries to do a Xen reboot. The reason
1004 we don't try a Xen reboot first is that the xen reboot launches an
1005 external command which connects to the Xen hypervisor, and that
1006 won't work in case the root filesystem is broken and/or the xend
1007 daemon is not working.
1008
1009 @type hvparams: dict of strings
1010 @param hvparams: hypervisor params to be used on this node
1011
1012 """
1013 try:
1014 self.LinuxPowercycle()
1015 finally:
1016 xen_cmd = self._GetCommand(hvparams)
1017 utils.RunCmd([xen_cmd, "debug", "R"])
1018
1033
1042
1060
1063 """Xen PVM hypervisor interface"""
1064
1065 PARAMETERS = {
1066 constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
1067 constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
1068 constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
1069 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1070 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
1071 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
1072 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
1073 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1074 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1075
1076 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1077 constants.HV_REBOOT_BEHAVIOR:
1078 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1079 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1080 constants.HV_CPU_CAP: hv_base.OPT_NONNEGATIVE_INT_CHECK,
1081 constants.HV_CPU_WEIGHT:
1082 (False, lambda x: 0 < x < 65536, "invalid weight", None, None),
1083 constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1084 constants.HV_XEN_CMD:
1085 hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1086 constants.HV_XEN_CPUID: hv_base.NO_CHECK,
1087 constants.HV_SOUNDHW: hv_base.NO_CHECK,
1088 }
1089
1090 - def _GetConfig(self, instance, startup_memory, block_devices):
1091 """Write the Xen config file for the instance.
1092
1093 """
1094 hvp = instance.hvparams
1095 config = StringIO()
1096 config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
1097
1098
1099
1100 if hvp[constants.HV_USE_BOOTLOADER]:
1101
1102 bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
1103 if bootloader_path:
1104 config.write("bootloader = '%s'\n" % bootloader_path)
1105 else:
1106 raise errors.HypervisorError("Bootloader enabled, but missing"
1107 " bootloader path")
1108
1109 bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
1110 if bootloader_args:
1111 config.write("bootargs = '%s'\n" % bootloader_args)
1112 else:
1113
1114 kpath = hvp[constants.HV_KERNEL_PATH]
1115 config.write("kernel = '%s'\n" % kpath)
1116
1117
1118 initrd_path = hvp[constants.HV_INITRD_PATH]
1119 if initrd_path:
1120 config.write("ramdisk = '%s'\n" % initrd_path)
1121
1122
1123 config.write("memory = %d\n" % startup_memory)
1124 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1125 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1126 cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1127 if cpu_pinning:
1128 config.write("%s\n" % cpu_pinning)
1129 cpu_cap = hvp[constants.HV_CPU_CAP]
1130 if cpu_cap:
1131 config.write("cpu_cap=%d\n" % cpu_cap)
1132 cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1133 if cpu_weight:
1134 config.write("cpu_weight=%d\n" % cpu_weight)
1135
1136 config.write("name = '%s'\n" % instance.name)
1137
1138 vif_data = []
1139 for idx, nic in enumerate(instance.nics):
1140 nic_str = "mac=%s" % (nic.mac)
1141 ip = getattr(nic, "ip", None)
1142 if ip is not None:
1143 nic_str += ", ip=%s" % ip
1144 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1145 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1146 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_OVS:
1147 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1148 if nic.nicparams[constants.NIC_VLAN]:
1149 nic_str += "%s" % nic.nicparams[constants.NIC_VLAN]
1150 if hvp[constants.HV_VIF_SCRIPT]:
1151 nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
1152 vif_data.append("'%s'" % nic_str)
1153 self._WriteNICInfoFile(instance, idx, nic)
1154
1155 disk_data = \
1156 _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1157
1158 config.write("vif = [%s]\n" % ",".join(vif_data))
1159 config.write("disk = [%s]\n" % ",".join(disk_data))
1160
1161 if hvp[constants.HV_ROOT_PATH]:
1162 config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
1163 config.write("on_poweroff = 'destroy'\n")
1164 if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
1165 config.write("on_reboot = 'restart'\n")
1166 else:
1167 config.write("on_reboot = 'destroy'\n")
1168 config.write("on_crash = 'restart'\n")
1169 config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
1170
1171 cpuid = hvp[constants.HV_XEN_CPUID]
1172 if cpuid:
1173 config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1174
1175 if hvp[constants.HV_SOUNDHW]:
1176 config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1177
1178 return config.getvalue()
1179
1182 """Xen HVM hypervisor interface"""
1183
1184 ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
1185 pathutils.VNC_PASSWORD_FILE,
1186 ]
1187 ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [
1188 pathutils.VNC_PASSWORD_FILE,
1189 ]
1190
1191 PARAMETERS = {
1192 constants.HV_ACPI: hv_base.NO_CHECK,
1193 constants.HV_BOOT_ORDER: (True, ) +
1194 (lambda x: x and len(x.strip("acdn")) == 0,
1195 "Invalid boot order specified, must be one or more of [acdn]",
1196 None, None),
1197 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
1198 constants.HV_DISK_TYPE:
1199 hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
1200 constants.HV_NIC_TYPE:
1201 hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
1202 constants.HV_PAE: hv_base.NO_CHECK,
1203 constants.HV_VNC_BIND_ADDRESS:
1204 (False, netutils.IP4Address.IsValid,
1205 "VNC bind address is not a valid IP address", None, None),
1206 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1207 constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
1208 constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
1209 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1210 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1211 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
1212
1213 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1214
1215 constants.HV_PASSTHROUGH: hv_base.NO_CHECK,
1216 constants.HV_REBOOT_BEHAVIOR:
1217 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1218 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1219 constants.HV_CPU_CAP: hv_base.NO_CHECK,
1220 constants.HV_CPU_WEIGHT:
1221 (False, lambda x: 0 < x < 65535, "invalid weight", None, None),
1222 constants.HV_VIF_TYPE:
1223 hv_base.ParamInSet(False, constants.HT_HVM_VALID_VIF_TYPES),
1224 constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1225 constants.HV_VIRIDIAN: hv_base.NO_CHECK,
1226 constants.HV_XEN_CMD:
1227 hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1228 constants.HV_XEN_CPUID: hv_base.NO_CHECK,
1229 constants.HV_SOUNDHW: hv_base.NO_CHECK,
1230 }
1231
1232 - def _GetConfig(self, instance, startup_memory, block_devices):
1233 """Create a Xen 3.1 HVM config file.
1234
1235 """
1236 hvp = instance.hvparams
1237
1238 config = StringIO()
1239
1240
1241 kpath = hvp[constants.HV_KERNEL_PATH]
1242 config.write("kernel = '%s'\n" % kpath)
1243
1244 config.write("builder = 'hvm'\n")
1245 config.write("memory = %d\n" % startup_memory)
1246 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1247 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1248 cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1249 if cpu_pinning:
1250 config.write("%s\n" % cpu_pinning)
1251 cpu_cap = hvp[constants.HV_CPU_CAP]
1252 if cpu_cap:
1253 config.write("cpu_cap=%d\n" % cpu_cap)
1254 cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1255 if cpu_weight:
1256 config.write("cpu_weight=%d\n" % cpu_weight)
1257
1258 config.write("name = '%s'\n" % instance.name)
1259 if hvp[constants.HV_PAE]:
1260 config.write("pae = 1\n")
1261 else:
1262 config.write("pae = 0\n")
1263 if hvp[constants.HV_ACPI]:
1264 config.write("acpi = 1\n")
1265 else:
1266 config.write("acpi = 0\n")
1267 if hvp[constants.HV_VIRIDIAN]:
1268 config.write("viridian = 1\n")
1269 else:
1270 config.write("viridian = 0\n")
1271
1272 config.write("apic = 1\n")
1273 config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
1274 config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
1275 config.write("sdl = 0\n")
1276 config.write("usb = 1\n")
1277 config.write("usbdevice = 'tablet'\n")
1278 config.write("vnc = 1\n")
1279 if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
1280 config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
1281 else:
1282 config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
1283
1284 if instance.network_port > constants.VNC_BASE_PORT:
1285 display = instance.network_port - constants.VNC_BASE_PORT
1286 config.write("vncdisplay = %s\n" % display)
1287 config.write("vncunused = 0\n")
1288 else:
1289 config.write("# vncdisplay = 1\n")
1290 config.write("vncunused = 1\n")
1291
1292 vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
1293 try:
1294 password = utils.ReadFile(vnc_pwd_file)
1295 except EnvironmentError, err:
1296 raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
1297 (vnc_pwd_file, err))
1298
1299 config.write("vncpasswd = '%s'\n" % password.rstrip())
1300
1301 config.write("serial = 'pty'\n")
1302 if hvp[constants.HV_USE_LOCALTIME]:
1303 config.write("localtime = 1\n")
1304
1305 vif_data = []
1306
1307
1308
1309 nic_type = hvp[constants.HV_NIC_TYPE]
1310
1311 if nic_type is None:
1312 vif_type_str = ""
1313 if hvp[constants.HV_VIF_TYPE]:
1314 vif_type_str = ", type=%s" % hvp[constants.HV_VIF_TYPE]
1315
1316 nic_type_str = vif_type_str
1317 elif nic_type == constants.HT_NIC_PARAVIRTUAL:
1318 nic_type_str = ", type=paravirtualized"
1319 else:
1320
1321 nic_type_str = ", model=%s, type=%s" % \
1322 (nic_type, constants.HT_HVM_VIF_IOEMU)
1323 for idx, nic in enumerate(instance.nics):
1324 nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
1325 ip = getattr(nic, "ip", None)
1326 if ip is not None:
1327 nic_str += ", ip=%s" % ip
1328 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1329 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1330 if hvp[constants.HV_VIF_SCRIPT]:
1331 nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
1332 vif_data.append("'%s'" % nic_str)
1333 self._WriteNICInfoFile(instance, idx, nic)
1334
1335 config.write("vif = [%s]\n" % ",".join(vif_data))
1336
1337 disk_data = \
1338 _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1339
1340 iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
1341 if iso_path:
1342 iso = "'file:%s,hdc:cdrom,r'" % iso_path
1343 disk_data.append(iso)
1344
1345 config.write("disk = [%s]\n" % (",".join(disk_data)))
1346
1347 pci_pass_arr = []
1348 pci_pass = hvp[constants.HV_PASSTHROUGH]
1349 if pci_pass:
1350 pci_pass_arr = pci_pass.split(";")
1351 config.write("pci = %s\n" % pci_pass_arr)
1352 config.write("on_poweroff = 'destroy'\n")
1353 if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
1354 config.write("on_reboot = 'restart'\n")
1355 else:
1356 config.write("on_reboot = 'destroy'\n")
1357 config.write("on_crash = 'restart'\n")
1358
1359 cpuid = hvp[constants.HV_XEN_CPUID]
1360 if cpuid:
1361 config.write("cpuid = %s\n" % _QuoteCpuidField(cpuid))
1362
1363 if hvp[constants.HV_SOUNDHW]:
1364 config.write("soundhw = '%s'\n" % hvp[constants.HV_SOUNDHW])
1365
1366 return config.getvalue()
1367