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
476 """Get the list of running instances.
477
478 """
479 return [iinfo[0] for iinfo in self.GetAllInstancesInfo()]
480
481 @classmethod
483 """Return True if instance is alive.
484
485 """
486 result = utils.RunCmd(["lxc-ls", "--running"])
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
494 """Get instance properties.
495
496 @type instance_name: string
497 @param instance_name: the instance name
498 @type hvparams: dict of strings
499 @param hvparams: hvparams to be used with this instance
500 @rtype: tuple of strings
501 @return: (name, id, memory, vcpus, stat, times)
502
503 """
504 if not self._IsInstanceAlive(instance_name):
505 return None
506
507 cpu_list = self._GetCgroupCpuList(instance_name)
508 memory = self._GetCgroupMemoryLimit(instance_name) / (1024 ** 2)
509 cputime = self._GetCgroupCpuUsage(instance_name)
510 return (instance_name, 0, memory, len(cpu_list),
511 hv_base.HvInstanceState.RUNNING, cputime)
512
514 """Get properties of all instances.
515
516 @type hvparams: dict of strings
517 @param hvparams: hypervisor parameter
518 @return: [(name, id, memory, vcpus, stat, times),...]
519
520 """
521 data = []
522 filter_fn = lambda x: os.path.isdir(utils.PathJoin(self._INSTANCE_DIR, x))
523 for dirname in filter(filter_fn, os.listdir(self._INSTANCE_DIR)):
524 try:
525 info = self.GetInstanceInfo(dirname)
526 except errors.HypervisorError:
527 continue
528 if info:
529 data.append(info)
530 return data
531
532 @classmethod
534 """Get and parse the drop capabilities list from the instance hvparams.
535
536 @type hvparams: dict of strings
537 @param hvparams: instance hvparams
538 @rtype list(string)
539 @return list of drop capabilities
540
541 """
542 drop_caps = hvparams[constants.HV_LXC_DROP_CAPABILITIES]
543 return drop_caps.split(",")
544
546 """Create an lxc.conf file for an instance.
547
548 """
549 out = []
550
551 out.append("lxc.utsname = %s" % instance.name)
552
553
554 out.append("lxc.pts = 255")
555
556 num_ttys = instance.hvparams[constants.HV_LXC_NUM_TTYS]
557 if num_ttys:
558 out.append("lxc.tty = %s" % num_ttys)
559
560
561
562
563
564
565 lxc_version = self._GetLXCVersionFromCmd("lxc-start")
566 if lxc_version >= LXCVersion("1.0.6"):
567 console_log_path = self._InstanceConsoleLogFilePath(instance.name)
568 _CreateBlankFile(console_log_path, constants.SECURE_FILE_MODE)
569 out.append("lxc.console.logfile = %s" % console_log_path)
570 else:
571 logging.warn("Console log file is not supported in LXC version %s,"
572 " disabling.", lxc_version)
573
574
575 out.append("lxc.rootfs = %s" % sda_dev_path)
576
577
578 out.append("lxc.mount.entry = proc proc proc nodev,noexec,nosuid 0 0")
579 out.append("lxc.mount.entry = sysfs sys sysfs defaults 0 0")
580
581
582 if instance.hvparams[constants.HV_CPU_MASK]:
583 cpu_list = utils.ParseCpuMask(instance.hvparams[constants.HV_CPU_MASK])
584 cpus_in_mask = len(cpu_list)
585 if cpus_in_mask != instance.beparams["vcpus"]:
586 raise errors.HypervisorError("Number of VCPUs (%d) doesn't match"
587 " the number of CPUs in the"
588 " cpu_mask (%d)" %
589 (instance.beparams["vcpus"],
590 cpus_in_mask))
591 out.append("lxc.cgroup.cpuset.cpus = %s" %
592 instance.hvparams[constants.HV_CPU_MASK])
593
594
595 out.append("lxc.cgroup.memory.limit_in_bytes = %dM" %
596 instance.beparams[constants.BE_MAXMEM])
597 if LXCHypervisor._IsCgroupParameterPresent(self._MEMORY_SWAP_PARAMETER,
598 instance.hvparams):
599 out.append("lxc.cgroup.memory.memsw.limit_in_bytes = %dM" %
600 instance.beparams[constants.BE_MAXMEM])
601
602
603
604 out.append("lxc.cgroup.devices.deny = a")
605 dev_specs = instance.hvparams[constants.HV_LXC_DEVICES]
606 for dev_spec in dev_specs.split(","):
607 out.append("lxc.cgroup.devices.allow = %s" % dev_spec)
608
609
610 for idx, nic in enumerate(instance.nics):
611 out.append("# NIC %d" % idx)
612 mode = nic.nicparams[constants.NIC_MODE]
613 link = nic.nicparams[constants.NIC_LINK]
614 if mode == constants.NIC_MODE_BRIDGED:
615 out.append("lxc.network.type = veth")
616 out.append("lxc.network.link = %s" % link)
617 else:
618 raise errors.HypervisorError("LXC hypervisor only supports"
619 " bridged mode (NIC %d has mode %s)" %
620 (idx, mode))
621 out.append("lxc.network.hwaddr = %s" % nic.mac)
622 out.append("lxc.network.flags = up")
623
624
625 for cap in self._GetInstanceDropCapabilities(instance.hvparams):
626 out.append("lxc.cap.drop = %s" % cap)
627
628
629
630
631
632
633 extra_configs = instance.hvparams[constants.HV_LXC_EXTRA_CONFIG]
634 if extra_configs:
635 out.append("# User defined configs")
636 out.extend(extra_configs.split(","))
637
638 return "\n".join(out) + "\n"
639
640 @classmethod
642 """Return cgroup subsystems list that are enabled in current kernel.
643
644 """
645 try:
646 subsys_table = utils.ReadFile(cls._PROC_CGROUPS_FILE)
647 except EnvironmentError, err:
648 raise HypervisorError("Failed to read cgroup info from %s: %s"
649 % (cls._PROC_CGROUPS_FILE, err))
650 return [x.split(None, 1)[0] for x in subsys_table.split("\n")
651 if x and not x.startswith("#")]
652
653 @classmethod
670
671 @classmethod
673 """Return mountable path for storage_path.
674
675 This function creates a partition mapping for storage_path and returns the
676 first partition device path as a rootfs partition, and stashes the loopback
677 device path.
678 If storage_path is not a multi-partition block device, just return
679 storage_path.
680
681 """
682 try:
683 ret = utils.CreateBdevPartitionMapping(storage_path)
684 except errors.CommandError, err:
685 raise HypervisorError("Failed to create partition mapping for %s"
686 ": %s" % (storage_path, err))
687
688 if ret is None:
689 return storage_path
690 else:
691 loop_dev_path, dm_dev_paths = ret
692 stash[cls._STASH_KEY_ALLOCATED_LOOP_DEV] = loop_dev_path
693 return dm_dev_paths[0]
694
695 @classmethod
697 """Wait for an instance state transition within timeout
698
699 Return True if an instance state changed to the desired state within
700 timeout secs.
701
702 """
703 result = utils.RunCmd(["lxc-wait", "-n", instance_name, "-s", state],
704 timeout=timeout)
705 if result.failed_by_timeout:
706 return False
707 elif result.failed:
708 raise HypervisorError("Failure while waiting for instance state"
709 " transition: %s" % result.output)
710 else:
711 return True
712
713 - def _SpawnLXC(self, instance, log_file, conf_file):
714 """Execute lxc-start and wait until container health is confirmed.
715
716 """
717 lxc_start_cmd = [
718 "lxc-start",
719 "-n", instance.name,
720 "-o", log_file,
721 "-l", "DEBUG",
722 "-f", conf_file,
723 "-d"
724 ]
725
726 result = utils.RunCmd(lxc_start_cmd)
727 if result.failed:
728 raise HypervisorError("Failed to start instance %s : %s" %
729 (instance.name, result.output))
730
731 lxc_startup_timeout = instance.hvparams[constants.HV_LXC_STARTUP_TIMEOUT]
732 if not self._WaitForInstanceState(instance.name,
733 constants.LXC_STATE_RUNNING,
734 lxc_startup_timeout):
735 raise HypervisorError("Instance %s state didn't change to RUNNING within"
736 " %s secs" % (instance.name, lxc_startup_timeout))
737
738
739 if not self._IsInstanceAlive(instance.name):
740 raise HypervisorError("Failed to start instance %s :"
741 " lxc process exited after being daemonized" %
742 instance.name)
743
744 @classmethod
746 """Insures that the disks provided work with the current implementation.
747
748 """
749 if len(block_devices) == 0:
750 raise HypervisorError("LXC cannot have diskless instances.")
751
752 if len(block_devices) > 1:
753 raise HypervisorError("At the moment, LXC cannot support more than one"
754 " disk attached to it. Please create this"
755 " instance anew with fewer disks.")
756
757 - def StartInstance(self, instance, block_devices, startup_paused):
758 """Start an instance.
759
760 For LXC, we try to mount the block device and execute 'lxc-start'.
761 We use volatile containers.
762
763 """
764 LXCHypervisor._VerifyDiskRequirements(block_devices)
765
766 stash = {}
767
768
769
770
771 self._EnsureCgroupMounts(instance.hvparams)
772
773 root_dir = self._InstanceDir(instance.name)
774 try:
775 utils.EnsureDirs([(root_dir, self._DIR_MODE)])
776 except errors.GenericError, err:
777 raise HypervisorError("Creating instance directory failed: %s", str(err))
778
779 log_file = self._InstanceLogFilePath(instance)
780 if not os.path.exists(log_file):
781 _CreateBlankFile(log_file, constants.SECURE_FILE_MODE)
782
783 try:
784 sda_dev_path = block_devices[0][1]
785
786
787 sda_dev_path = self._PrepareInstanceRootFsBdev(sda_dev_path, stash)
788 conf_file = self._InstanceConfFilePath(instance.name)
789 conf = self._CreateConfigFile(instance, sda_dev_path)
790 utils.WriteFile(conf_file, data=conf)
791
792 logging.info("Starting LXC container")
793 try:
794 self._SpawnLXC(instance, log_file, conf_file)
795 except:
796 logging.error("Failed to start instance %s. Please take a look at %s to"
797 " see LXC errors.", instance.name, log_file)
798 raise
799 except:
800
801 exc_info = sys.exc_info()
802 try:
803 self._CleanupInstance(instance.name, stash)
804 except HypervisorError, err:
805 logging.warn("Cleanup for instance %s incomplete: %s",
806 instance.name, err)
807 raise exc_info[0], exc_info[1], exc_info[2]
808
809 self._SaveInstanceStash(instance.name, stash)
810
811 - def StopInstance(self, instance, force=False, retry=False, name=None,
812 timeout=None):
813 """Stop an instance.
814
815 """
816 assert(timeout is None or force is not None)
817
818 if name is None:
819 name = instance.name
820
821 if self._IsInstanceAlive(instance.name):
822 lxc_stop_cmd = ["lxc-stop", "-n", name]
823
824 if force:
825 lxc_stop_cmd.append("--kill")
826 result = utils.RunCmd(lxc_stop_cmd, timeout=timeout)
827 if result.failed:
828 raise HypervisorError("Failed to kill instance %s: %s" %
829 (name, result.output))
830 else:
831
832
833 lxc_stop_cmd.extend(["--nokill", "--timeout", "-1"])
834 result = utils.RunCmd(lxc_stop_cmd, timeout=timeout)
835 if result.failed:
836 logging.error("Failed to stop instance %s: %s", name, result.output)
837
855
857 """Balloon an instance memory to a certain value.
858
859 @type instance: L{objects.Instance}
860 @param instance: instance to be accepted
861 @type mem: int
862 @param mem: actual memory size to use for instance runtime
863
864 """
865 mem_in_bytes = mem * 1024 ** 2
866 current_mem_usage = self._GetCgroupMemoryLimit(instance.name)
867 shrinking = mem_in_bytes <= current_mem_usage
868
869
870
871
872 if LXCHypervisor._IsCgroupParameterPresent(self._MEMORY_SWAP_PARAMETER,
873 instance.hvparams):
874
875
876 cgparams = [self._MEMORY_SWAP_PARAMETER, self._MEMORY_PARAMETER]
877 else:
878 cgparams = [self._MEMORY_PARAMETER]
879
880 if shrinking:
881 cgparams.reverse()
882
883 for i, cgparam in enumerate(cgparams):
884 try:
885 self._SetCgroupInstanceValue(instance.name, cgparam, str(mem_in_bytes))
886 except EnvironmentError, err:
887 if shrinking and err.errno == errno.EBUSY:
888 logging.warn("Unable to reclaim memory or swap usage from instance"
889 " %s", instance.name)
890
891 for restore_param in cgparams[0:i]:
892 try:
893 self._SetCgroupInstanceValue(instance.name, restore_param,
894 str(current_mem_usage))
895 except EnvironmentError, restore_err:
896 logging.warn("Can't restore the cgroup parameter %s of %s: %s",
897 restore_param, instance.name, restore_err)
898
899 raise HypervisorError("Failed to balloon the memory of %s, can't set"
900 " cgroup parameter %s: %s" %
901 (instance.name, cgparam, err))
902
904 """Return information about the node.
905
906 See L{BaseHypervisor.GetLinuxNodeInfo}.
907
908 """
909 return self.GetLinuxNodeInfo()
910
911 @classmethod
924
925 @classmethod
927 """Return the LXC version currently used in the system.
928
929 Version information will be retrieved by command specified by from_cmd.
930
931 @param from_cmd: the lxc command used to retrieve version information
932 @type from_cmd: string
933 @rtype: L{LXCVersion}
934 @return: a version object which represents the version retrieved from the
935 command
936
937 """
938 result = utils.RunCmd([from_cmd, "--version"])
939 if result.failed:
940 raise HypervisorError("Failed to get version info from command %s: %s" %
941 (from_cmd, result.output))
942
943 try:
944 return LXCVersion(result.stdout.strip())
945 except ValueError, err:
946 raise HypervisorError("Can't parse LXC version from %s: %s" %
947 (from_cmd, err))
948
949 @classmethod
951 """Verify the validity of lxc command line tools.
952
953 @rtype: list(str)
954 @return: list of problem descriptions. the blank list will be returned if
955 there is no problem.
956
957 """
958 msgs = []
959 for cmd in cls._LXC_COMMANDS_REQUIRED:
960 try:
961
962
963
964
965 if cmd == "lxc-ls":
966 help_string = utils.RunCmd(["lxc-ls", "--help"]).output
967 if "--running" not in help_string:
968
969 msgs.append("The python version of 'lxc-ls' is required."
970 " Maybe lxc was installed without --enable-python")
971 else:
972 try:
973 version = cls._GetLXCVersionFromCmd(cmd)
974 except HypervisorError, err:
975 msgs.append(str(err))
976 continue
977
978 if version < cls._LXC_MIN_VERSION_REQUIRED:
979 msgs.append("LXC version >= %s is required but command %s has"
980 " version %s" %
981 (cls._LXC_MIN_VERSION_REQUIRED, cmd, version))
982 except errors.OpExecError:
983 msgs.append("Required command %s not found" % cmd)
984
985 return msgs
986
987 - def Verify(self, hvparams=None):
988 """Verify the hypervisor.
989
990 For the LXC manager, it just checks the existence of the base dir.
991
992 @type hvparams: dict of strings
993 @param hvparams: hypervisor parameters to be verified against; not used here
994
995 @return: Problem description if something is wrong, C{None} otherwise
996
997 """
998 msgs = []
999
1000 if not os.path.exists(self._ROOT_DIR):
1001 msgs.append("The required directory '%s' does not exist" %
1002 self._ROOT_DIR)
1003
1004 try:
1005 self._EnsureCgroupMounts(hvparams)
1006 except errors.HypervisorError, err:
1007 msgs.append(str(err))
1008
1009 msgs.extend(self._VerifyLXCCommands())
1010
1011 return self._FormatVerifyResults(msgs)
1012
1013 @classmethod
1015 """LXC powercycle, just a wrapper over Linux powercycle.
1016
1017 @type hvparams: dict of strings
1018 @param hvparams: hypervisor params to be used on this node
1019
1020 """
1021 cls.LinuxPowercycle()
1022
1024 """Migrate an instance.
1025
1026 @type cluster_name: string
1027 @param cluster_name: name of the cluster
1028 @type instance: L{objects.Instance}
1029 @param instance: the instance to be migrated
1030 @type target: string
1031 @param target: hostname (usually ip) of the target node
1032 @type live: boolean
1033 @param live: whether to do a live or non-live migration
1034
1035 """
1036 raise HypervisorError("Migration is not supported by the LXC hypervisor")
1037
1039 """Get the migration status
1040
1041 @type instance: L{objects.Instance}
1042 @param instance: the instance that is being migrated
1043 @rtype: L{objects.MigrationStatus}
1044 @return: the status of the current migration (one of
1045 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
1046 progress info that can be retrieved from the hypervisor
1047
1048 """
1049 raise HypervisorError("Migration is not supported by the LXC hypervisor")
1050