Commit d393206c authored by Tomáš Peterka's avatar Tomáš Peterka

Extract cgroup-cpuset functionality into 'plugin-like' Manager class

parent 2a14a5c8
...@@ -237,35 +237,193 @@ def _getDict(obj): ...@@ -237,35 +237,193 @@ def _getDict(obj):
} }
class CGroupManager(object):
"""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.
"""
cpu_exclusive_file = ".slapos-cpu-exclusive"
cpuset_path = "/sys/fs/cgroup/cpuset/"
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
def 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."""
self.prepare_cpu_space()
self.ensure_exlusive_cpu()
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._list_cpus():
cpu_path = self._prepare_cgroup_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
"""
request_pid_set = set() # gather requests from all instances
for request_file in glob.iglob(os.path.join(self.instance_root, '*', CGroupManager.cpu_exclusive_file)):
with open(request_file, "rt") as fi:
request_pid_set.update(map(int, fi.read().split()))
cpu_list = self._list_cpus()
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
exlusive_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:
exlusive_pid_set.update(map(int, fi.read().split()))
for request in request_set:
if request in exclusive_pid_set:
continue # already exclusive
if request not in running_pid_set:
continue # stale PID which is not running anywhere
self._move_to_exclusive_cpu(request)
def prepare_cpu_space(self):
"""Move all PIDs from the pool of all CPUs into the first exclusive CPU."""
with open(self.cpuset_path + "tasks", "rt") as fi:
running_set = set(map(int, fi.read().split()))
first_cpu = self._list_cpus()[0]
task_path = os.path.join(self.cpuset_path, "cpu" + str(first_cpu), "tasks")
for pid in running_set:
with open(task_path, "wt") as fo:
fo.write(str(pid))
time.sleep(0.01)
def _list_cpus(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._list_cpus()[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(first_cpu), "tasks")
with open(task_path, "rt") as fi:
task_list = fi.read().split()
if len(task_list) > 0:
continue # skip occupied CPUs
with open(task_path, "wt") as fo:
fo.write(str(pid))
return exclusive_cpu
return -1
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
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 = instance_root
self.instance_root = software_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(self) for manager in manager_list if manager(self).allowed()) \
if manager_list else tuple()
def __getinitargs__(self): def __getinitargs__(self):
return (self.reference, self.interface) return (self.reference, self.interface)
...@@ -302,9 +460,11 @@ class Computer(object): ...@@ -302,9 +460,11 @@ 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:
""" 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()
...@@ -390,7 +550,8 @@ class Computer(object): ...@@ -390,7 +550,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):
""" """
Create a computer object from a valid xml file. Create a computer object from a valid xml file.
...@@ -412,6 +573,8 @@ class Computer(object): ...@@ -412,6 +573,8 @@ 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),
) )
for i, partition_dict in enumerate(dumped_dict['partition_list']): for i, partition_dict in enumerate(dumped_dict['partition_list']):
...@@ -494,61 +657,11 @@ class Computer(object): ...@@ -494,61 +657,11 @@ class Computer(object):
def software_gid(self): def software_gid(self):
"""Return GID for self.software_user. """Return GID for self.software_user.
Has to be dynamic because __init__ happens before ``construct`` where we Has to be dynamic because __init__ happens before ``format`` where we
effectively create the user and group.""" effectively create the user and group."""
return pwd.getpwnam(self.software_user)[3] 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):
def _prepare_cgroup_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)
return folder
def _prepare_cgroup_cpuset(self):
"""Create cgroup folder per-CPU with exclusive access to the CPU.
Those folders are "/sys/fs/cgroup/cpuset/cpu<N>".
"""
if not os.path.exists("/sys/fs/cgroup/cpuset/cpuset.cpus"):
self.logger.warining("Cannot prepare CGROUP CPUSET - not supported by current OS")
return
with open("/sys/fs/cgroup/cpuset/cpuset.cpus", "rt") as cpu_def:
# build up cpu_list of available CPU IDs
cpu_list = [] # types: list[int]
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))
# For every CPU ID create an exclusive cgroup
for cpu in cpu_list:
cpu_path = self._prepare_cgroup_folder(
os.path.join("/sys/fs/cgroup/cpuset/", "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
os.chown(cpu_path + "/tasks", -1, self.software_gid)
os.chmod(cpu_path + "/tasks", 0o664)
def prepare_cgroup(self):
"""Build CGROUP tree exclusively for slapos.
- Create hierarchy of CPU sets so that every partition can have exclusive
hold of one of the CPUs.
"""
self._prepare_cgroup_cpuset()
def construct(self, alter_user=True, alter_network=True, create_tap=True, use_unique_local_address_block=False):
""" """
Setup underlaying OS so it reflects this instance (``self``). Setup underlaying OS so it reflects this instance (``self``).
...@@ -582,8 +695,9 @@ class Computer(object): ...@@ -582,8 +695,9 @@ 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)
# Build own CGROUPS tree for resource control # Iterate over all managers and let them `format` the computer too
self.prepare_cgroup() for manager in self.manager_list:
manager.format()
# get list of instance external storage if exist # get list of instance external storage if exist
instance_external_list = [] instance_external_list = []
...@@ -1427,7 +1541,7 @@ def do_format(conf): ...@@ -1427,7 +1541,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)
......
...@@ -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
...@@ -158,6 +172,16 @@ class SlaposUtilMock: ...@@ -158,6 +172,16 @@ class SlaposUtilMock:
def chownDirectory(*args, **kw): def chownDirectory(*args, **kw):
pass pass
class CGroupManagerMock(slapos.format.CGroupManager):
cpuset_path = "/tmp/cpuset/"
def allowed(self):
"""Always allowed."""
return True
class SlapformatMixin(unittest.TestCase): class SlapformatMixin(unittest.TestCase):
# keep big diffs # keep big diffs
maxDiff = None maxDiff = None
...@@ -276,17 +300,17 @@ class TestComputer(SlapformatMixin): ...@@ -276,17 +300,17 @@ class TestComputer(SlapformatMixin):
@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')
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.test_result, 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)",
...@@ -302,12 +326,12 @@ class TestComputer(SlapformatMixin): ...@@ -302,12 +326,12 @@ 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.test_result, 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)",
...@@ -319,12 +343,12 @@ class TestComputer(SlapformatMixin): ...@@ -319,12 +343,12 @@ class TestComputer(SlapformatMixin):
@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.test_result, 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) computer.format(alter_network=False)
self.assertEqual([ self.assertEqual([
"makedirs('/instance_root', 493)", "makedirs('/instance_root', 493)",
"makedirs('/software_root', 493)", "makedirs('/software_root', 493)",
...@@ -340,12 +364,12 @@ class TestComputer(SlapformatMixin): ...@@ -340,12 +364,12 @@ 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.test_result, 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)",
...@@ -359,15 +383,15 @@ class TestComputer(SlapformatMixin): ...@@ -359,15 +383,15 @@ class TestComputer(SlapformatMixin):
@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.test_result, 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',
...@@ -375,7 +399,7 @@ class TestComputer(SlapformatMixin): ...@@ -375,7 +399,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)",
...@@ -405,13 +429,15 @@ class TestComputer(SlapformatMixin): ...@@ -405,13 +429,15 @@ 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.test_result, 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=None),
])
global USER_LIST global USER_LIST
USER_LIST = ['testuser'] USER_LIST = ['testuser']
partition.tap = slapos.format.Tap('tap') partition.tap = slapos.format.Tap('tap')
...@@ -423,7 +449,7 @@ class TestComputer(SlapformatMixin): ...@@ -423,7 +449,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)",
...@@ -447,14 +473,14 @@ class TestComputer(SlapformatMixin): ...@@ -447,14 +473,14 @@ 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'), interface=slapos.format.Interface(
tap_gateway_interface='eth1') logger=self.logger, name='iface', ipv4_local_network='127.0.0.1/16', tap_gateway_interface='eth1'),
computer.instance_root = '/instance_root' partition_list=[
computer.software_root = '/software_root' slapos.format.Partition(
partition = slapos.format.Partition('partition', '/part_path', 'partition', '/part_path', slapos.format.User('testuser'), [], tap=None),
slapos.format.User('testuser'), [], None) ])
global USER_LIST global USER_LIST
USER_LIST = ['testuser'] USER_LIST = ['testuser']
partition.tap = slapos.format.Tap('tap') partition.tap = slapos.format.Tap('tap')
...@@ -470,7 +496,7 @@ class TestComputer(SlapformatMixin): ...@@ -470,7 +496,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)",
...@@ -498,15 +524,15 @@ class TestComputer(SlapformatMixin): ...@@ -498,15 +524,15 @@ 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.test_result, 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',
...@@ -514,7 +540,7 @@ class TestComputer(SlapformatMixin): ...@@ -514,7 +540,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) computer.format(alter_network=False)
self.assertEqual([ self.assertEqual([
"makedirs('/instance_root', 493)", "makedirs('/instance_root', 493)",
"makedirs('/software_root', 493)", "makedirs('/software_root', 493)",
...@@ -540,15 +566,15 @@ class TestComputer(SlapformatMixin): ...@@ -540,15 +566,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.test_result, 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',
...@@ -556,7 +582,7 @@ class TestComputer(SlapformatMixin): ...@@ -556,7 +582,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)",
...@@ -581,15 +607,15 @@ class TestComputer(SlapformatMixin): ...@@ -581,15 +607,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.test_result, 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',
...@@ -597,7 +623,7 @@ class TestComputer(SlapformatMixin): ...@@ -597,7 +623,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)",
...@@ -615,6 +641,68 @@ class TestComputer(SlapformatMixin): ...@@ -615,6 +641,68 @@ 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()
os.mkdir("/tmp/slapgrid/")
os.mkdir(CGroupManagerMock.cpuset_path)
with open(os.path.join(CGroupManagerMock.cpuset_path, "cpuset.cpus"), "wt") as fo:
fo.write("0,1-3")
self.cpu_list = [0, 1, 2, 3]
with open(os.path.join(CGroupManagerMock.cpuset_path, "tasks"), "wt") as fo:
fo.writelines(("1000\n", "1001\n", "1002\n"))
self.computer = slapos.format.Computer('computer',
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, )
)
def tearDown(self):
shutil.rmtree(CGroupManagerMock.cpuset_path)
super(TestComputerWithCGroup, self).tearDown()
def test_positive_cgroups(self):
"""Positive test of cgroups."""
self.computer.format(alter_network=False, alter_user=False)
# Test parsing "cpuset.cpus" file
self.assertEqual(self.computer.manager_list[0]._cpu_list(), self.cpu_list)
# 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
file_write("1001\n",
os.path.join(self.computer.partition_list.path,
CGroupManagerMock.cpu_exclusive_file))
# 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"))))
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