From 31c97abb63c7223e224c399a7d474f012ecf8a45 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 11:46:54 +0200 Subject: containers: T2216: add environmnet variable constraint An environment variable passed to podman can only consist out of alphanumeric characters, a hypend and an underscore. --- interface-definitions/containers.xml.in | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'interface-definitions/containers.xml.in') diff --git a/interface-definitions/containers.xml.in b/interface-definitions/containers.xml.in index 124b1f65e..29762c171 100644 --- a/interface-definitions/containers.xml.in +++ b/interface-definitions/containers.xml.in @@ -25,6 +25,10 @@ Add custom environment variables + + ^[-_a-zA-Z0-9]+$ + + Environment variable name must be alphanumeric and can contain hyphen and underscores -- cgit v1.2.3 From 1f6746c44c5349a35847abfb410d3826a4e7ca99 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 11:48:00 +0200 Subject: containers: T2216: xml: impove help string for address command --- interface-definitions/containers.xml.in | 2 +- src/conf_mode/containers.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'interface-definitions/containers.xml.in') diff --git a/interface-definitions/containers.xml.in b/interface-definitions/containers.xml.in index 29762c171..030980dba 100644 --- a/interface-definitions/containers.xml.in +++ b/interface-definitions/containers.xml.in @@ -60,7 +60,7 @@ Set IPv4 static address to container (optional) ipv4 - IPv4 address (x.x.x.1 reserved) + IPv4 address diff --git a/src/conf_mode/containers.py b/src/conf_mode/containers.py index b573df889..23f17ab55 100755 --- a/src/conf_mode/containers.py +++ b/src/conf_mode/containers.py @@ -124,7 +124,8 @@ def verify(container): # We can not use the first IP address of a network prefix as this is used by podman if ip_address(address) == ip_network(network)[1]: - raise ConfigError(f'Address "{address}" reserved for the container engine!') + raise ConfigError(f'IP address "{address}" can not be used for a container, '\ + 'reserved for the container engine!') if 'environment' in container_config: for var, cfg in container_config['environment'].items(): -- cgit v1.2.3 From e71a98562aabea53cb9b5e1958398e1e674490bb Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 11:48:47 +0200 Subject: containers: T2216: add CLI commands to specify restart behavior and memory usage A container is limited to 256MB memory by default and will always restart on failure. --- interface-definitions/containers.xml.in | 42 +++++++++++++++++++++++++++++++++ src/conf_mode/containers.py | 22 +++++++++++++---- 2 files changed, 59 insertions(+), 5 deletions(-) (limited to 'interface-definitions/containers.xml.in') diff --git a/interface-definitions/containers.xml.in b/interface-definitions/containers.xml.in index 030980dba..419802866 100644 --- a/interface-definitions/containers.xml.in +++ b/interface-definitions/containers.xml.in @@ -47,6 +47,24 @@ Image name in the hub-registry + + + Constrain the memory available to a container (default: 256MB) + + u32:0 + Unlimited + + + u32:1-16384 + Container memory in megabytes (MB) + + + + + Container memory must be in range 0 to 16384 MB + + 256 + Attach user defined network to container @@ -119,6 +137,30 @@ + + + Mount a volume into the container + + no on-failure always + + + no + Do not restart containers on exit + + + on-failure + Restart containers when they exit with a non-zero exit code, retrying indefinitely (default) + + + always + Restart containers when they exit, regardless of status, retrying indefinitely + + + ^(no|on-failure|always)$ + + + on-failure + Mount a volume into the container diff --git a/src/conf_mode/containers.py b/src/conf_mode/containers.py index 23f17ab55..fd0c5e52d 100755 --- a/src/conf_mode/containers.py +++ b/src/conf_mode/containers.py @@ -79,8 +79,17 @@ def get_config(config=None): # We have gathered the dict representation of the CLI, but there are default # options which we need to update into the dictionary retrived. default_values = defaults(base) + # container base default values can not be merged here - remove and add them later + if 'name' in default_values: + del default_values['name'] container = dict_merge(default_values, container) + # Merge per-container default values + if 'name' in container: + default_values = defaults(base + ['name']) + for name in container['name']: + container['name'][name] = dict_merge(default_values, container['name'][name]) + # Delete container network, delete containers tmp = node_changed(conf, ['container', 'network']) if tmp: container.update({'net_remove' : tmp}) @@ -216,6 +225,8 @@ def apply(container): # Check if the container has already been created if not container_exists(name): image = container_config['image'] + memory = container_config['memory'] + restart = container_config['restart'] # Currently the best way to run a command and immediately print stdout print(os.system(f'podman pull {image}')) @@ -242,19 +253,20 @@ def apply(container): # Bind volume volume = '' if 'volume' in container_config: - for vol in container_config['volume']: - svol = container_config['volume'][vol]['source'] - dvol = container_config['volume'][vol]['destination'] + for vol, vol_config in container_config['volume']: + svol = vol_config['source'] + dvol = vol_config['destination'] volume += f' -v {svol}:{dvol}' + container_base_cmd = f'podman run -dit --name {name} --memory {memory} --restart {restart} {port} {volume} {env_opt} {image}' if 'allow_host_networks' in container_config: - _cmd(f'podman run -dit --name {name} --net host {port} {volume} {env_opt} {image}') + _cmd(f'{container_base_cmd} --net host') else: for network in container_config['network']: ipparam = '' if 'address' in container_config['network'][network]: ipparam = '--ip ' + container_config['network'][network]['address'] - _cmd(f'podman run --name {name} -dit --net {network} {ipparam} {port} {volume} {env_opt} {image}') + _cmd(f'{container_base_cmd} --net {network} {ipparam}') # Else container is already created. Just start it. # It's needed after reboot. -- cgit v1.2.3 From a663816d5add31ffea849839c7dccc2029d64201 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 15:18:12 +0200 Subject: container: T2216: name of container must be alphanumeric and can contain a hyphen --- interface-definitions/containers.xml.in | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'interface-definitions/containers.xml.in') diff --git a/interface-definitions/containers.xml.in b/interface-definitions/containers.xml.in index 419802866..39ef945a3 100644 --- a/interface-definitions/containers.xml.in +++ b/interface-definitions/containers.xml.in @@ -9,6 +9,10 @@ Container name + + ^[-a-zA-Z0-9]+$ + + Container name must be alphanumeric and can contain hyphens -- cgit v1.2.3 From c48566ead61cb7c7c0571167c75d78b7fb6aa759 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 15:58:43 +0200 Subject: container: T2216: increase default memory limit to 512MB --- interface-definitions/containers.xml.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'interface-definitions/containers.xml.in') diff --git a/interface-definitions/containers.xml.in b/interface-definitions/containers.xml.in index 39ef945a3..9c366b5f4 100644 --- a/interface-definitions/containers.xml.in +++ b/interface-definitions/containers.xml.in @@ -53,7 +53,7 @@ - Constrain the memory available to a container (default: 256MB) + Constrain the memory available to a container (default: 512MB) u32:0 Unlimited @@ -67,7 +67,7 @@ Container memory must be in range 0 to 16384 MB - 256 + 512 -- cgit v1.2.3 From f480f236d8bd43bbfc5928702a94d6b3684cbb68 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 16:57:16 +0200 Subject: container: T2216: use common "generic-description.xml.i" building block --- interface-definitions/containers.xml.in | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'interface-definitions/containers.xml.in') diff --git a/interface-definitions/containers.xml.in b/interface-definitions/containers.xml.in index 9c366b5f4..286b4942a 100644 --- a/interface-definitions/containers.xml.in +++ b/interface-definitions/containers.xml.in @@ -21,11 +21,7 @@ - - - Container description - - + #include Add custom environment variables -- cgit v1.2.3 From 7762e74d928e9f23360798a21c8dd970579365b6 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 23 Aug 2021 16:57:38 +0200 Subject: container: T2216: add option to "disable" a container --- interface-definitions/containers.xml.in | 1 + src/conf_mode/containers.py | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'interface-definitions/containers.xml.in') diff --git a/interface-definitions/containers.xml.in b/interface-definitions/containers.xml.in index 286b4942a..d990e41a3 100644 --- a/interface-definitions/containers.xml.in +++ b/interface-definitions/containers.xml.in @@ -22,6 +22,7 @@ #include + #include Add custom environment variables diff --git a/src/conf_mode/containers.py b/src/conf_mode/containers.py index 97e84a5bb..5b863fa03 100755 --- a/src/conf_mode/containers.py +++ b/src/conf_mode/containers.py @@ -214,8 +214,15 @@ def apply(container): # Add container if 'name' in container: for name, container_config in container['name'].items(): - # Check if the container has already been created image = container_config['image'] + + if 'disable' in container_config: + # check if there is a container by that name running + tmp = _cmd('podman ps -a --format "{{.Names}}"') + if name in tmp: + _cmd(f'podman stop {name}') + continue + memory = container_config['memory'] restart = container_config['restart'] -- cgit v1.2.3 From ae2dc55aa68679e828d4bb133fc515172c081d0f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 18 Sep 2021 22:12:09 +0200 Subject: container: T2216: add IPv6 support to container networks --- interface-definitions/containers.xml.in | 8 +++- src/conf_mode/containers.py | 72 ++++++++++++++++++--------------- 2 files changed, 46 insertions(+), 34 deletions(-) (limited to 'interface-definitions/containers.xml.in') diff --git a/interface-definitions/containers.xml.in b/interface-definitions/containers.xml.in index d990e41a3..bf672307c 100644 --- a/interface-definitions/containers.xml.in +++ b/interface-definitions/containers.xml.in @@ -76,7 +76,8 @@ - Set IPv4 static address to container (optional) + + Assign static IP address to container ipv4 IPv4 address @@ -206,8 +207,13 @@ ipv4net IPv4 network prefix + + ipv6net + IPv6 network prefix + + diff --git a/src/conf_mode/containers.py b/src/conf_mode/containers.py index 3a93bf062..1e0197a13 100755 --- a/src/conf_mode/containers.py +++ b/src/conf_mode/containers.py @@ -20,6 +20,7 @@ import json from ipaddress import ip_address from ipaddress import ip_network from time import sleep +from json import dumps as json_write from vyos.config import Config from vyos.configdict import dict_merge @@ -31,10 +32,10 @@ from vyos.util import read_file from vyos.util import write_file from vyos.util import is_systemd_service_active from vyos.util import is_systemd_service_running - -from vyos.template import render +from vyos.template import inc_ip from vyos.template import is_ipv4 from vyos.template import is_ipv6 +from vyos.template import render from vyos.xml import defaults from vyos import ConfigError from vyos import airbag @@ -185,6 +186,37 @@ def generate(container): if not container: return None + if 'network' in container: + for network, network_config in container['network'].items(): + tmp = { + 'cniVersion' : '0.4.0', + 'name' : network, + 'plugins' : [{ + 'type': 'bridge', + 'bridge': f'cni-{network}', + 'isGateway': True, + 'ipMasq': False, + 'hairpinMode': False, + 'ipam' : { + 'type': 'host-local', + 'routes': [], + 'ranges' : [], + }, + }] + } + + for prefix in network_config['prefix']: + net = [{'gateway' : inc_ip(prefix, 1), 'subnet' : prefix}] + tmp['plugins'][0]['ipam']['ranges'].append(net) + + # install per address-family default orutes + default_route = '0.0.0.0/0' + if is_ipv6(prefix): + default_route = '::/0' + tmp['plugins'][0]['ipam']['routes'].append({'dst': default_route}) + + write_file(f'/etc/cni/net.d/{network}.conflist', json_write(tmp, indent=2)) + render(config_containers_registry, 'containers/registry.tmpl', container) render(config_containers_storage, 'containers/storage.tmpl', container) @@ -201,7 +233,9 @@ def apply(container): # Delete old networks if needed if 'network_remove' in container: for network in container['network_remove']: - call(f'podman network rm --force {network}') + tmp = f'/etc/cni/net.d/{network}.conflist' + if os.path.exists(tmp): + os.unlink(tmp) service_name = 'podman.service' if 'network' in container or 'name' in container: @@ -214,35 +248,6 @@ def apply(container): else: _cmd(f'systemctl stop {service_name}') - - # Add network - if 'network' in container: - for network, network_config in container['network'].items(): - # Check if the network has already been created - if not network_exists(network) and 'prefix' in network_config: - tmp = f'podman network create {network}' - # we can not use list comprehension here as the --ipv6 option - # must immediately follow the specified subnet!!! - for prefix in sorted(network_config['prefix']): - tmp += f' --subnet={prefix}' - if is_ipv6(prefix): - tmp += ' --ipv6' - _cmd(tmp) - - # Disable masquerading and use traditional bridging so VyOS - # can control firewalling/NAT by the real VyOS CLI - cni_network_config = f'/etc/cni/net.d/{network}.conflist' - tmp = read_file(cni_network_config) - config = json.loads(tmp) - if 'plugins' in config: - for count in range(0, len(config['plugins'])): - if 'ipMasq' in config['plugins'][count]: - config['plugins'][count]['ipMasq'] = False - if 'hairpinMode' in config['plugins'][count]: - config['plugins'][count]['hairpinMode'] = False - - write_file(cni_network_config, json.dumps(config, indent=4)) - # Add container if 'name' in container: for name, container_config in container['name'].items(): @@ -303,7 +308,8 @@ def apply(container): for network in container_config['network']: ipparam = '' if 'address' in container_config['network'][network]: - ipparam = '--ip ' + container_config['network'][network]['address'] + address = container_config['network'][network]['address'] + ipparam = f'--ip {address}' _cmd(f'{container_base_cmd} --net {network} {ipparam} {image}') return None -- cgit v1.2.3 From 15d4977f5d52d3fc5f9c2fa501964739f2335741 Mon Sep 17 00:00:00 2001 From: Volodymyr Date: Tue, 5 Oct 2021 12:08:24 +0000 Subject: container: T3881: Fix description for container --- interface-definitions/containers.xml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'interface-definitions/containers.xml.in') diff --git a/interface-definitions/containers.xml.in b/interface-definitions/containers.xml.in index bf672307c..fb8241d71 100644 --- a/interface-definitions/containers.xml.in +++ b/interface-definitions/containers.xml.in @@ -141,7 +141,7 @@ - Mount a volume into the container + Restart options for container no on-failure always -- cgit v1.2.3