def _IsCpuMaskWellFormed(cpu_mask): """Verifies if the given single CPU mask is valid The single CPU mask should be in the form "a,b,c,d", where each letter is a positive number or range. """ try: cpu_list = utils.ParseCpuMask(cpu_mask) except errors.ParseError, _: return False
def _GetCgroupCpuList(cls, instance_name): """Return the list of CPU ids for an instance. """ try: cpumask = cls._GetCgroupInstanceValue(instance_name, "cpuset.cpus") except EnvironmentError as err: raise errors.HypervisorError("Getting CPU list for instance" " %s failed: %s" % (instance_name, err)) return utils.ParseCpuMask(cpumask)
def testWellFormed(self): self.assertEqual(utils.ParseCpuMask(""), []) self.assertEqual(utils.ParseCpuMask("1"), [1]) self.assertEqual(utils.ParseCpuMask("0-2,4,5-5"), [0, 1, 2, 4, 5])
def _CreateConfigFile(self, instance, sda_dev_path): """Create an lxc.conf file for an instance. """ out = [] # hostname out.append("lxc.utsname = %s" % instance.name) # separate pseudo-TTY instances out.append("lxc.pts = 255") # standard TTYs num_ttys = instance.hvparams[constants.HV_LXC_NUM_TTYS] if num_ttys: # if it is the number greater than 0 out.append("lxc.tty = %s" % num_ttys) # console log file # After the following patch was applied, we lost the console log file output # until the lxc.console.logfile parameter was introduced in 1.0.6. # https:// # lists.linuxcontainers.org/pipermail/lxc-devel/2014-March/008470.html lxc_version = self._GetLXCVersionFromCmd("lxc-start") if lxc_version >= LXCVersion("1.0.6"): console_log_path = self._InstanceConsoleLogFilePath(instance.name) _CreateBlankFile(console_log_path, constants.SECURE_FILE_MODE) out.append("lxc.console.logfile = %s" % console_log_path) else: logging.warn( "Console log file is not supported in LXC version %s," " disabling.", lxc_version) # root FS out.append("lxc.rootfs = %s" % sda_dev_path) # Necessary file systems out.append("lxc.mount.entry = proc proc proc nodev,noexec,nosuid 0 0") out.append("lxc.mount.entry = sysfs sys sysfs defaults 0 0") # CPUs if instance.hvparams[constants.HV_CPU_MASK]: cpu_list = utils.ParseCpuMask( instance.hvparams[constants.HV_CPU_MASK]) cpus_in_mask = len(cpu_list) if cpus_in_mask != instance.beparams["vcpus"]: raise errors.HypervisorError( "Number of VCPUs (%d) doesn't match" " the number of CPUs in the" " cpu_mask (%d)" % (instance.beparams["vcpus"], cpus_in_mask)) out.append("lxc.cgroup.cpuset.cpus = %s" % instance.hvparams[constants.HV_CPU_MASK]) # Memory out.append("lxc.cgroup.memory.limit_in_bytes = %dM" % instance.beparams[constants.BE_MAXMEM]) if LXCHypervisor._IsCgroupParameterPresent(self._MEMORY_SWAP_PARAMETER, instance.hvparams): out.append("lxc.cgroup.memory.memsw.limit_in_bytes = %dM" % instance.beparams[constants.BE_MAXMEM]) # Device control # deny direct device access out.append("lxc.cgroup.devices.deny = a") dev_specs = instance.hvparams[constants.HV_LXC_DEVICES] for dev_spec in dev_specs.split(","): out.append("lxc.cgroup.devices.allow = %s" % dev_spec) # Networking for idx, nic in enumerate(instance.nics): out.append("# NIC %d" % idx) mode = nic.nicparams[constants.NIC_MODE] link = nic.nicparams[constants.NIC_LINK] if mode == constants.NIC_MODE_BRIDGED: out.append("lxc.network.type = veth") out.append("lxc.network.link = %s" % link) else: raise errors.HypervisorError( "LXC hypervisor only supports" " bridged mode (NIC %d has mode %s)" % (idx, mode)) out.append("lxc.network.hwaddr = %s" % nic.mac) out.append("lxc.network.flags = up") # Capabilities for cap in self._GetInstanceDropCapabilities(instance.hvparams): out.append("lxc.cap.drop = %s" % cap) # Extra config # TODO: Currently a configuration parameter that includes comma # in its value can't be added via this parameter. # Make this parameter able to read from a file once the # "parameter from a file" feature added. extra_configs = instance.hvparams[constants.HV_LXC_EXTRA_CONFIG] if extra_configs: out.append("# User defined configs") out.extend(extra_configs.split(",")) return "\n".join(out) + "\n"
class LXCHypervisor(hv_base.BaseHypervisor): """LXC-based virtualization. TODO: - move hardcoded parameters into hypervisor parameters, once we have the container-parameter support Problems/issues: - LXC is very temperamental; in daemon mode, it succeeds or fails in launching the instance silently, without any error indication, and when failing it can leave network interfaces around, and future successful startups will list the instance twice """ _ROOT_DIR = pathutils.RUN_DIR + "/lxc" _DEVS = [ "c 1:3", # /dev/null "c 1:5", # /dev/zero "c 1:7", # /dev/full "c 1:8", # /dev/random "c 1:9", # /dev/urandom "c 1:10", # /dev/aio "c 5:0", # /dev/tty "c 5:1", # /dev/console "c 5:2", # /dev/ptmx "c 136:*", # first block of Unix98 PTY slaves ] _DENIED_CAPABILITIES = [ "mac_override", # Allow MAC configuration or state changes # TODO: remove sys_admin too, for safety #"sys_admin", # Perform a range of system administration operations "sys_boot", # Use reboot(2) and kexec_load(2) "sys_module", # Load and unload kernel modules "sys_time", # Set system clock, set real-time (hardware) clock ] _DIR_MODE = 0755 PARAMETERS = { constants.HV_CPU_MASK: hv_base.OPT_CPU_MASK_CHECK, } def __init__(self): hv_base.BaseHypervisor.__init__(self) utils.EnsureDirs([(self._ROOT_DIR, self._DIR_MODE)]) @staticmethod def _GetMountSubdirs(path): """Return the list of mountpoints under a given path. """ result = [] for _, mountpoint, _, _ in utils.GetMounts(): if (mountpoint.startswith(path) and mountpoint != path): result.append(mountpoint) result.sort(key=lambda x: x.count("/"), reverse=True) return result @classmethod def _InstanceDir(cls, instance_name): """Return the root directory for an instance. """ return utils.PathJoin(cls._ROOT_DIR, instance_name) @classmethod def _InstanceConfFile(cls, instance_name): """Return the configuration file for an instance. """ return utils.PathJoin(cls._ROOT_DIR, instance_name + ".conf") @classmethod def _InstanceLogFile(cls, instance_name): """Return the log file for an instance. """ return utils.PathJoin(cls._ROOT_DIR, instance_name + ".log") @classmethod def _GetCgroupMountPoint(cls): for _, mountpoint, fstype, _ in utils.GetMounts(): if fstype == "cgroup": return mountpoint raise errors.HypervisorError("The cgroup filesystem is not mounted") @classmethod def _GetCgroupCpuList(cls, instance_name): """Return the list of CPU ids for an instance. """ cgroup = cls._GetCgroupMountPoint() try: cpus = utils.ReadFile( utils.PathJoin(cgroup, 'lxc', instance_name, "cpuset.cpus")) except EnvironmentError, err: raise errors.HypervisorError("Getting CPU list for instance" " %s failed: %s" % (instance_name, err)) return utils.ParseCpuMask(cpus)
data="", mode=constants.SECURE_FILE_MODE) except EnvironmentError, err: raise errors.HypervisorError("Creating console log file %s for" " instance %s failed: %s" % (console_log, instance.name, err)) out.append("lxc.console = %s" % console_log) # root FS out.append("lxc.rootfs = %s" % root_dir) # TODO: additional mounts, if we disable CAP_SYS_ADMIN # CPUs if instance.hvparams[constants.HV_CPU_MASK]: cpu_list = utils.ParseCpuMask( instance.hvparams[constants.HV_CPU_MASK]) cpus_in_mask = len(cpu_list) if cpus_in_mask != instance.beparams["vcpus"]: raise errors.HypervisorError( "Number of VCPUs (%d) doesn't match" " the number of CPUs in the" " cpu_mask (%d)" % (instance.beparams["vcpus"], cpus_in_mask)) out.append("lxc.cgroup.cpuset.cpus = %s" % instance.hvparams[constants.HV_CPU_MASK]) # Memory # Conditionally enable, memory resource controller might be disabled cgroup = self._GetCgroupMountPoint() if os.path.exists(utils.PathJoin(cgroup, 'memory.limit_in_bytes')): out.append("lxc.cgroup.memory.limit_in_bytes = %dM" %
return os.path.exists(param_path) @classmethod def _GetCgroupCpuList(cls, instance_name): """Return the list of CPU ids for an instance. """ try: cpumask = cls._GetCgroupInstanceValue(instance_name, "cpuset.cpus") except EnvironmentError, err: raise errors.HypervisorError("Getting CPU list for instance" " %s failed: %s" % (instance_name, err)) return utils.ParseCpuMask(cpumask) @classmethod def _GetCgroupCpuUsage(cls, instance_name): """Return the CPU usage of an instance. """ try: cputime_ns = cls._GetCgroupInstanceValue(instance_name, "cpuacct.usage") except EnvironmentError, err: raise HypervisorError("Failed to get the cpu usage of %s: %s" % (instance_name, err)) return float(cputime_ns) / 10**9 # nano secs to float secs