1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """Xen hypervisors
23
24 """
25
26 import logging
27 import errno
28 import string
29 import shutil
30 from cStringIO import StringIO
31
32 from ganeti import constants
33 from ganeti import errors
34 from ganeti import utils
35 from ganeti.hypervisor import hv_base
36 from ganeti import netutils
37 from ganeti import objects
38 from ganeti import pathutils
39
40
41 XEND_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xend-config.sxp")
42 XL_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xen/xl.conf")
43 VIF_BRIDGE_SCRIPT = utils.PathJoin(pathutils.XEN_CONFIG_DIR,
44 "scripts/vif-bridge")
45 _DOM0_NAME = "Domain-0"
46 _DISK_LETTERS = string.ascii_lowercase
47
48 _FILE_DRIVER_MAP = {
49 constants.FD_LOOP: "file",
50 constants.FD_BLKTAP: "tap:aio",
51 constants.FD_BLKTAP2: "tap2:tapdisk:aio",
52 }
56 """Create a CPU config string for Xen's config file.
57
58 """
59
60 cpu_list = utils.ParseMultiCpuMask(cpu_mask)
61
62 if len(cpu_list) == 1:
63 all_cpu_mapping = cpu_list[0]
64 if all_cpu_mapping == constants.CPU_PINNING_OFF:
65
66
67 return None
68 else:
69
70
71 return "cpu = \"%s\"" % ",".join(map(str, all_cpu_mapping))
72 else:
73
74 def _GetCPUMap(vcpu):
75 if vcpu[0] == constants.CPU_PINNING_ALL_VAL:
76 cpu_map = constants.CPU_PINNING_ALL_XEN
77 else:
78 cpu_map = ",".join(map(str, vcpu))
79 return "\"%s\"" % cpu_map
80
81
82
83
84 return "cpus = [ %s ]" % ", ".join(map(_GetCPUMap, cpu_list))
85
88 """Helper function for L{_GetInstanceList} to retrieve the list of instances
89 from xen.
90
91 @type fn: callable
92 @param fn: Function to query xen for the list of instances
93 @type instance_list_errors: list
94 @param instance_list_errors: Error list
95 @rtype: list
96
97 """
98 result = fn()
99 if result.failed:
100 logging.error("Retrieving the instance list from xen failed (%s): %s",
101 result.fail_reason, result.output)
102 instance_list_errors.append(result)
103 raise utils.RetryAgain()
104
105
106 return result.stdout.splitlines()
107
110 """Parses the output of listing instances by xen.
111
112 @type lines: list
113 @param lines: Result of retrieving the instance list from xen
114 @type include_node: boolean
115 @param include_node: If True, return information for Dom0
116 @return: list of tuple containing (name, id, memory, vcpus, state, time
117 spent)
118
119 """
120 result = []
121
122
123 for line in lines[1:]:
124
125
126
127 data = line.split()
128 if len(data) != 6:
129 raise errors.HypervisorError("Can't parse instance list,"
130 " line: %s" % line)
131 try:
132 data[1] = int(data[1])
133 data[2] = int(data[2])
134 data[3] = int(data[3])
135 data[5] = float(data[5])
136 except (TypeError, ValueError), err:
137 raise errors.HypervisorError("Can't parse instance list,"
138 " line: %s, error: %s" % (line, err))
139
140
141 if include_node or data[0] != _DOM0_NAME:
142 result.append(data)
143
144 return result
145
148 """Return the list of running instances.
149
150 See L{_RunInstanceList} and L{_ParseInstanceList} for parameter details.
151
152 """
153 instance_list_errors = []
154 try:
155 lines = utils.Retry(_RunInstanceList, (0.3, 1.5, 1.0), _timeout,
156 args=(fn, instance_list_errors))
157 except utils.RetryTimeout:
158 if instance_list_errors:
159 instance_list_result = instance_list_errors.pop()
160
161 errmsg = ("listing instances failed, timeout exceeded (%s): %s" %
162 (instance_list_result.fail_reason, instance_list_result.output))
163 else:
164 errmsg = "listing instances failed"
165
166 raise errors.HypervisorError(errmsg)
167
168 return _ParseInstanceList(lines, include_node)
169
172 return instance_info == "r-----" \
173 or instance_info == "-b----"
174
177 return instance_info == "---s--"
178
181 """Return information about the node.
182
183 @return: a dict with the following keys (memory values in MiB):
184 - memory_total: the total memory size on the node
185 - memory_free: the available memory on the node for instances
186 - nr_cpus: total number of CPUs
187 - nr_nodes: in a NUMA system, the number of domains
188 - nr_sockets: the number of physical CPU sockets in the node
189 - hv_version: the hypervisor version in the form (major, minor)
190
191 """
192 result = {}
193 cores_per_socket = threads_per_core = nr_cpus = None
194 xen_major, xen_minor = None, None
195 memory_total = None
196 memory_free = None
197
198 for line in info.splitlines():
199 fields = line.split(":", 1)
200
201 if len(fields) < 2:
202 continue
203
204 (key, val) = map(lambda s: s.strip(), fields)
205
206
207 if key in ("memory", "total_memory"):
208 memory_total = int(val)
209 elif key == "free_memory":
210 memory_free = int(val)
211 elif key == "nr_cpus":
212 nr_cpus = result["cpu_total"] = int(val)
213 elif key == "nr_nodes":
214 result["cpu_nodes"] = int(val)
215 elif key == "cores_per_socket":
216 cores_per_socket = int(val)
217 elif key == "threads_per_core":
218 threads_per_core = int(val)
219 elif key == "xen_major":
220 xen_major = int(val)
221 elif key == "xen_minor":
222 xen_minor = int(val)
223
224 if None not in [cores_per_socket, threads_per_core, nr_cpus]:
225 result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
226
227 if memory_free is not None:
228 result["memory_free"] = memory_free
229
230 if memory_total is not None:
231 result["memory_total"] = memory_total
232
233 if not (xen_major is None or xen_minor is None):
234 result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
235
236 return result
237
240 """Updates node information from L{_ParseNodeInfo} with instance info.
241
242 @type info: dict
243 @param info: Result from L{_ParseNodeInfo}
244 @type instance_list: list of tuples
245 @param instance_list: list of instance information; one tuple per instance
246 @rtype: dict
247
248 """
249 total_instmem = 0
250
251 for (name, _, mem, vcpus, _, _) in instance_list:
252 if name == _DOM0_NAME:
253 info["memory_dom0"] = mem
254 info["cpu_dom0"] = vcpus
255
256
257 total_instmem += mem
258
259 memory_free = info.get("memory_free")
260 memory_total = info.get("memory_total")
261
262
263 if None not in [memory_total, memory_free, total_instmem]:
264 info["memory_hv"] = memory_total - memory_free - total_instmem
265
266 return info
267
270 """Combines L{_MergeInstanceInfo} and L{_ParseNodeInfo}.
271
272 @type instance_list: list of tuples
273 @param instance_list: list of instance information; one tuple per instance
274
275 """
276 return _MergeInstanceInfo(_ParseNodeInfo(info), instance_list)
277
281 """Get disk directives for Xen config file.
282
283 This method builds the xen config disk directive according to the
284 given disk_template and block_devices.
285
286 @param block_devices: list of tuples (cfdev, rldev):
287 - cfdev: dict containing ganeti config disk part
288 - rldev: ganeti.block.bdev.BlockDev object
289 @param blockdev_prefix: a string containing blockdevice prefix,
290 e.g. "sd" for /dev/sda
291
292 @return: string containing disk directive for xen instance config file
293
294 """
295 if len(block_devices) > len(_letters):
296 raise errors.HypervisorError("Too many disks")
297
298 disk_data = []
299
300 for sd_suffix, (cfdev, dev_path) in zip(_letters, block_devices):
301 sd_name = blockdev_prefix + sd_suffix
302
303 if cfdev.mode == constants.DISK_RDWR:
304 mode = "w"
305 else:
306 mode = "r"
307
308 if cfdev.dev_type in [constants.DT_FILE, constants.DT_SHARED_FILE]:
309 driver = _FILE_DRIVER_MAP[cfdev.physical_id[0]]
310 else:
311 driver = "phy"
312
313 disk_data.append("'%s:%s,%s,%s'" % (driver, dev_path, sd_name, mode))
314
315 return disk_data
316
319 """Xen generic hypervisor interface
320
321 This is the Xen base class used for both Xen PVM and HVM. It contains
322 all the functionality that is identical for both.
323
324 """
325 CAN_MIGRATE = True
326 REBOOT_RETRY_COUNT = 60
327 REBOOT_RETRY_INTERVAL = 10
328 _ROOT_DIR = pathutils.RUN_DIR + "/xen-hypervisor"
329 _NICS_DIR = _ROOT_DIR + "/nic"
330 _DIRS = [_ROOT_DIR, _NICS_DIR]
331
332 ANCILLARY_FILES = [
333 XEND_CONFIG_FILE,
334 XL_CONFIG_FILE,
335 VIF_BRIDGE_SCRIPT,
336 ]
337 ANCILLARY_FILES_OPT = [
338 XL_CONFIG_FILE,
339 ]
340
341 - def __init__(self, _cfgdir=None, _run_cmd_fn=None, _cmd=None):
355
356 @staticmethod
358 """Returns the Xen command extracted from the given hvparams.
359
360 @type hvparams: dict of strings
361 @param hvparams: hypervisor parameters
362
363 """
364 if hvparams is None or constants.HV_XEN_CMD not in hvparams:
365 raise errors.HypervisorError("Cannot determine xen command.")
366 else:
367 return hvparams[constants.HV_XEN_CMD]
368
385
386 - def _RunXen(self, args, hvparams, timeout=None):
387 """Wrapper around L{utils.process.RunCmd} to run Xen command.
388
389 @type hvparams: dict of strings
390 @param hvparams: dictionary of hypervisor params
391 @type timeout: int or None
392 @param timeout: if a timeout (in seconds) is specified, the command will be
393 terminated after that number of seconds.
394 @see: L{utils.process.RunCmd}
395
396 """
397 cmd = []
398
399 if timeout is not None:
400 cmd.extend(["timeout", str(timeout)])
401
402 cmd.extend([self._GetCommand(hvparams)])
403 cmd.extend(args)
404
405 return self._run_cmd_fn(cmd)
406
408 """Get the config file name for an instance.
409
410 @param instance_name: instance name
411 @type instance_name: str
412 @return: fully qualified path to instance config file
413 @rtype: str
414
415 """
416 return utils.PathJoin(self._cfgdir, instance_name)
417
418 @classmethod
420 """Write the Xen config file for the instance.
421
422 This version of the function just writes the config file from static data.
423
424 """
425 dirs = [(dname, constants.RUN_DIRS_MODE)
426 for dname in cls._DIRS + [cls._InstanceNICDir(instance_name)]]
427 utils.EnsureDirs(dirs)
428
429 cfg_file = cls._InstanceNICFile(instance_name, idx)
430 data = StringIO()
431
432 if nic.netinfo:
433 netinfo = objects.Network.FromDict(nic.netinfo)
434 data.write("NETWORK_NAME=%s\n" % netinfo.name)
435 if netinfo.network:
436 data.write("NETWORK_SUBNET=%s\n" % netinfo.network)
437 if netinfo.gateway:
438 data.write("NETWORK_GATEWAY=%s\n" % netinfo.gateway)
439 if netinfo.network6:
440 data.write("NETWORK_SUBNET6=%s\n" % netinfo.network6)
441 if netinfo.gateway6:
442 data.write("NETWORK_GATEWAY6=%s\n" % netinfo.gateway6)
443 if netinfo.mac_prefix:
444 data.write("NETWORK_MAC_PREFIX=%s\n" % netinfo.mac_prefix)
445 if netinfo.tags:
446 data.write("NETWORK_TAGS=%s\n" % r"\ ".join(netinfo.tags))
447
448 data.write("MAC=%s\n" % nic.mac)
449 data.write("IP=%s\n" % nic.ip)
450 data.write("MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
451 data.write("LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
452
453 try:
454 utils.WriteFile(cfg_file, data=data.getvalue())
455 except EnvironmentError, err:
456 raise errors.HypervisorError("Cannot write Xen instance configuration"
457 " file %s: %s" % (cfg_file, err))
458
459 @classmethod
461 """Returns the directory holding the tap device files for a given instance.
462
463 """
464 return utils.PathJoin(cls._NICS_DIR, instance_name)
465
466 @classmethod
468 """Returns the name of the file containing the tap device for a given NIC
469
470 """
471 return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
472
473 @classmethod
474 - def _GetConfig(cls, instance, startup_memory, block_devices):
475 """Build Xen configuration for an instance.
476
477 """
478 raise NotImplementedError
479
481 """Write the Xen config file for the instance.
482
483 This version of the function just writes the config file from static data.
484
485 """
486
487 utils.RemoveFile(utils.PathJoin(self._cfgdir, "auto", instance_name))
488
489 cfg_file = self._ConfigFileName(instance_name)
490 try:
491 utils.WriteFile(cfg_file, data=data)
492 except EnvironmentError, err:
493 raise errors.HypervisorError("Cannot write Xen instance configuration"
494 " file %s: %s" % (cfg_file, err))
495
497 """Returns the contents of the instance config file.
498
499 """
500 filename = self._ConfigFileName(instance_name)
501
502 try:
503 file_content = utils.ReadFile(filename)
504 except EnvironmentError, err:
505 raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
506
507 return file_content
508
510 """Remove the xen configuration file.
511
512 """
513 utils.RemoveFile(self._ConfigFileName(instance_name))
514 try:
515 shutil.rmtree(self._InstanceNICDir(instance_name))
516 except OSError, err:
517 if err.errno != errno.ENOENT:
518 raise
519
521 """Move the Xen config file to the log directory and return its new path.
522
523 """
524 old_filename = self._ConfigFileName(instance_name)
525 base = ("%s-%s" %
526 (instance_name, utils.TimestampForFilename()))
527 new_filename = utils.PathJoin(pathutils.LOG_XEN_DIR, base)
528 utils.RenameFile(old_filename, new_filename)
529 return new_filename
530
532 """Wrapper around module level L{_GetInstanceList}.
533
534 @type hvparams: dict of strings
535 @param hvparams: hypervisor parameters to be used on this node
536
537 """
538 return _GetInstanceList(lambda: self._RunXen(["list"], hvparams),
539 include_node)
540
542 """Get the list of running instances.
543
544 """
545 instance_list = self._GetInstanceList(False, hvparams)
546 names = [info[0] for info in instance_list]
547 return names
548
550 """Get instance properties.
551
552 @type instance_name: string
553 @param instance_name: the instance name
554 @type hvparams: dict of strings
555 @param hvparams: the instance's hypervisor params
556
557 @return: tuple (name, id, memory, vcpus, stat, times)
558
559 """
560 instance_list = self._GetInstanceList(instance_name == _DOM0_NAME, hvparams)
561 result = None
562 for data in instance_list:
563 if data[0] == instance_name:
564 result = data
565 break
566 return result
567
569 """Get properties of all instances.
570
571 @type hvparams: dict of strings
572 @param hvparams: hypervisor parameters
573 @return: list of tuples (name, id, memory, vcpus, stat, times)
574
575 """
576 return self._GetInstanceList(False, hvparams)
577
579 """Gather configuration details and write to disk.
580
581 See L{_GetConfig} for arguments.
582
583 """
584 buf = StringIO()
585 buf.write("# Automatically generated by Ganeti. Do not edit!\n")
586 buf.write("\n")
587 buf.write(self._GetConfig(instance, startup_memory, block_devices))
588 buf.write("\n")
589
590 self._WriteConfigFile(instance.name, buf.getvalue())
591
592 - def StartInstance(self, instance, block_devices, startup_paused):
614
615 - def StopInstance(self, instance, force=False, retry=False, name=None,
616 timeout=None):
617 """Stop an instance.
618
619 A soft shutdown can be interrupted. A hard shutdown tries forever.
620
621 """
622 assert(timeout is None or force is not None)
623
624 if name is None:
625 name = instance.name
626
627 return self._StopInstance(name, force, instance.hvparams, timeout)
628
630 """Shutdown an instance if the instance is running.
631
632 The '-w' flag waits for shutdown to complete which avoids the need
633 to poll in the case where we want to destroy the domain
634 immediately after shutdown.
635
636 @type name: string
637 @param name: name of the instance to stop
638 @type hvparams: dict of string
639 @param hvparams: hypervisor parameters of the instance
640 @type timeout: int or None
641 @param timeout: a timeout after which the shutdown command should be killed,
642 or None for no timeout
643
644 """
645 instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
646
647 if instance_info is None or _IsInstanceShutdown(instance_info[4]):
648 logging.info("Failed to shutdown instance %s, not running", name)
649 return None
650
651 return self._RunXen(["shutdown", "-w", name], hvparams, timeout)
652
654 """Destroy an instance if the instance if the instance exists.
655
656 @type name: string
657 @param name: name of the instance to destroy
658 @type hvparams: dict of string
659 @param hvparams: hypervisor parameters of the instance
660
661 """
662 instance_info = self.GetInstanceInfo(name, hvparams=hvparams)
663
664 if instance_info is None:
665 logging.info("Failed to destroy instance %s, does not exist", name)
666 return None
667
668 return self._RunXen(["destroy", name], hvparams)
669
671 """Stop an instance.
672
673 @type name: string
674 @param name: name of the instance to destroy
675
676 @type force: boolean
677 @param force: whether to do a "hard" stop (destroy)
678
679 @type hvparams: dict of string
680 @param hvparams: hypervisor parameters of the instance
681
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 if force:
688 result = self._DestroyInstance(name, hvparams)
689 else:
690 self._ShutdownInstance(name, hvparams, timeout)
691 result = self._DestroyInstance(name, hvparams)
692
693 if result is not None and result.failed and \
694 self.GetInstanceInfo(name, hvparams=hvparams) is not None:
695 raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
696 (name, result.fail_reason, result.output))
697
698
699 self._RemoveConfigFile(name)
700
726
727 try:
728 utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
729 self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
730 except utils.RetryTimeout:
731 raise errors.HypervisorError("Failed to reboot instance %s: instance"
732 " did not reboot in the expected interval" %
733 (instance.name, ))
734
736 """Balloon an instance memory to a certain value.
737
738 @type instance: L{objects.Instance}
739 @param instance: instance to be accepted
740 @type mem: int
741 @param mem: actual memory size to use for instance runtime
742
743 """
744 result = self._RunXen(["mem-set", instance.name, mem], instance.hvparams)
745 if result.failed:
746 raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" %
747 (instance.name, result.fail_reason,
748 result.output))
749
750
751 cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem]
752 cmd.append(self._ConfigFileName(instance.name))
753
754 result = utils.RunCmd(cmd)
755 if result.failed:
756 raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" %
757 (instance.name, result.fail_reason,
758 result.output))
759
761 """Return information about the node.
762
763 @see: L{_GetNodeInfo} and L{_ParseNodeInfo}
764
765 """
766 result = self._RunXen(["info"], hvparams)
767 if result.failed:
768 logging.error("Can't retrieve xen hypervisor information (%s): %s",
769 result.fail_reason, result.output)
770 return None
771
772 instance_list = self._GetInstanceList(True, hvparams)
773 return _GetNodeInfo(result.stdout, instance_list)
774
775 @classmethod
787
788 - def Verify(self, hvparams=None):
789 """Verify the hypervisor.
790
791 For Xen, this verifies that the xend process is running.
792
793 @type hvparams: dict of strings
794 @param hvparams: hypervisor parameters to be verified against
795
796 @return: Problem description if something is wrong, C{None} otherwise
797
798 """
799 if hvparams is None:
800 return "Could not verify the hypervisor, because no hvparams were" \
801 " provided."
802
803 if constants.HV_XEN_CMD in hvparams:
804 xen_cmd = hvparams[constants.HV_XEN_CMD]
805 try:
806 self._CheckToolstack(xen_cmd)
807 except errors.HypervisorError:
808 return "The configured xen toolstack '%s' is not available on this" \
809 " node." % xen_cmd
810
811 result = self._RunXen(["info"], hvparams)
812 if result.failed:
813 return "Retrieving information from xen failed: %s, %s" % \
814 (result.fail_reason, result.output)
815
816 return None
817
819 """Get instance information to perform a migration.
820
821 @type instance: L{objects.Instance}
822 @param instance: instance to be migrated
823 @rtype: string
824 @return: content of the xen config file
825
826 """
827 return self._ReadConfigFile(instance.name)
828
830 """Prepare to accept an instance.
831
832 @type instance: L{objects.Instance}
833 @param instance: instance to be accepted
834 @type info: string
835 @param info: content of the xen config file on the source node
836 @type target: string
837 @param target: target host (usually ip), on this node
838
839 """
840 pass
841
843 """Finalize an instance migration.
844
845 After a successful migration we write the xen config file.
846 We do nothing on a failure, as we did not change anything at accept time.
847
848 @type instance: L{objects.Instance}
849 @param instance: instance whose migration is being finalized
850 @type info: string
851 @param info: content of the xen config file on the source node
852 @type success: boolean
853 @param success: whether the migration was a success or a failure
854
855 """
856 if success:
857 self._WriteConfigFile(instance.name, info)
858
860 """Migrate an instance to a target node.
861
862 The migration will not be attempted if the instance is not
863 currently running.
864
865 @type instance: L{objects.Instance}
866 @param instance: the instance to be migrated
867 @type target: string
868 @param target: ip address of the target node
869 @type live: boolean
870 @param live: perform a live migration
871
872 """
873 port = instance.hvparams[constants.HV_MIGRATION_PORT]
874
875 return self._MigrateInstance(cluster_name, instance.name, target, port,
876 live, instance.hvparams)
877
880 """Migrate an instance to a target node.
881
882 @see: L{MigrateInstance} for details
883
884 """
885 if hvparams is None:
886 raise errors.HypervisorError("No hvparams provided.")
887
888 if self.GetInstanceInfo(instance_name, hvparams=hvparams) is None:
889 raise errors.HypervisorError("Instance not running, cannot migrate")
890
891 cmd = self._GetCommand(hvparams)
892
893 if (cmd == constants.XEN_CMD_XM and
894 not _ping_fn(target, port, live_port_needed=True)):
895 raise errors.HypervisorError("Remote host %s not listening on port"
896 " %s, cannot migrate" % (target, port))
897
898 args = ["migrate"]
899
900 if cmd == constants.XEN_CMD_XM:
901 args.extend(["-p", "%d" % port])
902 if live:
903 args.append("-l")
904
905 elif cmd == constants.XEN_CMD_XL:
906 args.extend([
907 "-s", constants.XL_SSH_CMD % cluster_name,
908 "-C", self._ConfigFileName(instance_name),
909 ])
910
911 else:
912 raise errors.HypervisorError("Unsupported Xen command: %s" % self._cmd)
913
914 args.extend([instance_name, target])
915
916 result = self._RunXen(args, hvparams)
917 if result.failed:
918 raise errors.HypervisorError("Failed to migrate instance %s: %s" %
919 (instance_name, result.output))
920
922 """Finalize the instance migration on the source node.
923
924 @type instance: L{objects.Instance}
925 @param instance: the instance that was migrated
926 @type success: bool
927 @param success: whether the migration succeeded or not
928 @type live: bool
929 @param live: whether the user requested a live migration or not
930
931 """
932
933 if success:
934
935 try:
936 self._RemoveConfigFile(instance.name)
937 except EnvironmentError:
938 logging.exception("Failure while removing instance config file")
939
941 """Get the migration status
942
943 As MigrateInstance for Xen is still blocking, if this method is called it
944 means that MigrateInstance has completed successfully. So we can safely
945 assume that the migration was successful and notify this fact to the client.
946
947 @type instance: L{objects.Instance}
948 @param instance: the instance that is being migrated
949 @rtype: L{objects.MigrationStatus}
950 @return: the status of the current migration (one of
951 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
952 progress info that can be retrieved from the hypervisor
953
954 """
955 return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
956
958 """Xen-specific powercycle.
959
960 This first does a Linux reboot (which triggers automatically a Xen
961 reboot), and if that fails it tries to do a Xen reboot. The reason
962 we don't try a Xen reboot first is that the xen reboot launches an
963 external command which connects to the Xen hypervisor, and that
964 won't work in case the root filesystem is broken and/or the xend
965 daemon is not working.
966
967 @type hvparams: dict of strings
968 @param hvparams: hypervisor params to be used on this node
969
970 """
971 try:
972 self.LinuxPowercycle()
973 finally:
974 xen_cmd = self._GetCommand(hvparams)
975 utils.RunCmd([xen_cmd, "debug", "R"])
976
991
1000
1018
1021 """Xen PVM hypervisor interface"""
1022
1023 PARAMETERS = {
1024 constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
1025 constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
1026 constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
1027 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1028 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
1029 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
1030 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
1031 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1032 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1033
1034 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1035 constants.HV_REBOOT_BEHAVIOR:
1036 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1037 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1038 constants.HV_CPU_CAP: hv_base.OPT_NONNEGATIVE_INT_CHECK,
1039 constants.HV_CPU_WEIGHT:
1040 (False, lambda x: 0 < x < 65536, "invalid weight", None, None),
1041 constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1042 constants.HV_XEN_CMD:
1043 hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1044 }
1045
1046 - def _GetConfig(self, instance, startup_memory, block_devices):
1047 """Write the Xen config file for the instance.
1048
1049 """
1050 hvp = instance.hvparams
1051 config = StringIO()
1052 config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
1053
1054
1055
1056 if hvp[constants.HV_USE_BOOTLOADER]:
1057
1058 bootloader_path = hvp[constants.HV_BOOTLOADER_PATH]
1059 if bootloader_path:
1060 config.write("bootloader = '%s'\n" % bootloader_path)
1061 else:
1062 raise errors.HypervisorError("Bootloader enabled, but missing"
1063 " bootloader path")
1064
1065 bootloader_args = hvp[constants.HV_BOOTLOADER_ARGS]
1066 if bootloader_args:
1067 config.write("bootargs = '%s'\n" % bootloader_args)
1068 else:
1069
1070 kpath = hvp[constants.HV_KERNEL_PATH]
1071 config.write("kernel = '%s'\n" % kpath)
1072
1073
1074 initrd_path = hvp[constants.HV_INITRD_PATH]
1075 if initrd_path:
1076 config.write("ramdisk = '%s'\n" % initrd_path)
1077
1078
1079 config.write("memory = %d\n" % startup_memory)
1080 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1081 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1082 cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1083 if cpu_pinning:
1084 config.write("%s\n" % cpu_pinning)
1085 cpu_cap = hvp[constants.HV_CPU_CAP]
1086 if cpu_cap:
1087 config.write("cpu_cap=%d\n" % cpu_cap)
1088 cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1089 if cpu_weight:
1090 config.write("cpu_weight=%d\n" % cpu_weight)
1091
1092 config.write("name = '%s'\n" % instance.name)
1093
1094 vif_data = []
1095 for idx, nic in enumerate(instance.nics):
1096 nic_str = "mac=%s" % (nic.mac)
1097 ip = getattr(nic, "ip", None)
1098 if ip is not None:
1099 nic_str += ", ip=%s" % ip
1100 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1101 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1102 if hvp[constants.HV_VIF_SCRIPT]:
1103 nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
1104 vif_data.append("'%s'" % nic_str)
1105 self._WriteNICInfoFile(instance.name, idx, nic)
1106
1107 disk_data = \
1108 _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1109
1110 config.write("vif = [%s]\n" % ",".join(vif_data))
1111 config.write("disk = [%s]\n" % ",".join(disk_data))
1112
1113 if hvp[constants.HV_ROOT_PATH]:
1114 config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
1115 config.write("on_poweroff = 'destroy'\n")
1116 if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
1117 config.write("on_reboot = 'restart'\n")
1118 else:
1119 config.write("on_reboot = 'destroy'\n")
1120 config.write("on_crash = 'restart'\n")
1121 config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
1122
1123 return config.getvalue()
1124
1127 """Xen HVM hypervisor interface"""
1128
1129 ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
1130 pathutils.VNC_PASSWORD_FILE,
1131 ]
1132 ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [
1133 pathutils.VNC_PASSWORD_FILE,
1134 ]
1135
1136 PARAMETERS = {
1137 constants.HV_ACPI: hv_base.NO_CHECK,
1138 constants.HV_BOOT_ORDER: (True, ) +
1139 (lambda x: x and len(x.strip("acdn")) == 0,
1140 "Invalid boot order specified, must be one or more of [acdn]",
1141 None, None),
1142 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
1143 constants.HV_DISK_TYPE:
1144 hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
1145 constants.HV_NIC_TYPE:
1146 hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
1147 constants.HV_PAE: hv_base.NO_CHECK,
1148 constants.HV_VNC_BIND_ADDRESS:
1149 (False, netutils.IP4Address.IsValid,
1150 "VNC bind address is not a valid IP address", None, None),
1151 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
1152 constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
1153 constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
1154 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
1155 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
1156 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
1157
1158 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
1159
1160 constants.HV_PASSTHROUGH: hv_base.NO_CHECK,
1161 constants.HV_REBOOT_BEHAVIOR:
1162 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
1163 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
1164 constants.HV_CPU_CAP: hv_base.NO_CHECK,
1165 constants.HV_CPU_WEIGHT:
1166 (False, lambda x: 0 < x < 65535, "invalid weight", None, None),
1167 constants.HV_VIF_TYPE:
1168 hv_base.ParamInSet(False, constants.HT_HVM_VALID_VIF_TYPES),
1169 constants.HV_VIF_SCRIPT: hv_base.OPT_FILE_CHECK,
1170 constants.HV_VIRIDIAN: hv_base.NO_CHECK,
1171 constants.HV_XEN_CMD:
1172 hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS),
1173 }
1174
1175 - def _GetConfig(self, instance, startup_memory, block_devices):
1176 """Create a Xen 3.1 HVM config file.
1177
1178 """
1179 hvp = instance.hvparams
1180
1181 config = StringIO()
1182
1183
1184 kpath = hvp[constants.HV_KERNEL_PATH]
1185 config.write("kernel = '%s'\n" % kpath)
1186
1187 config.write("builder = 'hvm'\n")
1188 config.write("memory = %d\n" % startup_memory)
1189 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1190 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1191 cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1192 if cpu_pinning:
1193 config.write("%s\n" % cpu_pinning)
1194 cpu_cap = hvp[constants.HV_CPU_CAP]
1195 if cpu_cap:
1196 config.write("cpu_cap=%d\n" % cpu_cap)
1197 cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1198 if cpu_weight:
1199 config.write("cpu_weight=%d\n" % cpu_weight)
1200
1201 config.write("name = '%s'\n" % instance.name)
1202 if hvp[constants.HV_PAE]:
1203 config.write("pae = 1\n")
1204 else:
1205 config.write("pae = 0\n")
1206 if hvp[constants.HV_ACPI]:
1207 config.write("acpi = 1\n")
1208 else:
1209 config.write("acpi = 0\n")
1210 if hvp[constants.HV_VIRIDIAN]:
1211 config.write("viridian = 1\n")
1212 else:
1213 config.write("viridian = 0\n")
1214
1215 config.write("apic = 1\n")
1216 config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
1217 config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
1218 config.write("sdl = 0\n")
1219 config.write("usb = 1\n")
1220 config.write("usbdevice = 'tablet'\n")
1221 config.write("vnc = 1\n")
1222 if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
1223 config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
1224 else:
1225 config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
1226
1227 if instance.network_port > constants.VNC_BASE_PORT:
1228 display = instance.network_port - constants.VNC_BASE_PORT
1229 config.write("vncdisplay = %s\n" % display)
1230 config.write("vncunused = 0\n")
1231 else:
1232 config.write("# vncdisplay = 1\n")
1233 config.write("vncunused = 1\n")
1234
1235 vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
1236 try:
1237 password = utils.ReadFile(vnc_pwd_file)
1238 except EnvironmentError, err:
1239 raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
1240 (vnc_pwd_file, err))
1241
1242 config.write("vncpasswd = '%s'\n" % password.rstrip())
1243
1244 config.write("serial = 'pty'\n")
1245 if hvp[constants.HV_USE_LOCALTIME]:
1246 config.write("localtime = 1\n")
1247
1248 vif_data = []
1249
1250
1251
1252 nic_type = hvp[constants.HV_NIC_TYPE]
1253
1254 if nic_type is None:
1255 vif_type_str = ""
1256 if hvp[constants.HV_VIF_TYPE]:
1257 vif_type_str = ", type=%s" % hvp[constants.HV_VIF_TYPE]
1258
1259 nic_type_str = vif_type_str
1260 elif nic_type == constants.HT_NIC_PARAVIRTUAL:
1261 nic_type_str = ", type=paravirtualized"
1262 else:
1263
1264 nic_type_str = ", model=%s, type=%s" % \
1265 (nic_type, constants.HT_HVM_VIF_IOEMU)
1266 for idx, nic in enumerate(instance.nics):
1267 nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
1268 ip = getattr(nic, "ip", None)
1269 if ip is not None:
1270 nic_str += ", ip=%s" % ip
1271 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1272 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1273 if hvp[constants.HV_VIF_SCRIPT]:
1274 nic_str += ", script=%s" % hvp[constants.HV_VIF_SCRIPT]
1275 vif_data.append("'%s'" % nic_str)
1276 self._WriteNICInfoFile(instance.name, idx, nic)
1277
1278 config.write("vif = [%s]\n" % ",".join(vif_data))
1279
1280 disk_data = \
1281 _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1282
1283 iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
1284 if iso_path:
1285 iso = "'file:%s,hdc:cdrom,r'" % iso_path
1286 disk_data.append(iso)
1287
1288 config.write("disk = [%s]\n" % (",".join(disk_data)))
1289
1290 pci_pass_arr = []
1291 pci_pass = hvp[constants.HV_PASSTHROUGH]
1292 if pci_pass:
1293 pci_pass_arr = pci_pass.split(";")
1294 config.write("pci = %s\n" % pci_pass_arr)
1295 config.write("on_poweroff = 'destroy'\n")
1296 if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
1297 config.write("on_reboot = 'restart'\n")
1298 else:
1299 config.write("on_reboot = 'destroy'\n")
1300 config.write("on_crash = 'restart'\n")
1301
1302 return config.getvalue()
1303