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 """LXC hypervisor
32
33 """
34
35 import errno
36 import os
37 import os.path
38 import logging
39 import sys
40 import re
41
42 from ganeti import constants
43 from ganeti import errors
44 from ganeti import utils
45 from ganeti import objects
46 from ganeti import pathutils
47 from ganeti import serializer
48 from ganeti.hypervisor import hv_base
49 from ganeti.errors import HypervisorError
53 """Create blank file.
54
55 Create a blank file for the path with specified mode.
56 An existing file will be overwritten.
57
58 """
59 try:
60 utils.WriteFile(path, data="", mode=mode)
61 except EnvironmentError, err:
62 raise HypervisorError("Failed to create file %s: %s" % (path, err))
63
66 """LXC version class.
67
68 """
69
70 _VERSION_RE = re.compile(r"^(\d+)\.(\d+)\.(\d+)")
71
72 @classmethod
73 - def _Parse(cls, version_string):
74 """Parse a passed string as an LXC version string.
75
76 @param version_string: a valid LXC version string
77 @type version_string: string
78 @raise ValueError: if version_string is an invalid LXC version string
79 @rtype tuple(int, int, int)
80 @return (major_num, minor_num, micro_num)
81
82 """
83 match = cls._VERSION_RE.match(version_string)
84 if match:
85 return tuple(map(int, match.groups()))
86 else:
87 raise ValueError("'%s' is not a valid LXC version string" %
88 version_string)
89
94
96 return self.original_string
97
100 """LXC-based virtualization.
101
102 """
103 _ROOT_DIR = pathutils.RUN_DIR + "/lxc"
104 _LOG_DIR = pathutils.LOG_DIR + "/lxc"
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124 _INSTANCE_DIR = _ROOT_DIR + "/instance"
125
126 _CGROUP_ROOT_DIR = _ROOT_DIR + "/cgroup"
127 _PROC_CGROUPS_FILE = "/proc/cgroups"
128 _PROC_SELF_CGROUP_FILE = "/proc/self/cgroup"
129
130 _LXC_MIN_VERSION_REQUIRED = LXCVersion("1.0.0")
131 _LXC_COMMANDS_REQUIRED = [
132 "lxc-console",
133 "lxc-ls",
134 "lxc-start",
135 "lxc-stop",
136 "lxc-wait",
137 ]
138
139 _DIR_MODE = 0755
140 _STASH_KEY_ALLOCATED_LOOP_DEV = "allocated_loopdev"
141
142 _MEMORY_PARAMETER = "memory.limit_in_bytes"
143 _MEMORY_SWAP_PARAMETER = "memory.memsw.limit_in_bytes"
144
145 PARAMETERS = {
146 constants.HV_CPU_MASK: hv_base.OPT_CPU_MASK_CHECK,
147 constants.HV_LXC_DEVICES: hv_base.NO_CHECK,
148 constants.HV_LXC_DROP_CAPABILITIES: hv_base.NO_CHECK,
149 constants.HV_LXC_EXTRA_CGROUPS: hv_base.NO_CHECK,
150 constants.HV_LXC_EXTRA_CONFIG: hv_base.NO_CHECK,
151 constants.HV_LXC_NUM_TTYS: hv_base.REQ_NONNEGATIVE_INT_CHECK,
152 constants.HV_LXC_STARTUP_TIMEOUT: hv_base.OPT_NONNEGATIVE_INT_CHECK,
153 }
154
155 _REBOOT_TIMEOUT = 120
156 _REQUIRED_CGROUP_SUBSYSTEMS = [
157 "cpuset",
158 "memory",
159 "devices",
160 "cpuacct",
161 ]
162
166
167 @classmethod
169 """Return the root directory for an instance.
170
171 """
172 return utils.PathJoin(cls._INSTANCE_DIR, instance_name)
173
174 @classmethod
176 """Return the configuration file for an instance.
177
178 """
179 return utils.PathJoin(cls._InstanceDir(instance_name), "config")
180
181 @classmethod
183 """Return the log file for an instance.
184
185 @type instance: L{objects.Instance}
186
187 """
188 filename = "%s.%s.log" % (instance.name, instance.uuid)
189 return utils.PathJoin(cls._LOG_DIR, filename)
190
191 @classmethod
193 """Return the console log file path for an instance.
194
195 """
196 return utils.PathJoin(cls._InstanceDir(instance_name), "console.log")
197
198 @classmethod
200 """Return the stash file path for an instance.
201
202 The stash file is used to keep information needed to clean up after the
203 destruction of the instance.
204
205 """
206 return utils.PathJoin(cls._InstanceDir(instance_name), "stash")
207
217
230
232 """Load information stashed in file which was created by
233 L{_SaveInstanceStash}.
234
235 """
236 stash_file = self._InstanceStashFilePath(instance_name)
237 try:
238 return serializer.Load(utils.ReadFile(stash_file))
239 except (EnvironmentError, ValueError), err:
240 raise HypervisorError("Failed to load instance stash file %s : %s" %
241 (stash_file, err))
242
243 @classmethod
245 """Mount the cgroup subsystem fs under the cgroup root dir.
246
247 @type subsystem: string
248 @param subsystem: cgroup subsystem name to mount
249 @rtype string
250 @return path of subsystem mount point
251
252 """
253 subsys_dir = utils.PathJoin(cls._GetCgroupMountPoint(), subsystem)
254 if not os.path.isdir(subsys_dir):
255 try:
256 os.makedirs(subsys_dir)
257 except EnvironmentError, err:
258 raise HypervisorError("Failed to create directory %s: %s" %
259 (subsys_dir, err))
260
261 mount_cmd = ["mount", "-t", "cgroup", "-o", subsystem, subsystem,
262 subsys_dir]
263 result = utils.RunCmd(mount_cmd)
264 if result.failed:
265 raise HypervisorError("Failed to mount cgroup subsystem '%s': %s" %
266 (subsystem, result.output))
267
268 return subsys_dir
269
287
294
295 @classmethod
297 """Return the directory that should be the base of cgroup fs.
298
299 """
300 return cls._CGROUP_ROOT_DIR
301
302 @classmethod
304 """Prepare cgroup subsystem mount point.
305
306 @type subsystem: string
307 @param subsystem: cgroup subsystem name to mount
308 @rtype string
309 @return path of subsystem mount point
310
311 """
312 for _, mpoint, fstype, options in utils.GetMounts():
313 if fstype == "cgroup" and subsystem in options.split(","):
314 return mpoint
315
316 return cls._MountCgroupSubsystem(subsystem)
317
318 @classmethod
320 """Return the dict of cgroup subsystem hierarchies this process belongs to.
321
322 The dictionary has the cgroup subsystem as a key and its hierarchy as a
323 value.
324 Information is read from /proc/self/cgroup.
325
326 """
327 try:
328 cgroup_list = utils.ReadFile(cls._PROC_SELF_CGROUP_FILE)
329 except EnvironmentError, err:
330 raise HypervisorError("Failed to read %s : %s" %
331 (cls._PROC_SELF_CGROUP_FILE, err))
332
333 cgroups = {}
334 for line in filter(None, cgroup_list.split("\n")):
335 _, subsystems, hierarchy = line.split(":")
336 for subsys in subsystems.split(","):
337 cgroups[subsys] = hierarchy[1:]
338
339 return cgroups
340
341 @classmethod
343 """Return the directory of the cgroup subsystem we use.
344
345 @type subsystem: string
346 @param subsystem: cgroup subsystem name
347 @rtype: string
348 @return: path of the hierarchy directory for the subsystem
349
350 """
351 subsys_dir = cls._GetOrPrepareCgroupSubsysMountPoint(subsystem)
352 base_group = cls._GetCurrentCgroupSubsysGroups().get(subsystem, "")
353
354 return utils.PathJoin(subsys_dir, base_group, "lxc")
355
356 @classmethod
358 """Return the path of the specified cgroup parameter file.
359
360 @type param_name: string
361 @param param_name: cgroup subsystem parameter name
362 @rtype: string
363 @return: path of the cgroup subsystem parameter file
364
365 """
366 subsystem = param_name.split(".", 1)[0]
367 subsys_dir = cls._GetCgroupSubsysDir(subsystem)
368 if instance_name is not None:
369 return utils.PathJoin(subsys_dir, instance_name, param_name)
370 else:
371 return utils.PathJoin(subsys_dir, param_name)
372
373 @classmethod
375 """Return the value of the specified cgroup parameter.
376
377 @type instance_name: string
378 @param instance_name: instance name
379 @type param_name: string
380 @param param_name: cgroup subsystem parameter name
381 @rtype string
382 @return value read from cgroup subsystem fs
383
384 """
385 param_path = cls._GetCgroupParamPath(param_name,
386 instance_name=instance_name)
387 return utils.ReadFile(param_path).rstrip("\n")
388
389 @classmethod
391 """Set the value to the specified instance cgroup parameter.
392
393 @type instance_name: string
394 @param instance_name: instance name
395 @type param_name: string
396 @param param_name: cgroup subsystem parameter name
397 @type param_value: string
398 @param param_value: cgroup subsystem parameter value to be set
399
400 """
401 param_path = cls._GetCgroupParamPath(param_name,
402 instance_name=instance_name)
403
404
405
406
407
408
409 fd = -1
410 try:
411 fd = os.open(param_path, os.O_WRONLY)
412 os.write(fd, param_value)
413 finally:
414 if fd != -1:
415 os.close(fd)
416
417 @classmethod
419 """Return whether a cgroup parameter can be used.
420
421 This is checked by seeing whether there is a file representation of the
422 parameter in the location where the cgroup is mounted.
423
424 @type parameter: string
425 @param parameter: The name of the parameter.
426 @param hvparams: dict
427 @param hvparams: The hypervisor parameters, optional.
428 @rtype: boolean
429
430 """
431 cls._EnsureCgroupMounts(hvparams)
432 param_path = cls._GetCgroupParamPath(parameter)
433
434 return os.path.exists(param_path)
435
436 @classmethod
438 """Return the list of CPU ids for an instance.
439
440 """
441 try:
442 cpumask = cls._GetCgroupInstanceValue(instance_name, "cpuset.cpus")
443 except EnvironmentError, err:
444 raise errors.HypervisorError("Getting CPU list for instance"
445 " %s failed: %s" % (instance_name, err))
446
447 return utils.ParseCpuMask(cpumask)
448
449 @classmethod
451 """Return the CPU usage of an instance.
452
453 """
454 try:
455 cputime_ns = cls._GetCgroupInstanceValue(instance_name, "cpuacct.usage")
456 except EnvironmentError, err:
457 raise HypervisorError("Failed to get the cpu usage of %s: %s" %
458 (instance_name, err))
459
460 return float(cputime_ns) / 10 ** 9
461
462 @classmethod
464 """Return the memory limit for an instance
465
466 """
467 try:
468 mem_limit = cls._GetCgroupInstanceValue(instance_name,
469 "memory.limit_in_bytes")
470 return int(mem_limit)
471 except EnvironmentError, err:
472 raise HypervisorError("Can't get instance memory limit of %s: %s" %
473 (instance_name, err))
474
480
481 @classmethod
483 """Return True if instance is alive.
484
485 """
486 result = utils.RunCmd(["lxc-ls", "--running", re.escape(instance_name)])
487 if result.failed:
488 raise HypervisorError("Failed to get running LXC containers list: %s" %
489 result.output)
490
491 return instance_name in result.stdout.split()
492
493 @classmethod
495 """Return list of alive instances.
496
497 """
498 result = utils.RunCmd(["lxc-ls", "--running"])
499 if result.failed:
500 raise HypervisorError("Failed to get running LXC containers list: %s" %
501 result.output)
502
503 return result.stdout.split()
504
506 """Get instance properties.
507
508 @type instance_name: string
509 @param instance_name: the instance name
510 @type hvparams: dict of strings
511 @param hvparams: hvparams to be used with this instance
512 @rtype: tuple of strings
513 @return: (name, id, memory, vcpus, stat, times)
514
515 """
516 if not self._IsInstanceAlive(instance_name):
517 return None
518
519 return self._GetInstanceInfoInner(instance_name)
520
522 """Get instance properties.
523
524 @type instance_name: string
525 @param instance_name: the instance name
526 @rtype: tuple of strings
527 @return: (name, id, memory, vcpus, stat, times)
528
529 """
530
531 cpu_list = self._GetCgroupCpuList(instance_name)
532 memory = self._GetCgroupMemoryLimit(instance_name) / (1024 ** 2)
533 cputime = self._GetCgroupCpuUsage(instance_name)
534 return (instance_name, 0, memory, len(cpu_list),
535 hv_base.HvInstanceState.RUNNING, cputime)
536
538 """Get properties of all instances.
539
540 @type hvparams: dict of strings
541 @param hvparams: hypervisor parameter
542 @return: [(name, id, memory, vcpus, stat, times),...]
543
544 """
545 data = []
546 running_instances = self._ListAliveInstances()
547 filter_fn = lambda x: os.path.isdir(utils.PathJoin(self._INSTANCE_DIR, x))
548 for dirname in filter(filter_fn, os.listdir(self._INSTANCE_DIR)):
549 if dirname not in running_instances:
550 continue
551 try:
552 info = self._GetInstanceInfoInner(dirname)
553 except errors.HypervisorError:
554 continue
555 if info:
556 data.append(info)
557 return data
558
559 @classmethod
561 """Get and parse the drop capabilities list from the instance hvparams.
562
563 @type hvparams: dict of strings
564 @param hvparams: instance hvparams
565 @rtype list(string)
566 @return list of drop capabilities
567
568 """
569 drop_caps = hvparams[constants.HV_LXC_DROP_CAPABILITIES]
570 return drop_caps.split(",")
571
573 """Create an lxc.conf file for an instance.
574
575 """
576 out = []
577
578 out.append("lxc.utsname = %s" % instance.name)
579
580
581 out.append("lxc.pts = 255")
582
583 num_ttys = instance.hvparams[constants.HV_LXC_NUM_TTYS]
584 if num_ttys:
585 out.append("lxc.tty = %s" % num_ttys)
586
587
588
589
590
591
592 lxc_version = self._GetLXCVersionFromCmd("lxc-start")
593 if lxc_version >= LXCVersion("1.0.6"):
594 console_log_path = self._InstanceConsoleLogFilePath(instance.name)
595 _CreateBlankFile(console_log_path, constants.SECURE_FILE_MODE)
596 out.append("lxc.console.logfile = %s" % console_log_path)
597 else:
598 logging.warn("Console log file is not supported in LXC version %s,"
599 " disabling.", lxc_version)
600
601
602 out.append("lxc.rootfs = %s" % sda_dev_path)
603
604
605 out.append("lxc.mount.entry = proc proc proc nodev,noexec,nosuid 0 0")
606 out.append("lxc.mount.entry = sysfs sys sysfs defaults 0 0")
607
608
609 if instance.hvparams[constants.HV_CPU_MASK]:
610 cpu_list = utils.ParseCpuMask(instance.hvparams[constants.HV_CPU_MASK])
611 cpus_in_mask = len(cpu_list)
612 if cpus_in_mask != instance.beparams["vcpus"]:
613 raise errors.HypervisorError("Number of VCPUs (%d) doesn't match"
614 " the number of CPUs in the"
615 " cpu_mask (%d)" %
616 (instance.beparams["vcpus"],
617 cpus_in_mask))
618 out.append("lxc.cgroup.cpuset.cpus = %s" %
619 instance.hvparams[constants.HV_CPU_MASK])
620
621
622 out.append("lxc.cgroup.memory.limit_in_bytes = %dM" %
623 instance.beparams[constants.BE_MAXMEM])
624 if LXCHypervisor._IsCgroupParameterPresent(self._MEMORY_SWAP_PARAMETER,
625 instance.hvparams):
626 out.append("lxc.cgroup.memory.memsw.limit_in_bytes = %dM" %
627 instance.beparams[constants.BE_MAXMEM])
628
629
630
631 out.append("lxc.cgroup.devices.deny = a")
632 dev_specs = instance.hvparams[constants.HV_LXC_DEVICES]
633 for dev_spec in dev_specs.split(","):
634 out.append("lxc.cgroup.devices.allow = %s" % dev_spec)
635
636
637 for idx, nic in enumerate(instance.nics):
638 out.append("# NIC %d" % idx)
639 mode = nic.nicparams[constants.NIC_MODE]
640 link = nic.nicparams[constants.NIC_LINK]
641 if mode == constants.NIC_MODE_BRIDGED:
642 out.append("lxc.network.type = veth")
643 out.append("lxc.network.link = %s" % link)
644 else:
645 raise errors.HypervisorError("LXC hypervisor only supports"
646 " bridged mode (NIC %d has mode %s)" %
647 (idx, mode))
648 out.append("lxc.network.hwaddr = %s" % nic.mac)
649 out.append("lxc.network.flags = up")
650
651
652 for cap in self._GetInstanceDropCapabilities(instance.hvparams):
653 out.append("lxc.cap.drop = %s" % cap)
654
655
656
657
658
659
660 extra_configs = instance.hvparams[constants.HV_LXC_EXTRA_CONFIG]
661 if extra_configs:
662 out.append("# User defined configs")
663 out.extend(extra_configs.split(","))
664
665 return "\n".join(out) + "\n"
666
667 @classmethod
669 """Return cgroup subsystems list that are enabled in current kernel.
670
671 """
672 try:
673 subsys_table = utils.ReadFile(cls._PROC_CGROUPS_FILE)
674 except EnvironmentError, err:
675 raise HypervisorError("Failed to read cgroup info from %s: %s"
676 % (cls._PROC_CGROUPS_FILE, err))
677 return [x.split(None, 1)[0] for x in subsys_table.split("\n")
678 if x and not x.startswith("#")]
679
680 @classmethod
697
698 @classmethod
700 """Return mountable path for storage_path.
701
702 This function creates a partition mapping for storage_path and returns the
703 first partition device path as a rootfs partition, and stashes the loopback
704 device path.
705 If storage_path is not a multi-partition block device, just return
706 storage_path.
707
708 """
709 try:
710 ret = utils.CreateBdevPartitionMapping(storage_path)
711 except errors.CommandError, err:
712 raise HypervisorError("Failed to create partition mapping for %s"
713 ": %s" % (storage_path, err))
714
715 if ret is None:
716 return storage_path
717 else:
718 loop_dev_path, dm_dev_paths = ret
719 stash[cls._STASH_KEY_ALLOCATED_LOOP_DEV] = loop_dev_path
720 return dm_dev_paths[0]
721
722 @classmethod
724 """Wait for an instance state transition within timeout
725
726 Return True if an instance state changed to the desired state within
727 timeout secs.
728
729 """
730 result = utils.RunCmd(["lxc-wait", "-n", instance_name, "-s", state],
731 timeout=timeout)
732 if result.failed_by_timeout:
733 return False
734 elif result.failed:
735 raise HypervisorError("Failure while waiting for instance state"
736 " transition: %s" % result.output)
737 else:
738 return True
739
740 - def _SpawnLXC(self, instance, log_file, conf_file):
741 """Execute lxc-start and wait until container health is confirmed.
742
743 """
744 lxc_start_cmd = [
745 "lxc-start",
746 "-n", instance.name,
747 "-o", log_file,
748 "-l", "DEBUG",
749 "-f", conf_file,
750 "-d"
751 ]
752
753 result = utils.RunCmd(lxc_start_cmd)
754 if result.failed:
755 raise HypervisorError("Failed to start instance %s : %s" %
756 (instance.name, result.output))
757
758 lxc_startup_timeout = instance.hvparams[constants.HV_LXC_STARTUP_TIMEOUT]
759 if not self._WaitForInstanceState(instance.name,
760 constants.LXC_STATE_RUNNING,
761 lxc_startup_timeout):
762 raise HypervisorError("Instance %s state didn't change to RUNNING within"
763 " %s secs" % (instance.name, lxc_startup_timeout))
764
765
766 if not self._IsInstanceAlive(instance.name):
767 raise HypervisorError("Failed to start instance %s :"
768 " lxc process exited after being daemonized" %
769 instance.name)
770
771 @classmethod
773 """Insures that the disks provided work with the current implementation.
774
775 """
776 if len(block_devices) == 0:
777 raise HypervisorError("LXC cannot have diskless instances.")
778
779 if len(block_devices) > 1:
780 raise HypervisorError("At the moment, LXC cannot support more than one"
781 " disk attached to it. Please create this"
782 " instance anew with fewer disks.")
783
784 - def StartInstance(self, instance, block_devices, startup_paused):
785 """Start an instance.
786
787 For LXC, we try to mount the block device and execute 'lxc-start'.
788 We use volatile containers.
789
790 """
791 LXCHypervisor._VerifyDiskRequirements(block_devices)
792
793 stash = {}
794
795
796
797
798 self._EnsureCgroupMounts(instance.hvparams)
799
800 root_dir = self._InstanceDir(instance.name)
801 try:
802 utils.EnsureDirs([(root_dir, self._DIR_MODE)])
803 except errors.GenericError, err:
804 raise HypervisorError("Creating instance directory failed: %s", str(err))
805
806 log_file = self._InstanceLogFilePath(instance)
807 if not os.path.exists(log_file):
808 _CreateBlankFile(log_file, constants.SECURE_FILE_MODE)
809
810 try:
811 sda_dev_path = block_devices[0][1]
812
813
814 sda_dev_path = self._PrepareInstanceRootFsBdev(sda_dev_path, stash)
815 conf_file = self._InstanceConfFilePath(instance.name)
816 conf = self._CreateConfigFile(instance, sda_dev_path)
817 utils.WriteFile(conf_file, data=conf)
818
819 logging.info("Starting LXC container")
820 try:
821 self._SpawnLXC(instance, log_file, conf_file)
822 except:
823 logging.error("Failed to start instance %s. Please take a look at %s to"
824 " see LXC errors.", instance.name, log_file)
825 raise
826 except:
827
828 exc_info = sys.exc_info()
829 try:
830 self._CleanupInstance(instance.name, stash)
831 except HypervisorError, err:
832 logging.warn("Cleanup for instance %s incomplete: %s",
833 instance.name, err)
834 raise exc_info[0], exc_info[1], exc_info[2]
835
836 self._SaveInstanceStash(instance.name, stash)
837
838 - def StopInstance(self, instance, force=False, retry=False, name=None,
839 timeout=None):
840 """Stop an instance.
841
842 """
843 assert(timeout is None or force is not None)
844
845 if name is None:
846 name = instance.name
847
848 if self._IsInstanceAlive(instance.name):
849 lxc_stop_cmd = ["lxc-stop", "-n", name]
850
851 if force:
852 lxc_stop_cmd.append("--kill")
853 result = utils.RunCmd(lxc_stop_cmd, timeout=timeout)
854 if result.failed:
855 raise HypervisorError("Failed to kill instance %s: %s" %
856 (name, result.output))
857 else:
858
859
860 lxc_stop_cmd.extend(["--nokill", "--timeout", "-1"])
861 result = utils.RunCmd(lxc_stop_cmd, timeout=timeout)
862 if result.failed:
863 logging.error("Failed to stop instance %s: %s", name, result.output)
864
882
884 """Balloon an instance memory to a certain value.
885
886 @type instance: L{objects.Instance}
887 @param instance: instance to be accepted
888 @type mem: int
889 @param mem: actual memory size to use for instance runtime
890
891 """
892 mem_in_bytes = mem * 1024 ** 2
893 current_mem_usage = self._GetCgroupMemoryLimit(instance.name)
894 shrinking = mem_in_bytes <= current_mem_usage
895
896
897
898
899 if LXCHypervisor._IsCgroupParameterPresent(self._MEMORY_SWAP_PARAMETER,
900 instance.hvparams):
901
902
903 cgparams = [self._MEMORY_SWAP_PARAMETER, self._MEMORY_PARAMETER]
904 else:
905 cgparams = [self._MEMORY_PARAMETER]
906
907 if shrinking:
908 cgparams.reverse()
909
910 for i, cgparam in enumerate(cgparams):
911 try:
912 self._SetCgroupInstanceValue(instance.name, cgparam, str(mem_in_bytes))
913 except EnvironmentError, err:
914 if shrinking and err.errno == errno.EBUSY:
915 logging.warn("Unable to reclaim memory or swap usage from instance"
916 " %s", instance.name)
917
918 for restore_param in cgparams[0:i]:
919 try:
920 self._SetCgroupInstanceValue(instance.name, restore_param,
921 str(current_mem_usage))
922 except EnvironmentError, restore_err:
923 logging.warn("Can't restore the cgroup parameter %s of %s: %s",
924 restore_param, instance.name, restore_err)
925
926 raise HypervisorError("Failed to balloon the memory of %s, can't set"
927 " cgroup parameter %s: %s" %
928 (instance.name, cgparam, err))
929
931 """Return information about the node.
932
933 See L{BaseHypervisor.GetLinuxNodeInfo}.
934
935 """
936 return self.GetLinuxNodeInfo()
937
938 @classmethod
951
952 @classmethod
954 """Return the LXC version currently used in the system.
955
956 Version information will be retrieved by command specified by from_cmd.
957
958 @param from_cmd: the lxc command used to retrieve version information
959 @type from_cmd: string
960 @rtype: L{LXCVersion}
961 @return: a version object which represents the version retrieved from the
962 command
963
964 """
965 result = utils.RunCmd([from_cmd, "--version"])
966 if result.failed:
967 raise HypervisorError("Failed to get version info from command %s: %s" %
968 (from_cmd, result.output))
969
970 try:
971 return LXCVersion(result.stdout.strip())
972 except ValueError, err:
973 raise HypervisorError("Can't parse LXC version from %s: %s" %
974 (from_cmd, err))
975
976 @classmethod
978 """Verify the validity of lxc command line tools.
979
980 @rtype: list(str)
981 @return: list of problem descriptions. the blank list will be returned if
982 there is no problem.
983
984 """
985 msgs = []
986 for cmd in cls._LXC_COMMANDS_REQUIRED:
987 try:
988
989
990
991
992 if cmd == "lxc-ls":
993 help_string = utils.RunCmd(["lxc-ls", "--help"]).output
994 if "--running" not in help_string:
995
996 msgs.append("The python version of 'lxc-ls' is required."
997 " Maybe lxc was installed without --enable-python")
998 else:
999 try:
1000 version = cls._GetLXCVersionFromCmd(cmd)
1001 except HypervisorError, err:
1002 msgs.append(str(err))
1003 continue
1004
1005 if version < cls._LXC_MIN_VERSION_REQUIRED:
1006 msgs.append("LXC version >= %s is required but command %s has"
1007 " version %s" %
1008 (cls._LXC_MIN_VERSION_REQUIRED, cmd, version))
1009 except errors.OpExecError:
1010 msgs.append("Required command %s not found" % cmd)
1011
1012 return msgs
1013
1014 - def Verify(self, hvparams=None):
1015 """Verify the hypervisor.
1016
1017 For the LXC manager, it just checks the existence of the base dir.
1018
1019 @type hvparams: dict of strings
1020 @param hvparams: hypervisor parameters to be verified against; not used here
1021
1022 @return: Problem description if something is wrong, C{None} otherwise
1023
1024 """
1025 msgs = []
1026
1027 if not os.path.exists(self._ROOT_DIR):
1028 msgs.append("The required directory '%s' does not exist" %
1029 self._ROOT_DIR)
1030
1031 try:
1032 self._EnsureCgroupMounts(hvparams)
1033 except errors.HypervisorError, err:
1034 msgs.append(str(err))
1035
1036 msgs.extend(self._VerifyLXCCommands())
1037
1038 return self._FormatVerifyResults(msgs)
1039
1040 @classmethod
1042 """LXC powercycle, just a wrapper over Linux powercycle.
1043
1044 @type hvparams: dict of strings
1045 @param hvparams: hypervisor params to be used on this node
1046
1047 """
1048 cls.LinuxPowercycle()
1049
1051 """Migrate an instance.
1052
1053 @type cluster_name: string
1054 @param cluster_name: name of the cluster
1055 @type instance: L{objects.Instance}
1056 @param instance: the instance to be migrated
1057 @type target: string
1058 @param target: hostname (usually ip) of the target node
1059 @type live: boolean
1060 @param live: whether to do a live or non-live migration
1061
1062 """
1063 raise HypervisorError("Migration is not supported by the LXC hypervisor")
1064
1066 """Get the migration status
1067
1068 @type instance: L{objects.Instance}
1069 @param instance: the instance that is being migrated
1070 @rtype: L{objects.MigrationStatus}
1071 @return: the status of the current migration (one of
1072 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
1073 progress info that can be retrieved from the hypervisor
1074
1075 """
1076 raise HypervisorError("Migration is not supported by the LXC hypervisor")
1077