# limitations under the License.
# Bug workaround in pylint for abstract classes.
-#pylint: disable=W0223
+# pylint: disable=W0223
"""Library to manipulate Containers."""
def construct_containers(self, **kwargs):
"""Construct 1..N container(s) on node with specified name.
+
Ordinal number is automatically added to the name of container as
suffix.
- :param kwargs: Name of container.
- :param kwargs: str
+ :param kwargs: Named parameters.
+ :param kwargs: dict
"""
name = kwargs['name']
for i in range(kwargs['count']):
dut_cnt = len(Counter([self.containers[container].node['host']
for container in self.containers]))
container_cnt = len(self.containers)
- mod = dut_cnt/container_cnt
+ mod = container_cnt/dut_cnt
for i, container in enumerate(self.containers):
+ mid1 = i % mod + 1
+ mid2 = i % mod + 1
+ sid1 = i % mod * 2 + 1
+ sid2 = i % mod * 2 + 2
self.engine.container = self.containers[container]
self.engine.create_vpp_startup_config()
- self.engine.create_vpp_exec_config(vat_template_file,
- memif_id1=i % mod * 2 + 1,
- memif_id2=i % mod * 2 + 2,
- socket1='memif-{c.name}-1'
- .format(c=self.engine.container),
- socket2='memif-{c.name}-2'
- .format(c=self.engine.container))
+ self.engine.create_vpp_exec_config(vat_template_file, mid1=mid1,
+ mid2=mid2, sid1=sid1, sid2=sid2,
+ socket1='memif-{c.name}-{sid}'
+ .format(c=self.engine.container,
+ sid=sid1),
+ socket2='memif-{c.name}-{sid}'
+ .format(c=self.engine.container,
+ sid=sid2))
def stop_all_containers(self):
"""Stop all containers."""
'do dpkg -i --force-all {0}/install_dir/$i; done'
.format(self.container.guest_dir))
self.execute('apt-get -f install -y')
+ self.execute('apt-get install -y ca-certificates')
self.execute('echo "{0}" >> {1}'
.format(
'[program:vpp]\n'
def restart_vpp(self):
"""Restart VPP service inside a container."""
self.execute('supervisorctl restart vpp')
+ self.execute('cat /tmp/supervisord.log')
def create_vpp_startup_config(self,
config_filename='/etc/vpp/startup.conf'):
# Create config instance
vpp_config = VppConfigGenerator()
vpp_config.set_node(self.container.node)
- vpp_config.set_config_filename(config_filename)
vpp_config.add_unix_cli_listen()
vpp_config.add_unix_nodaemon()
vpp_config.add_unix_exec('/tmp/running.exec')
if cpuset_cpus:
corelist_workers = ','.join(str(cpu) for cpu in cpuset_cpus)
vpp_config.add_cpu_corelist_workers(corelist_workers)
- vpp_config.add_plugin_disable('dpdk_plugin.so')
+ vpp_config.add_plugin('disable', 'dpdk_plugin.so')
self.execute('mkdir -p /etc/vpp/')
self.execute('echo "{c}" | tee {f}'
.format(c=vpp_config.get_config_str(),
- f=vpp_config.get_config_filename()))
+ f=config_filename))
- def create_vpp_exec_config(self, vat_template_file, **args):
+ def create_vpp_exec_config(self, vat_template_file, **kwargs):
"""Create VPP exec configuration on container.
:param vat_template_file: File name of a VAT template script.
- :param args: Parameters for VAT script.
+ :param kwargs: Parameters for VAT script.
:type vat_template_file: str
- :type args: dict
+ :type kwargs: dict
"""
vat_file_path = '{p}/{f}'.format(p=Constants.RESOURCES_TPL_VAT,
f=vat_template_file)
with open(vat_file_path, 'r') as template_file:
cmd_template = template_file.readlines()
for line_tmpl in cmd_template:
- vat_cmd = line_tmpl.format(**args)
+ vat_cmd = line_tmpl.format(**kwargs)
self.execute('echo "{c}" >> /tmp/running.exec'
.format(c=vat_cmd.replace('\n', '')))
"""Check if container is present."""
raise NotImplementedError
+ def _configure_cgroup(self, name):
+ """Configure the control group associated with a container.
+
+ By default the cpuset cgroup is using exclusive CPU/MEM. When Docker
+ container is initialized a new cgroup /docker or /lxc is created under
+ cpuset parent tree. This newly created cgroup is inheriting parent
+ setting for cpu/mem exclusive parameter and thus cannot be overriden
+ within /docker or /lxc cgroup. This patch is supposed to set cpu/mem
+ exclusive parameter for both parent and subgroup.
+
+ :param name: Name of cgroup.
+ :type name: str
+ :raises RuntimeError: If applying cgroup settings via cgset failed.
+ """
+ ret, _, _ = self.container.ssh.exec_command_sudo(
+ 'cgset -r cpuset.cpu_exclusive=0 /')
+ if int(ret) != 0:
+ raise RuntimeError('Failed to apply cgroup settings.')
+
+ ret, _, _ = self.container.ssh.exec_command_sudo(
+ 'cgset -r cpuset.mem_exclusive=0 /')
+ if int(ret) != 0:
+ raise RuntimeError('Failed to apply cgroup settings.')
+
+ ret, _, _ = self.container.ssh.exec_command_sudo(
+ 'cgcreate -g cpuset:/{name}'.format(name=name))
+ if int(ret) != 0:
+ raise RuntimeError('Failed to copy cgroup settings from root.')
+
+ ret, _, _ = self.container.ssh.exec_command_sudo(
+ 'cgset -r cpuset.cpu_exclusive=0 /{name}'.format(name=name))
+ if int(ret) != 0:
+ raise RuntimeError('Failed to apply cgroup settings.')
+
+ ret, _, _ = self.container.ssh.exec_command_sudo(
+ 'cgset -r cpuset.mem_exclusive=0 /{name}'.format(name=name))
+ if int(ret) != 0:
+ raise RuntimeError('Failed to apply cgroup settings.')
+
class LXC(ContainerEngine):
"""LXC implementation."""
super(LXC, self).__init__()
def acquire(self, force=True):
- """Acquire a privileged system object where configuration is stored and
- where user information can be stored.
+ """Acquire a privileged system object where configuration is stored.
:param force: If a container exists, destroy it and create a new
container.
if int(ret) != 0:
raise RuntimeError('Failed to write {c.name} config.'
.format(c=self.container))
+ self._configure_cgroup('lxc')
def create(self):
"""Create/deploy an application inside a container on system.
raise RuntimeError('Failed to start container {c.name}.'
.format(c=self.container))
self._lxc_wait('RUNNING')
- self._lxc_cgroup(state_object='cpuset.cpus',
- value=cpuset_cpus)
+
+ # Workaround for LXC to be able to allocate all cpus including isolated.
+ cmd = 'cgset --copy-from / lxc/'
+ ret, _, _ = self.container.ssh.exec_command_sudo(cmd)
+ if int(ret) != 0:
+ raise RuntimeError('Failed to copy cgroup to LXC')
+
+ cmd = 'lxc-cgroup --name {c.name} cpuset.cpus {cpus}'\
+ .format(c=self.container, cpus=cpuset_cpus)
+ ret, _, _ = self.container.ssh.exec_command_sudo(cmd)
+ if int(ret) != 0:
+ raise RuntimeError('Failed to set cpuset.cpus to container '
+ '{c.name}.'.format(c=self.container))
def execute(self, command):
- """Start a process inside a running container. Runs the specified
- command inside the container specified by name. The container has to
- be running already.
+ """Start a process inside a running container.
+
+ Runs the specified command inside the container specified by name. The
+ container has to be running already.
:param command: Command to run inside container.
:type command: str
raise RuntimeError('Failed to wait for state "{s}" of container '
'{c.name}.'.format(s=state, c=self.container))
- def _lxc_cgroup(self, state_object, value=''):
- """Manage the control group associated with a container.
-
- :param state_object: Specify the state object name.
- :param value: Specify the value to assign to the state object. If empty,
- then action is GET, otherwise is action SET.
- :type state_object: str
- :type value: str
- :raises RuntimeError: If getting/setting state of a container failed.
- """
- cmd = 'lxc-cgroup --name {c.name} {s} {v}'\
- .format(c=self.container, s=state_object, v=value)
-
- ret, _, _ = self.container.ssh.exec_command_sudo(
- 'cgset --copy-from / lxc')
- if int(ret) != 0:
- raise RuntimeError('Failed to copy cgroup settings from root.')
-
- ret, _, _ = self.container.ssh.exec_command_sudo(cmd)
- if int(ret) != 0:
- if value:
- raise RuntimeError('Failed to set {s} of container {c.name}.'
- .format(s=state_object, c=self.container))
- else:
- raise RuntimeError('Failed to get {s} of container {c.name}.'
- .format(s=state_object, c=self.container))
-
class Docker(ContainerEngine):
"""Docker implementation."""
if int(ret) != 0:
raise RuntimeError('Failed to create container {c.name}.'
.format(c=self.container))
+ self._configure_cgroup('docker')
def create(self):
"""Create/deploy container.
cpuset_mems = '--cpuset-mems={0}'.format(self.container.cpuset_mems)\
if self.container.cpuset_mems is not None else ''
+ # Temporary workaround - disabling due to bug in memif
+ cpuset_mems = ''
env = '{0}'.format(
' '.join('--env %s' % env for env in self.container.env))\
cmd = 'docker run '\
'--privileged --detach --interactive --tty --rm '\
- '--cgroup-parent lxc {cpuset_cpus} {cpuset_mems} {publish} '\
+ '--cgroup-parent docker {cpuset_cpus} {cpuset_mems} {publish} '\
'{env} {volume} --name {container.name} {container.image} '\
'{command}'.format(cpuset_cpus=cpuset_cpus, cpuset_mems=cpuset_mems,
container=self.container, command=command,
self.info()
def execute(self, command):
- """Start a process inside a running container. Runs the specified
- command inside the container specified by name. The container has to
- be running already.
+ """Start a process inside a running container.
+
+ Runs the specified command inside the container specified by name. The
+ container has to be running already.
:param command: Command to run inside container.
:type command: str
pass
def __getattr__(self, attr):
+ """Get attribute custom implementation.
+
+ :param attr: Attribute to get.
+ :type attr: str
+ :returns: Attribute value or None.
+ :rtype: any
+ """
try:
return self.__dict__[attr]
except KeyError:
return None
def __setattr__(self, attr, value):
+ """Set attribute custom implementation.
+
+ :param attr: Attribute to set.
+ :param value: Value to set.
+ :type attr: str
+ :type value: any
+ """
try:
# Check if attribute exists
self.__dict__[attr]