Commit 640ae837 authored by Tomáš Peterka's avatar Tomáš Peterka

[format] Add CGroup Manager in form of optional "plugin"

-  Move all Computer attribute initialization into Computer constructor
-  Rename 'construct' to 'format' because we are in slap.format
-  Introduce Manager base class
-  Implement CGroupManager to take care of cgroup's cpuset
parent cf7225e5
format Format
====== ======
slapformat is an application to prepare SlapOS ready node (machine). slapformat is an application to prepare SlapOS-ready node to be used inside SlapGrid Cloud.
It "formats" the machine by: It "formats" the machine by:
...@@ -10,9 +10,21 @@ It "formats" the machine by: ...@@ -10,9 +10,21 @@ It "formats" the machine by:
- creating needed tap interfaces - creating needed tap interfaces
- creating TUN interfaces - creating TUN interfaces
- creating needed directories with proper ownership and permissions - creating needed directories with proper ownership and permissions
- (optional-manager) creating cgroup resource tree for slapos
In the end special report is generated and information are posted to It reads configuration from /etc/opt/slapos/slapos.cfg and formats computer
configured SlapOS server. accordingly. The variables are number of partitions, IP addresses, storages
and network interfaces.
Format uploads a into configured SlapOS Master server.
Format dumps allocated resources for the partition into a JSON file per
partition ~/.slapos-resource. This file contains network interfaces,
IP address ranges and port ranges. The resource constraints can be
recursively folded.
Pluggable parts for formatting are available too. They are called Managers
and can be turned on/off via configuration property manager_list.
This program shall be only run by root. This program shall be only run by root.
......
...@@ -64,6 +64,10 @@ from slapos import version ...@@ -64,6 +64,10 @@ from slapos import version
logger = logging.getLogger("slapos.format") logger = logging.getLogger("slapos.format")
# dict[str: ManagerClass] used in configuration and XML dump of computer
# this dictionary is intended to be filled after each definition of a Manager
available_manager_list = {}
def prettify_xml(xml): def prettify_xml(xml):
root = lxml.etree.fromstring(xml) root = lxml.etree.fromstring(xml)
...@@ -235,39 +239,239 @@ def _getDict(obj): ...@@ -235,39 +239,239 @@ def _getDict(obj):
key: _getDict(value) \ key: _getDict(value) \
for key, value in dikt.iteritems() \ for key, value in dikt.iteritems() \
# do not attempt to serialize logger: it is both useless and recursive. # do not attempt to serialize logger: it is both useless and recursive.
if not isinstance(value, logging.Logger) # do not serialize attributes starting with "_", let the classes have some privacy
if not key.startswith("_")
} }
class Manager(object):
short_name = None
def format(self):
raise NotImplementedError("Implement function to format underlaying OS")
def update(self):
raise NotImplementedError("Implement function to format underlaying OS")
class CGroupManager(Manager):
"""Manage cgroups in terms on initializing and runtime operations.
This class takes advantage of slapformat being run periodically thus
it can act as a "daemon" performing runtime tasks.
"""
short_name = 'cgroup'
cpu_exclusive_file = ".slapos-cpu-exclusive"
cpuset_path = "/sys/fs/cgroup/cpuset/"
task_write_mode = "at"
def __init__(self, computer):
"""Extract necessary information from the ``computer``.
:param computer: slapos.format.Computer, extract necessary attributes
"""
self.instance_root = computer.instance_root
self.software_gid = computer.software_gid
logger.info("Allowing " + self.__class__.__name__)
def __str__(self):
"""Manager representation when dumped to string."""
return self.short_name
def is_allowed(self):
return os.path.exists("/sys/fs/cgroup/cpuset/cpuset.cpus")
def format(self):
"""Build CGROUP tree to fit SlapOS needs.
- Create hierarchy of CPU sets so that every partition can have exclusive
hold of one of the CPUs.
"""
self.prepare_cpuset()
self.prepare_cpu_space()
def update(self):
"""Control runtime state of the computer."""
cpu0_path = os.path.join(self.cpuset_path, "cpu0")
if os.path.exists(cpu0_path):
# proceed only whe CPUSETs were formatted by this manager
self.prepare_cpu_space()
self.ensure_exlusive_cpu()
else:
logger.warning("Computer was not formatted by {} because {} doesn't exist!".format(
self.__class__.__name__, cpu0_path))
def prepare_cpuset(self):
"""Create cgroup folder per-CPU with exclusive access to the CPU.
Those folders are "/sys/fs/cgroup/cpuset/cpu<N>".
"""
for cpu in self._cpu_list():
cpu_path = self._prepare_folder(
os.path.join(self.cpuset_path, "cpu" + str(cpu)))
with open(cpu_path + "/cpuset.cpus", "wt") as fx:
fx.write(str(cpu)) # this cgroup manages only this cpu
with open(cpu_path + "/cpuset.cpu_exclusive", "wt") as fx:
fx.write("1") # manages it exclusively
with open(cpu_path + "/cpuset.mems", "wt") as fx:
fx.write("0") # it doesn't work without that
os.chown(cpu_path + "/tasks", -1, self.software_gid)
os.chmod(cpu_path + "/tasks", 0o664)
def ensure_exlusive_cpu(self):
"""Move processes among exclusive CPUSets based on software release demands.
We expect PIDs which require own CPU to be found in ~instance/.slapos-cpu-exclusive
"""
cpu_list = self._cpu_list()
generic_cpu = cpu_list[0]
exclusive_cpu_list = cpu_list[1:]
# gather all running PIDs for filtering out stale PIDs
running_pid_set = set()
with open(os.path.join(self.cpuset_path, "tasks"), "rt") as fi:
running_pid_set.update(map(int, fi.read().split()))
with open(os.path.join(self.cpuset_path, "cpu" + str(generic_cpu), "tasks"), "rt") as fi:
running_pid_set.update(map(int, fi.read().split()))
# gather already exclusively running PIDs
exclusive_pid_set = set()
for exclusive_cpu in exclusive_cpu_list:
with open(os.path.join(self.cpuset_path, "cpu" + str(exclusive_cpu), "tasks"), "rt") as fi:
exclusive_pid_set.update(map(int, fi.read().split()))
for request_file in glob.iglob(os.path.join(self.instance_root, '*', CGroupManager.cpu_exclusive_file)):
with open(request_file, "rt") as fi:
# take such PIDs which are either really running or are not already exclusive
request_pid_list = [int(pid) for pid in fi.read().split()
if int(pid) in running_pid_set or int(pid) not in exclusive_pid_set]
with open(request_file, "wt") as fo:
fo.write("") # empty file (we will write back only PIDs which weren't moved)
for request_pid in request_pid_list:
assigned_cpu = self._move_to_exclusive_cpu(request_pid)
if assigned_cpu < 0:
# if no exclusive CPU was assigned - write the PID back and try other time
with open(request_file, "at") as fo:
fo.write(str(request_pid) + "\n")
def prepare_cpu_space(self):
"""Move all PIDs from the pool of all CPUs into the first exclusive CPU."""
with open(os.path.join(self.cpuset_path, "tasks"), "rt") as fi:
running_list = sorted(list(map(int, fi.read().split())), reverse=True)
first_cpu = self._cpu_list()[0]
success_set = set()
refused_set = set()
for pid in running_list:
try:
self._move_task(pid, first_cpu)
success_set.add(pid)
time.sleep(0.01)
except IOError as e:
refused_set.add(pid)
logger.debug("Refused to move {:d} PIDs: {!s}\n"
"Suceeded in moving {:d} PIDs {!s}\n".format(
len(refused_set), refused_set, len(success_set), success_set)
)
def _cpu_list(self):
"""Extract IDs of available CPUs and return them as a list.
The first one will be always used for all non-exclusive processes.
:return: list[int]
"""
cpu_list = [] # types: list[int]
with open(self.cpuset_path + "cpuset.cpus", "rt") as cpu_def:
for cpu_def_split in cpu_def.read().strip().split(","):
# IDs can be in form "0-4" or "0,1,2,3,4"
if "-" in cpu_def_split:
a, b = map(int, cpu_def_split.split("-"))
cpu_list.extend(range(a, b + 1)) # because cgroup's range is inclusive
continue
cpu_list.append(int(cpu_def_split))
return cpu_list
def _move_to_exclusive_cpu(self, pid):
"""Try all exclusive CPUs and place the ``pid`` to the first available one.
:return: int, cpu_id of used CPU, -1 if placement was not possible
"""
exclusive_cpu_list = self._cpu_list()[1:]
for exclusive_cpu in exclusive_cpu_list:
# gather tasks assigned to current exclusive CPU
task_path = os.path.join(self.cpuset_path, "cpu" + str(exclusive_cpu), "tasks")
with open(task_path, "rt") as fi:
task_list = fi.read().split()
if len(task_list) > 0:
continue # skip occupied CPUs
return self._move_task(pid, exclusive_cpu)[1]
return -1
def _move_task(self, pid, cpu_id):
"""Move ``pid`` to ``cpu_id``."""
with open(os.path.join(self.cpuset_path, "cpu" + str(cpu_id), "tasks"), self.task_write_mode) as fo:
fo.write(str(pid) + "\n")
return pid, cpu_id
def _prepare_folder(self, folder):
"""If-Create folder and set group write permission."""
if not os.path.exists(folder):
os.mkdir(folder)
os.chown(folder, -1, self.software_gid)
# make your life and testing easier and create mandatory files if they don't exist
mandatory_file_list = ("tasks", "cpuset.cpus")
for mandatory_file in mandatory_file_list:
file_path = os.path.join(folder, mandatory_file)
if not os.path.exists(file_path):
with open(file_path, "wb"):
pass # touche
return folder
# mark manager available
available_manager_list[CGroupManager.short_name] = CGroupManager
class Computer(object): class Computer(object):
"Object representing the computer" """Object representing the computer"""
instance_root = None
software_root = None
instance_storage_home = None
def __init__(self, reference, interface=None, addr=None, netmask=None, def __init__(self, reference, interface=None, addr=None, netmask=None,
ipv6_interface=None, software_user='slapsoft', ipv6_interface=None, software_user='slapsoft',
tap_gateway_interface=None): tap_gateway_interface=None,
instance_root=None, software_root=None, instance_storage_home=None,
partition_list=None, manager_list=None):
""" """
Attributes: Attributes:
reference: String, the reference of the computer. reference: str, the reference of the computer.
interface: String, the name of the computer's used interface. interface: str, the name of the computer's used interface.
""" """
self.reference = str(reference) self.reference = str(reference)
self.interface = interface self.interface = interface
self.partition_list = [] self.partition_list = partition_list or []
self.address = addr self.address = addr
self.netmask = netmask self.netmask = netmask
self.ipv6_interface = ipv6_interface self.ipv6_interface = ipv6_interface
self.software_user = software_user self.software_user = software_user
self.tap_gateway_interface = tap_gateway_interface self.tap_gateway_interface = tap_gateway_interface
# The follow properties are updated on update() method # Used to be static attributes of the class object - didn't make sense (Marco again)
assert instance_root is not None and software_root is not None, \
"Computer's instance_root and software_root must not be empty!"
self.software_root = software_root
self.instance_root = instance_root
self.instance_storage_home = instance_storage_home
# The following properties are updated on update() method
self.public_ipv4_address = None self.public_ipv4_address = None
self.os_type = None self.os_type = None
self.python_version = None self.python_version = None
self.slapos_version = None self.slapos_version = None
# HASA relation to managers (could turn into plugins with `format` and `update` methods)
self.manager_list = manager_list # for serialization
# hide list[Manager] from serializer by prepending "_"
self._manager_list = tuple(filter(lambda manager: manager.is_allowed(),
(available_manager_list[manager_str](self) for manager_str in manager_list))) \
if manager_list else tuple()
def __getinitargs__(self): def __getinitargs__(self):
return (self.reference, self.interface) return (self.reference, self.interface)
...@@ -304,9 +508,12 @@ class Computer(object): ...@@ -304,9 +508,12 @@ class Computer(object):
raise NoAddressOnInterface('No valid IPv6 found on %s.' % self.interface.name) raise NoAddressOnInterface('No valid IPv6 found on %s.' % self.interface.name)
def update(self): def update(self):
""" """Update computer runtime info and state."""
Collect environmental hardware/network information. for manager in self._manager_list:
""" logger.info("Updating computer with " + manager.__class__.__name__)
manager.update()
# Collect environmental hardware/network information.
self.public_ipv4_address = getPublicIPv4Address() self.public_ipv4_address = getPublicIPv4Address()
self.slapos_version = version.version self.slapos_version = version.version
self.python_version = platform.python_version() self.python_version = platform.python_version()
...@@ -392,7 +599,8 @@ class Computer(object): ...@@ -392,7 +599,8 @@ class Computer(object):
archive.writestr(saved_filename, xml_content, zipfile.ZIP_DEFLATED) archive.writestr(saved_filename, xml_content, zipfile.ZIP_DEFLATED)
@classmethod @classmethod
def load(cls, path_to_xml, reference, ipv6_interface, tap_gateway_interface): def load(cls, path_to_xml, reference, ipv6_interface, tap_gateway_interface,
instance_root=None, software_root=None, manager_list=None):
""" """
Create a computer object from a valid xml file. Create a computer object from a valid xml file.
...@@ -414,6 +622,9 @@ class Computer(object): ...@@ -414,6 +622,9 @@ class Computer(object):
ipv6_interface=ipv6_interface, ipv6_interface=ipv6_interface,
software_user=dumped_dict.get('software_user', 'slapsoft'), software_user=dumped_dict.get('software_user', 'slapsoft'),
tap_gateway_interface=tap_gateway_interface, tap_gateway_interface=tap_gateway_interface,
software_root=dumped_dict.get('software_root', software_root),
instance_root=dumped_dict.get('instance_root', instance_root),
manager_list=dumped_dict.get('manager_list', manager_list),
) )
for partition_index, partition_dict in enumerate(dumped_dict['partition_list']): for partition_index, partition_dict in enumerate(dumped_dict['partition_list']):
...@@ -492,9 +703,22 @@ class Computer(object): ...@@ -492,9 +703,22 @@ class Computer(object):
command = 'ip address add dev %s fd00::1/64' % interface_name command = 'ip address add dev %s fd00::1/64' % interface_name
callAndRead(command.split()) callAndRead(command.split())
def construct(self, alter_user=True, alter_network=True, create_tap=True, use_unique_local_address_block=False): @property
def software_gid(self):
"""Return GID for self.software_user.
Has to be dynamic because __init__ happens before ``format`` where we
effectively create the user and group."""
return pwd.getpwnam(self.software_user)[3]
def format(self, alter_user=True, alter_network=True, create_tap=True, use_unique_local_address_block=False):
""" """
Construct the computer object as it is. Setup underlaying OS so it reflects this instance (``self``).
- setup interfaces and addresses
- setup TAP and TUN interfaces
- add groups and users
- construct partitions inside slapgrid
""" """
if alter_network and self.address is not None: if alter_network and self.address is not None:
self.interface.addAddr(self.address, self.netmask) self.interface.addAddr(self.address, self.netmask)
...@@ -521,6 +745,11 @@ class Computer(object): ...@@ -521,6 +745,11 @@ class Computer(object):
os.chown(slapsoft.path, slapsoft_pw.pw_uid, slapsoft_pw.pw_gid) os.chown(slapsoft.path, slapsoft_pw.pw_uid, slapsoft_pw.pw_gid)
os.chmod(self.software_root, 0o755) os.chmod(self.software_root, 0o755)
# Iterate over all managers and let them `format` the computer too
for manager in self._manager_list:
logger.info("Formatting computer with " + manager.__class__.__name__)
manager.format()
# get list of instance external storage if exist # get list of instance external storage if exist
instance_external_list = [] instance_external_list = []
if self.instance_storage_home: if self.instance_storage_home:
...@@ -954,7 +1183,7 @@ class Interface(object): ...@@ -954,7 +1183,7 @@ class Interface(object):
name: String, the name of the interface name: String, the name of the interface
""" """
self.logger = logger self._logger = logger
self.name = str(name) self.name = str(name)
self.ipv4_local_network = ipv4_local_network self.ipv4_local_network = ipv4_local_network
self.ipv6_interface = ipv6_interface self.ipv6_interface = ipv6_interface
...@@ -1138,7 +1367,7 @@ class Interface(object): ...@@ -1138,7 +1367,7 @@ class Interface(object):
if self._addSystemAddress(addr, netmask, False): if self._addSystemAddress(addr, netmask, False):
return dict(addr=addr, netmask=netmask) return dict(addr=addr, netmask=netmask)
else: else:
self.logger.warning('Impossible to add old local IPv4 %s. Generating ' self._logger.warning('Impossible to add old local IPv4 %s. Generating '
'new IPv4 address.' % addr) 'new IPv4 address.' % addr)
return self._generateRandomIPv4Address(netmask) return self._generateRandomIPv4Address(netmask)
else: else:
...@@ -1196,7 +1425,7 @@ class Interface(object): ...@@ -1196,7 +1425,7 @@ class Interface(object):
# succeed, return it # succeed, return it
return dict(addr=addr, netmask=netmask) return dict(addr=addr, netmask=netmask)
else: else:
self.logger.warning('Impossible to add old public IPv6 %s. ' self._logger.warning('Impossible to add old public IPv6 %s. '
'Generating new IPv6 address.' % addr) 'Generating new IPv6 address.' % addr)
# Try 10 times to add address, raise in case if not possible # Try 10 times to add address, raise in case if not possible
...@@ -1276,7 +1505,10 @@ def parse_computer_xml(conf, xml_path): ...@@ -1276,7 +1505,10 @@ def parse_computer_xml(conf, xml_path):
computer = Computer.load(xml_path, computer = Computer.load(xml_path,
reference=conf.computer_id, reference=conf.computer_id,
ipv6_interface=conf.ipv6_interface, ipv6_interface=conf.ipv6_interface,
tap_gateway_interface=conf.tap_gateway_interface) tap_gateway_interface=conf.tap_gateway_interface,
software_root=conf.software_root,
instance_root=conf.instance_root,
manager_list=conf.manager_list)
# Connect to the interface defined by the configuration # Connect to the interface defined by the configuration
computer.interface = interface computer.interface = interface
else: else:
...@@ -1284,12 +1516,15 @@ def parse_computer_xml(conf, xml_path): ...@@ -1284,12 +1516,15 @@ def parse_computer_xml(conf, xml_path):
conf.logger.warning('Creating new computer data with id %r', conf.computer_id) conf.logger.warning('Creating new computer data with id %r', conf.computer_id)
computer = Computer( computer = Computer(
reference=conf.computer_id, reference=conf.computer_id,
software_root=conf.software_root,
instance_root=conf.software_root,
interface=interface, interface=interface,
addr=None, addr=None,
netmask=None, netmask=None,
ipv6_interface=conf.ipv6_interface, ipv6_interface=conf.ipv6_interface,
software_user=conf.software_user, software_user=conf.software_user,
tap_gateway_interface=conf.tap_gateway_interface, tap_gateway_interface=conf.tap_gateway_interface,
manager_list=conf.manager_list,
) )
partition_amount = int(conf.partition_amount) partition_amount = int(conf.partition_amount)
...@@ -1360,8 +1595,6 @@ def do_format(conf): ...@@ -1360,8 +1595,6 @@ def do_format(conf):
# no definition file, figure out computer # no definition file, figure out computer
computer = parse_computer_xml(conf, conf.computer_xml) computer = parse_computer_xml(conf, conf.computer_xml)
computer.instance_root = conf.instance_root
computer.software_root = conf.software_root
computer.instance_storage_home = conf.instance_storage_home computer.instance_storage_home = conf.instance_storage_home
conf.logger.info('Updating computer') conf.logger.info('Updating computer')
address = computer.getAddress(conf.create_tap) address = computer.getAddress(conf.create_tap)
...@@ -1371,7 +1604,7 @@ def do_format(conf): ...@@ -1371,7 +1604,7 @@ def do_format(conf):
if conf.output_definition_file: if conf.output_definition_file:
write_computer_definition(conf, computer) write_computer_definition(conf, computer)
computer.construct(alter_user=conf.alter_user, computer.format(alter_user=conf.alter_user,
alter_network=conf.alter_network, alter_network=conf.alter_network,
create_tap=conf.create_tap, create_tap=conf.create_tap,
use_unique_local_address_block=conf.use_unique_local_address_block) use_unique_local_address_block=conf.use_unique_local_address_block)
...@@ -1406,6 +1639,7 @@ class FormatConfig(object): ...@@ -1406,6 +1639,7 @@ class FormatConfig(object):
tap_gateway_interface = None tap_gateway_interface = None
use_unique_local_address_block = None use_unique_local_address_block = None
instance_storage_home = None instance_storage_home = None
manager_list = None
def __init__(self, logger): def __init__(self, logger):
self.logger = logger self.logger = logger
...@@ -1493,6 +1727,16 @@ class FormatConfig(object): ...@@ -1493,6 +1727,16 @@ class FormatConfig(object):
self.logger.error(message) self.logger.error(message)
raise UsageError(message) raise UsageError(message)
# Split str into list[str] and check availability of every manager
# Config value is expected to be strings separated by spaces or commas
manager_list = []
for manager in self.manager_list.replace(",", " ").split():
if manager not in available_manager_list:
raise ValueError("Unknown manager \"{}\"! Known are: {!s}".format(
manager, list(available_manager_list.keys())))
manager_list.append(manager)
self.manager_list = manager_list # replace original str with list[str] of known managers
if not self.dry_run: if not self.dry_run:
if self.alter_user: if self.alter_user:
self.checkRequiredBinary(['groupadd', 'useradd', 'usermod', ['passwd', '-h']]) self.checkRequiredBinary(['groupadd', 'useradd', 'usermod', ['passwd', '-h']])
......
...@@ -26,15 +26,15 @@ ...@@ -26,15 +26,15 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# #
############################################################################## ##############################################################################
import glob
import logging import logging
import slapos.format import slapos.format
import slapos.util import slapos.util
import unittest import unittest
import netaddr import netaddr
import shutil
import socket import socket
# for mocking # for mocking
import grp import grp
import netifaces import netifaces
...@@ -47,6 +47,20 @@ GROUP_LIST = [] ...@@ -47,6 +47,20 @@ GROUP_LIST = []
INTERFACE_DICT = {} INTERFACE_DICT = {}
def file_content(file_path):
"""Read file(s) content."""
if isinstance(file_path, (list, tuple)):
return [file_content(fx) for fx in file_path]
with open(file_path, "rt") as fi:
return fi.read().strip()
def file_write(stuff, file_path):
"""Write stuff into file_path."""
with open(file_path, "wt") as fo:
fo.write(stuff)
class FakeConfig: class FakeConfig:
pass pass
...@@ -138,11 +152,16 @@ class PwdMock: ...@@ -138,11 +152,16 @@ class PwdMock:
def getpwnam(self, name): def getpwnam(self, name):
global USER_LIST global USER_LIST
if name in USER_LIST: if name in USER_LIST:
class result: class PwdResult:
pw_uid = 0 pw_uid = 0
pw_gid = 0 pw_gid = 0
return result def __getitem__(self, index):
raise KeyError if index == 2:
return self.pw_uid
if index == 3:
return self.pw_gid
return PwdResult()
raise KeyError("User \"{}\" not in global USER_LIST {!s}".format(name, USER_LIST))
class NetifacesMock: class NetifacesMock:
...@@ -151,7 +170,8 @@ class NetifacesMock: ...@@ -151,7 +170,8 @@ class NetifacesMock:
global INTERFACE_DICT global INTERFACE_DICT
if name in INTERFACE_DICT: if name in INTERFACE_DICT:
return INTERFACE_DICT[name] return INTERFACE_DICT[name]
raise ValueError raise ValueError("Interface \"{}\" not in INTERFACE_DICT {!s}".format(
name, INTERFACE_DICT))
@classmethod @classmethod
def interfaces(self): def interfaces(self):
...@@ -163,6 +183,21 @@ class SlaposUtilMock: ...@@ -163,6 +183,21 @@ class SlaposUtilMock:
def chownDirectory(*args, **kw): def chownDirectory(*args, **kw):
pass pass
class CGroupManagerMock(slapos.format.CGroupManager):
short_name = 'cgroup_mock'
cpuset_path = "/tmp/cpuset/"
task_write_mode = "at" # append insted of write tasks PIDs for the tests
def is_allowed(self):
"""Always allowed."""
return True
# update available managers with our partially-mocked version
slapos.format.available_manager_list[CGroupManagerMock.short_name] = CGroupManagerMock
class SlapformatMixin(unittest.TestCase): class SlapformatMixin(unittest.TestCase):
# keep big diffs # keep big diffs
maxDiff = None maxDiff = None
...@@ -220,6 +255,8 @@ class SlapformatMixin(unittest.TestCase): ...@@ -220,6 +255,8 @@ class SlapformatMixin(unittest.TestCase):
setattr(os, fake, f) setattr(os, fake, f)
def restoreOs(self): def restoreOs(self):
if not hasattr(self, 'saved_os'):
return # os was never patched or already restored
for name, original_value in self.saved_os.items(): for name, original_value in self.saved_os.items():
setattr(os, name, original_value) setattr(os, name, original_value)
del self.saved_os del self.saved_os
...@@ -278,23 +315,23 @@ class SlapformatMixin(unittest.TestCase): ...@@ -278,23 +315,23 @@ class SlapformatMixin(unittest.TestCase):
class TestComputer(SlapformatMixin): class TestComputer(SlapformatMixin):
def test_getAddress_empty_computer(self): def test_getAddress_empty_computer(self):
computer = slapos.format.Computer('computer') computer = slapos.format.Computer('computer', instance_root='/instance_root', software_root='software_root')
self.assertEqual(computer.getAddress(), {'netmask': None, 'addr': None}) self.assertEqual(computer.getAddress(), {'netmask': None, 'addr': None})
@unittest.skip("Not implemented") @unittest.skip("Not implemented")
def test_construct_empty(self): def test_construct_empty(self):
computer = slapos.format.Computer('computer') computer = slapos.format.Computer('computer', instance_root='/instance_root', software_root='software_root')
computer.construct() computer.format()
@unittest.skip("Not implemented") @unittest.skip("Not implemented")
def test_construct_empty_prepared(self): def test_construct_empty_prepared(self):
computer = slapos.format.Computer('computer', computer = slapos.format.Computer('computer',
interface=slapos.format.Interface(logger=self.logger, instance_root='/instance_root',
name='bridge', software_root='/software_root',
ipv4_local_network='127.0.0.1/16')) interface=slapos.format.Interface(
computer.instance_root = '/instance_root' logger=self.logger, name='bridge', ipv4_local_network='127.0.0.1/16'),
computer.software_root = '/software_root' partition_list=[])
computer.construct() computer.format()
self.assertEqual([ self.assertEqual([
"makedirs('/instance_root', 493)", "makedirs('/instance_root', 493)",
"makedirs('/software_root', 493)", "makedirs('/software_root', 493)",
...@@ -310,29 +347,31 @@ class TestComputer(SlapformatMixin): ...@@ -310,29 +347,31 @@ class TestComputer(SlapformatMixin):
def test_construct_empty_prepared_no_alter_user(self): def test_construct_empty_prepared_no_alter_user(self):
computer = slapos.format.Computer('computer', computer = slapos.format.Computer('computer',
interface=slapos.format.Interface(logger=self.logger, instance_root='/instance_root',
name='bridge', software_root='/software_root',
ipv4_local_network='127.0.0.1/16')) interface=slapos.format.Interface(
computer.instance_root = '/instance_root' logger=self.logger, name='bridge', ipv4_local_network='127.0.0.1/16'),
computer.software_root = '/software_root' partition_list=[])
computer.construct(alter_user=False) computer.format(alter_user=False)
self.assertEqual([ self.assertEqual([
"makedirs('/instance_root', 493)", "makedirs('/instance_root', 493)",
"makedirs('/software_root', 493)", "makedirs('/software_root', 493)",
"chmod('/software_root', 493)"], "chmod('/software_root', 493)"],
self.test_result.bucket) self.test_result.bucket)
self.assertEqual(['ip addr list bridge'], self.assertEqual([
'ip addr list bridge',
'brctl show',
],
self.fakeCallAndRead.external_command_list) self.fakeCallAndRead.external_command_list)
@unittest.skip("Not implemented") @unittest.skip("Not implemented")
def test_construct_empty_prepared_no_alter_network(self): def test_construct_empty_prepared_no_alter_network(self):
computer = slapos.format.Computer('computer', computer = slapos.format.Computer('computer',
interface=slapos.format.Interface(logger=self.logger, instance_root='/instance_root',
name='bridge', software_root='/software_root',
ipv4_local_network='127.0.0.1/16')) interface=slapos.format.Interface(
computer.instance_root = '/instance_root' logger=self.logger, name='bridge', ipv4_local_network='127.0.0.1/16'),
computer.software_root = '/software_root' partition_list=[])
computer.construct(alter_network=False)
self.assertEqual([ self.assertEqual([
"makedirs('/instance_root', 493)", "makedirs('/instance_root', 493)",
"makedirs('/software_root', 493)", "makedirs('/software_root', 493)",
...@@ -348,34 +387,34 @@ class TestComputer(SlapformatMixin): ...@@ -348,34 +387,34 @@ class TestComputer(SlapformatMixin):
def test_construct_empty_prepared_no_alter_network_user(self): def test_construct_empty_prepared_no_alter_network_user(self):
computer = slapos.format.Computer('computer', computer = slapos.format.Computer('computer',
interface=slapos.format.Interface(logger=self.logger, instance_root='/instance_root',
name='bridge', software_root='/software_root',
ipv4_local_network='127.0.0.1/16')) interface=slapos.format.Interface(
computer.instance_root = '/instance_root' logger=self.logger, name='bridge', ipv4_local_network='127.0.0.1/16'),
computer.software_root = '/software_root' partition_list=[])
computer.construct(alter_network=False, alter_user=False) computer.format(alter_network=False, alter_user=False)
self.assertEqual([ self.assertEqual([
"makedirs('/instance_root', 493)", "makedirs('/instance_root', 493)",
"makedirs('/software_root', 493)", "makedirs('/software_root', 493)",
"chmod('/software_root', 493)"], "chmod('/software_root', 493)"],
self.test_result.bucket) self.test_result.bucket)
self.assertEqual([ self.assertEqual([
'ip addr list bridge' 'ip addr list bridge',
'brctl show',
], ],
self.fakeCallAndRead.external_command_list) self.fakeCallAndRead.external_command_list)
@unittest.skip("Not implemented") @unittest.skip("Not implemented")
def test_construct_prepared(self): def test_construct_prepared(self):
computer = slapos.format.Computer('computer', computer = slapos.format.Computer('computer',
interface=slapos.format.Interface(logger=self.logger, instance_root='/instance_root',
name='bridge', software_root='/software_root',
ipv4_local_network='127.0.0.1/16')) interface=slapos.format.Interface(
computer.instance_root = '/instance_root' logger=self.logger, name='bridge', ipv4_local_network='127.0.0.1/16'),
computer.software_root = '/software_root' partition_list=[
partition = slapos.format.Partition('partition', '/part_path', slapos.format.Partition(
slapos.format.User('testuser'), [], None) 'partition', '/part_path', slapos.format.User('testuser'), [], tap=slapos.format.Tap('tap')),
partition.tap = slapos.format.Tap('tap') ])
computer.partition_list = [partition]
global INTERFACE_DICT global INTERFACE_DICT
INTERFACE_DICT['bridge'] = { INTERFACE_DICT['bridge'] = {
socket.AF_INET: [{'addr': '192.168.242.77', 'broadcast': '127.0.0.1', socket.AF_INET: [{'addr': '192.168.242.77', 'broadcast': '127.0.0.1',
...@@ -383,7 +422,7 @@ class TestComputer(SlapformatMixin): ...@@ -383,7 +422,7 @@ class TestComputer(SlapformatMixin):
socket.AF_INET6: [{'addr': '2a01:e35:2e27::e59c', 'netmask': 'ffff:ffff:ffff:ffff::'}] socket.AF_INET6: [{'addr': '2a01:e35:2e27::e59c', 'netmask': 'ffff:ffff:ffff:ffff::'}]
} }
computer.construct() computer.format()
self.assertEqual([ self.assertEqual([
"makedirs('/instance_root', 493)", "makedirs('/instance_root', 493)",
"makedirs('/software_root', 493)", "makedirs('/software_root', 493)",
...@@ -400,7 +439,8 @@ class TestComputer(SlapformatMixin): ...@@ -400,7 +439,8 @@ class TestComputer(SlapformatMixin):
'useradd -d /software_root -g slapsoft slapsoft -r', 'useradd -d /software_root -g slapsoft slapsoft -r',
'groupadd testuser', 'groupadd testuser',
'useradd -d /instance_root/partition -g testuser -G slapsoft testuser -r', 'useradd -d /instance_root/partition -g testuser -G slapsoft testuser -r',
'tunctl -t tap -u testuser', 'brctl show',
'ip tuntap add dev tap mode tap user testuser',
'ip link set tap up', 'ip link set tap up',
'brctl show', 'brctl show',
'brctl addif bridge tap', 'brctl addif bridge tap',
...@@ -413,17 +453,16 @@ class TestComputer(SlapformatMixin): ...@@ -413,17 +453,16 @@ class TestComputer(SlapformatMixin):
def test_construct_prepared_no_alter_user(self): def test_construct_prepared_no_alter_user(self):
computer = slapos.format.Computer('computer', computer = slapos.format.Computer('computer',
interface=slapos.format.Interface(logger=self.logger, instance_root='/instance_root',
name='bridge', software_root='/software_root',
ipv4_local_network='127.0.0.1/16')) interface=slapos.format.Interface(
computer.instance_root = '/instance_root' logger=self.logger, name='bridge', ipv4_local_network='127.0.0.1/16'),
computer.software_root = '/software_root' partition_list=[
partition = slapos.format.Partition('partition', '/part_path', slapos.format.Partition(
slapos.format.User('testuser'), [], None) 'partition', '/part_path', slapos.format.User('testuser'), [], tap=slapos.format.Tap('tap')),
])
global USER_LIST global USER_LIST
USER_LIST = ['testuser'] USER_LIST = ['testuser']
partition.tap = slapos.format.Tap('tap')
computer.partition_list = [partition]
global INTERFACE_DICT global INTERFACE_DICT
INTERFACE_DICT['bridge'] = { INTERFACE_DICT['bridge'] = {
socket.AF_INET: [{'addr': '192.168.242.77', 'broadcast': '127.0.0.1', socket.AF_INET: [{'addr': '192.168.242.77', 'broadcast': '127.0.0.1',
...@@ -431,7 +470,7 @@ class TestComputer(SlapformatMixin): ...@@ -431,7 +470,7 @@ class TestComputer(SlapformatMixin):
socket.AF_INET6: [{'addr': '2a01:e35:2e27::e59c', 'netmask': 'ffff:ffff:ffff:ffff::'}] socket.AF_INET6: [{'addr': '2a01:e35:2e27::e59c', 'netmask': 'ffff:ffff:ffff:ffff::'}]
} }
computer.construct(alter_user=False) computer.format(alter_user=False)
self.assertEqual([ self.assertEqual([
"makedirs('/instance_root', 493)", "makedirs('/instance_root', 493)",
"makedirs('/software_root', 493)", "makedirs('/software_root', 493)",
...@@ -442,9 +481,10 @@ class TestComputer(SlapformatMixin): ...@@ -442,9 +481,10 @@ class TestComputer(SlapformatMixin):
self.test_result.bucket) self.test_result.bucket)
self.assertEqual([ self.assertEqual([
'ip addr list bridge', 'ip addr list bridge',
'tunctl -t tap -u testuser', 'brctl show',
'ip tuntap add dev tap mode tap user testuser',
'ip link set tap up', 'ip link set tap up',
'brctl show', # we check bridges at Interface initialization 'brctl show',
'brctl show', 'brctl show',
'brctl addif bridge tap', 'brctl addif bridge tap',
'ip addr add ip/255.255.255.255 dev bridge', 'ip addr add ip/255.255.255.255 dev bridge',
...@@ -456,18 +496,18 @@ class TestComputer(SlapformatMixin): ...@@ -456,18 +496,18 @@ class TestComputer(SlapformatMixin):
def test_construct_prepared_tap_no_alter_user(self): def test_construct_prepared_tap_no_alter_user(self):
computer = slapos.format.Computer('computer', computer = slapos.format.Computer('computer',
interface=slapos.format.Interface(logger=self.test_result, instance_root='/instance_root',
name='iface', software_root='/software_root',
ipv4_local_network='127.0.0.1/16'), tap_gateway_interface='eth1',
tap_gateway_interface='eth1') interface=slapos.format.Interface(
computer.instance_root = '/instance_root' logger=self.logger, name='iface', ipv4_local_network='127.0.0.1/16'),
computer.software_root = '/software_root' partition_list=[
partition = slapos.format.Partition('partition', '/part_path', slapos.format.Partition(
slapos.format.User('testuser'), [], None) 'partition', '/part_path', slapos.format.User('testuser'), [],
tap=slapos.format.Tap('tap')),
])
global USER_LIST global USER_LIST
USER_LIST = ['testuser'] USER_LIST = ['testuser']
partition.tap = slapos.format.Tap('tap')
computer.partition_list = [partition]
global INTERFACE_DICT global INTERFACE_DICT
INTERFACE_DICT['iface'] = { INTERFACE_DICT['iface'] = {
socket.AF_INET: [{'addr': '192.168.242.77', 'broadcast': '127.0.0.1', socket.AF_INET: [{'addr': '192.168.242.77', 'broadcast': '127.0.0.1',
...@@ -479,7 +519,7 @@ class TestComputer(SlapformatMixin): ...@@ -479,7 +519,7 @@ class TestComputer(SlapformatMixin):
'netmask': '255.255.255.0'}] 'netmask': '255.255.255.0'}]
} }
computer.construct(alter_user=False) computer.format(alter_user=False)
self.assertEqual([ self.assertEqual([
"makedirs('/instance_root', 493)", "makedirs('/instance_root', 493)",
"makedirs('/software_root', 493)", "makedirs('/software_root', 493)",
...@@ -490,15 +530,17 @@ class TestComputer(SlapformatMixin): ...@@ -490,15 +530,17 @@ class TestComputer(SlapformatMixin):
self.test_result.bucket) self.test_result.bucket)
self.assertEqual([ self.assertEqual([
'ip addr list iface', 'ip addr list iface',
'tunctl -t tap -u testuser', 'brctl show',
'ip tuntap add dev tap mode tap user testuser',
'ip link set tap up', 'ip link set tap up',
'ip route show 10.8.0.2', 'ip route show 10.8.0.2',
'route add -host 10.8.0.2 dev tap', 'ip route add 10.8.0.2 dev tap',
'ip addr add ip/255.255.255.255 dev iface', 'ip addr add ip/255.255.255.255 dev iface',
'ip addr add ip/ffff:ffff:ffff:ffff:: dev iface', 'ip addr add ip/ffff:ffff:ffff:ffff:: dev iface',
'ip -6 addr list iface' 'ip -6 addr list iface'
], ],
self.fakeCallAndRead.external_command_list) self.fakeCallAndRead.external_command_list)
partition = computer.partition_list[0]
self.assertEqual(partition.tap.ipv4_addr, '10.8.0.2') self.assertEqual(partition.tap.ipv4_addr, '10.8.0.2')
self.assertEqual(partition.tap.ipv4_netmask, '255.255.255.0') self.assertEqual(partition.tap.ipv4_netmask, '255.255.255.0')
self.assertEqual(partition.tap.ipv4_gateway, '10.8.0.1') self.assertEqual(partition.tap.ipv4_gateway, '10.8.0.1')
...@@ -507,23 +549,22 @@ class TestComputer(SlapformatMixin): ...@@ -507,23 +549,22 @@ class TestComputer(SlapformatMixin):
@unittest.skip("Not implemented") @unittest.skip("Not implemented")
def test_construct_prepared_no_alter_network(self): def test_construct_prepared_no_alter_network(self):
computer = slapos.format.Computer('computer', computer = slapos.format.Computer('computer',
interface=slapos.format.Interface(logger=self.logger, instance_root='/instance_root',
name='bridge', software_root='/software_root',
ipv4_local_network='127.0.0.1/16')) interface=slapos.format.Interface(
computer.instance_root = '/instance_root' logger=self.logger, name='bridge', ipv4_local_network='127.0.0.1/16'),
computer.software_root = '/software_root' partition_list=[
partition = slapos.format.Partition('partition', '/part_path', slapos.format.Partition(
slapos.format.User('testuser'), [], None) 'partition', '/part_path', slapos.format.User('testuser'), [],
partition.tap = slapos.format.Tap('tap') tap=slapos.format.Tap('tap')),
computer.partition_list = [partition] ])
global INTERFACE_DICT global INTERFACE_DICT
INTERFACE_DICT['bridge'] = { INTERFACE_DICT['bridge'] = {
socket.AF_INET: [{'addr': '192.168.242.77', 'broadcast': '127.0.0.1', socket.AF_INET: [{'addr': '192.168.242.77', 'broadcast': '127.0.0.1',
'netmask': '255.255.255.0'}], 'netmask': '255.255.255.0'}],
socket.AF_INET6: [{'addr': '2a01:e35:2e27::e59c', 'netmask': 'ffff:ffff:ffff:ffff::'}] socket.AF_INET6: [{'addr': '2a01:e35:2e27::e59c', 'netmask': 'ffff:ffff:ffff:ffff::'}]
} }
computer.format(alter_network=False)
computer.construct(alter_network=False)
self.assertEqual([ self.assertEqual([
"makedirs('/instance_root', 493)", "makedirs('/instance_root', 493)",
"makedirs('/software_root', 493)", "makedirs('/software_root', 493)",
...@@ -549,15 +590,15 @@ class TestComputer(SlapformatMixin): ...@@ -549,15 +590,15 @@ class TestComputer(SlapformatMixin):
def test_construct_prepared_no_alter_network_user(self): def test_construct_prepared_no_alter_network_user(self):
computer = slapos.format.Computer('computer', computer = slapos.format.Computer('computer',
interface=slapos.format.Interface(logger=self.logger, instance_root='/instance_root',
name='bridge', software_root='/software_root',
ipv4_local_network='127.0.0.1/16')) interface=slapos.format.Interface(
computer.instance_root = '/instance_root' logger=self.logger, name='bridge', ipv4_local_network='127.0.0.1/16'),
computer.software_root = '/software_root' partition_list=[
partition = slapos.format.Partition('partition', '/part_path', slapos.format.Partition(
slapos.format.User('testuser'), [], None) 'partition', '/part_path', slapos.format.User('testuser'), [],
partition.tap = slapos.format.Tap('tap') tap=slapos.format.Tap('tap')),
computer.partition_list = [partition] ])
global INTERFACE_DICT global INTERFACE_DICT
INTERFACE_DICT['bridge'] = { INTERFACE_DICT['bridge'] = {
socket.AF_INET: [{'addr': '192.168.242.77', 'broadcast': '127.0.0.1', socket.AF_INET: [{'addr': '192.168.242.77', 'broadcast': '127.0.0.1',
...@@ -565,7 +606,7 @@ class TestComputer(SlapformatMixin): ...@@ -565,7 +606,7 @@ class TestComputer(SlapformatMixin):
socket.AF_INET6: [{'addr': '2a01:e35:2e27::e59c', 'netmask': 'ffff:ffff:ffff:ffff::'}] socket.AF_INET6: [{'addr': '2a01:e35:2e27::e59c', 'netmask': 'ffff:ffff:ffff:ffff::'}]
} }
computer.construct(alter_network=False, alter_user=False) computer.format(alter_network=False, alter_user=False)
self.assertEqual([ self.assertEqual([
"makedirs('/instance_root', 493)", "makedirs('/instance_root', 493)",
"makedirs('/software_root', 493)", "makedirs('/software_root', 493)",
...@@ -576,6 +617,7 @@ class TestComputer(SlapformatMixin): ...@@ -576,6 +617,7 @@ class TestComputer(SlapformatMixin):
self.test_result.bucket) self.test_result.bucket)
self.assertEqual([ self.assertEqual([
'ip addr list bridge', 'ip addr list bridge',
'brctl show',
'ip addr add ip/255.255.255.255 dev bridge', 'ip addr add ip/255.255.255.255 dev bridge',
# 'ip addr list bridge', # 'ip addr list bridge',
'ip addr add ip/ffff:ffff:ffff:ffff:: dev bridge', 'ip addr add ip/ffff:ffff:ffff:ffff:: dev bridge',
...@@ -590,15 +632,15 @@ class TestComputer(SlapformatMixin): ...@@ -590,15 +632,15 @@ class TestComputer(SlapformatMixin):
global USER_LIST global USER_LIST
USER_LIST = ['root'] USER_LIST = ['root']
computer = slapos.format.Computer('computer', computer = slapos.format.Computer('computer',
interface=slapos.format.Interface(logger=self.logger, instance_root='/instance_root',
name='myinterface', software_root='/software_root',
ipv4_local_network='127.0.0.1/16')) interface=slapos.format.Interface(
computer.instance_root = '/instance_root' logger=self.logger, name='myinterface', ipv4_local_network='127.0.0.1/16'),
computer.software_root = '/software_root' partition_list=[
partition = slapos.format.Partition('partition', '/part_path', slapos.format.Partition(
slapos.format.User('testuser'), [], None) 'partition', '/part_path', slapos.format.User('testuser'), [],
partition.tap = slapos.format.Tap('tap') tap=slapos.format.Tap('tap')),
computer.partition_list = [partition] ])
global INTERFACE_DICT global INTERFACE_DICT
INTERFACE_DICT['myinterface'] = { INTERFACE_DICT['myinterface'] = {
socket.AF_INET: [{'addr': '192.168.242.77', 'broadcast': '127.0.0.1', socket.AF_INET: [{'addr': '192.168.242.77', 'broadcast': '127.0.0.1',
...@@ -606,7 +648,7 @@ class TestComputer(SlapformatMixin): ...@@ -606,7 +648,7 @@ class TestComputer(SlapformatMixin):
socket.AF_INET6: [{'addr': '2a01:e35:2e27::e59c', 'netmask': 'ffff:ffff:ffff:ffff::'}] socket.AF_INET6: [{'addr': '2a01:e35:2e27::e59c', 'netmask': 'ffff:ffff:ffff:ffff::'}]
} }
computer.construct(use_unique_local_address_block=True, alter_user=False, create_tap=False) computer.format(use_unique_local_address_block=True, alter_user=False, create_tap=False)
self.assertEqual([ self.assertEqual([
"makedirs('/instance_root', 493)", "makedirs('/instance_root', 493)",
"makedirs('/software_root', 493)", "makedirs('/software_root', 493)",
...@@ -617,6 +659,7 @@ class TestComputer(SlapformatMixin): ...@@ -617,6 +659,7 @@ class TestComputer(SlapformatMixin):
self.test_result.bucket) self.test_result.bucket)
self.assertEqual([ self.assertEqual([
'ip addr list myinterface', 'ip addr list myinterface',
'brctl show',
'ip address add dev myinterface fd00::1/64', 'ip address add dev myinterface fd00::1/64',
'ip addr add ip/255.255.255.255 dev myinterface', 'ip addr add ip/255.255.255.255 dev myinterface',
'ip addr add ip/ffff:ffff:ffff:ffff:: dev myinterface', 'ip addr add ip/ffff:ffff:ffff:ffff:: dev myinterface',
...@@ -624,6 +667,89 @@ class TestComputer(SlapformatMixin): ...@@ -624,6 +667,89 @@ class TestComputer(SlapformatMixin):
], ],
self.fakeCallAndRead.external_command_list) self.fakeCallAndRead.external_command_list)
class TestComputerWithCGroup(SlapformatMixin):
def setUp(self):
super(TestComputerWithCGroup, self).setUp()
self.restoreOs()
if os.path.isdir("/tmp/slapgrid/"):
shutil.rmtree("/tmp/slapgrid/")
os.mkdir("/tmp/slapgrid/")
if os.path.isdir(CGroupManagerMock.cpuset_path):
shutil.rmtree(CGroupManagerMock.cpuset_path)
os.mkdir(CGroupManagerMock.cpuset_path)
file_write("0,1-3",
os.path.join(CGroupManagerMock.cpuset_path, "cpuset.cpus"))
file_write("\n".join(("1000", "1001", "1002", "")),
os.path.join(CGroupManagerMock.cpuset_path, "tasks"))
self.cpu_list = [0, 1, 2, 3]
global USER_LIST, INTERFACE_DICT
USER_LIST = ['testuser']
INTERFACE_DICT['bridge'] = {
socket.AF_INET: [
{'addr': '127.0.0.1', 'broadcast': '127.0.255.255', 'netmask': '255.255.0.0'}],
socket.AF_INET6: [
{'addr': '2a01:e35:2e27::e59c', 'netmask': 'ffff:ffff:ffff:ffff::'}]
}
self.computer = slapos.format.Computer('computer',
software_user='testuser',
instance_root='/tmp/slapgrid/instance_root',
software_root='/tmp/slapgrid/software_root',
interface=slapos.format.Interface(
logger=self.logger, name='bridge', ipv4_local_network='127.0.0.1/16'),
partition_list=[
slapos.format.Partition(
'partition', '/tmp/slapgrid/instance_root/part1', slapos.format.User('testuser'), [], tap=None),
],
manager_list=(CGroupManagerMock.short_name, )
)
# self.patchOs(self.logger)
def tearDown(self):
"""Cleanup temporary test folders."""
super(TestComputerWithCGroup, self).tearDown()
shutil.rmtree("/tmp/slapgrid/")
if CGroupManagerMock.cpuset_path.startswith("/tmp"):
shutil.rmtree(CGroupManagerMock.cpuset_path)
def test_positive_cgroups(self):
"""Positive test of cgroups."""
# Test parsing "cpuset.cpus" file
self.assertEqual(self.computer._manager_list[0]._cpu_list(), self.cpu_list)
# This should created per-cpu groups and move all tasks in CPU pool into cpu0
self.computer.format(alter_network=False, alter_user=False)
# Test files creation for exclusive CPUs
for cpu_id in self.cpu_list:
cpu_n_path = os.path.join(CGroupManagerMock.cpuset_path, "cpu" + str(cpu_id))
self.assertEqual(str(cpu_id), file_content(os.path.join(cpu_n_path, "cpuset.cpus")))
self.assertEqual("1", file_content(os.path.join(cpu_n_path, "cpuset.cpu_exclusive")))
if cpu_id > 0:
self.assertEqual("", file_content(os.path.join(cpu_n_path, "tasks")))
# Test that format moved all PIDs from CPU pool into CPU0
tasks_at_cpu0 = file_content(os.path.join(CGroupManagerMock.cpuset_path, "cpu0", "tasks")).split()
self.assertIn("1000", tasks_at_cpu0)
self.assertIn("1001", tasks_at_cpu0)
self.assertIn("1002", tasks_at_cpu0)
# Simulate cgroup behaviour - empty tasks in the pool
file_write("", os.path.join(CGroupManagerMock.cpuset_path, "tasks"))
# test moving tasks from generic core to private core
# request PID 1001 to be moved to its private CPU
request_file_path = os.path.join(self.computer.partition_list[0].path,
CGroupManagerMock.cpu_exclusive_file)
file_write("1001\n", request_file_path)
# let format do the moving
self.computer.update()
# test if the moving suceeded into any provate CPUS (id>0)
self.assertTrue(any("1001" in file_content(exclusive_task)
for exclusive_task in glob.glob(os.path.join(CGroupManagerMock.cpuset_path, "cpu[1-9]", "tasks"))))
# slapformat should remove successfully moved PIDs from the .slapos-cpu-exclusive file
self.assertEqual("", file_content(request_file_path).strip())
class TestPartition(SlapformatMixin): class TestPartition(SlapformatMixin):
def test_createPath_no_alter_user(self): def test_createPath_no_alter_user(self):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment