diff options
-rw-r--r-- | interface-definitions/container.xml.in | 29 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_interfaces_bridge.py | 15 | ||||
-rwxr-xr-x | src/conf_mode/container.py | 26 | ||||
-rwxr-xr-x | src/conf_mode/interfaces_bridge.py | 5 |
4 files changed, 72 insertions, 3 deletions
diff --git a/interface-definitions/container.xml.in b/interface-definitions/container.xml.in index 04318a7c9..65ac99e12 100644 --- a/interface-definitions/container.xml.in +++ b/interface-definitions/container.xml.in @@ -412,6 +412,35 @@ </constraint> </properties> </leafNode> + <tagNode name="tmpfs"> + <properties> + <help>Mount a tmpfs filesystem into the container</help> + </properties> + <children> + <leafNode name="destination"> + <properties> + <help>Destination container directory</help> + <valueHelp> + <format>txt</format> + <description>Destination container directory</description> + </valueHelp> + </properties> + </leafNode> + <leafNode name="size"> + <properties> + <help>tmpfs filesystem size in MB</help> + <valueHelp> + <format>u32:1-65536</format> + <description>tmpfs filesystem size in MB</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + <constraintErrorMessage>Container tmpfs size must be between 1 and 65535 MB</constraintErrorMessage> + </properties> + </leafNode> + </children> + </tagNode> <tagNode name="volume"> <properties> <help>Mount a volume into the container</help> diff --git a/smoketest/scripts/cli/test_interfaces_bridge.py b/smoketest/scripts/cli/test_interfaces_bridge.py index 54c981adc..4041b3ef3 100755 --- a/smoketest/scripts/cli/test_interfaces_bridge.py +++ b/smoketest/scripts/cli/test_interfaces_bridge.py @@ -158,6 +158,21 @@ class BridgeInterfaceTest(BasicInterfaceTest.TestCase): # verify member is assigned to the bridge self.assertEqual(interface, tmp['master']) + def test_bridge_multi_use_member(self): + # Define available bonding hash policies + bridges = ['br10', 'br20', 'br30'] + for interface in bridges: + for member in self._members: + self.cli_set(self._base_path + [interface, 'member', 'interface', member]) + + # check validate() - can not use the same member interfaces multiple times + with self.assertRaises(ConfigSessionError): + self.cli_commit() + # only keep the first bond interface configuration + for interface in bridges[1:]: + self.cli_delete(self._base_path + [interface]) + + self.cli_commit() def test_add_remove_bridge_member(self): # Add member interfaces to bridge and set STP cost/priority diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py index 594de3eb0..3636b0871 100755 --- a/src/conf_mode/container.py +++ b/src/conf_mode/container.py @@ -22,6 +22,7 @@ from ipaddress import ip_address from ipaddress import ip_network from json import dumps as json_write +import psutil from vyos.base import Warning from vyos.config import Config from vyos.configdict import dict_merge @@ -223,6 +224,21 @@ def verify(container): if not os.path.exists(source): raise ConfigError(f'Volume "{volume}" source path "{source}" does not exist!') + if 'tmpfs' in container_config: + for tmpfs, tmpfs_config in container_config['tmpfs'].items(): + if 'destination' not in tmpfs_config: + raise ConfigError(f'tmpfs "{tmpfs}" has no destination path configured!') + if 'size' in tmpfs_config: + free_mem_mb: int = psutil.virtual_memory().available / 1024 / 1024 + if int(tmpfs_config['size']) > free_mem_mb: + Warning(f'tmpfs "{tmpfs}" size is greater than the current free memory!') + + total_mem_mb: int = (psutil.virtual_memory().total / 1024 / 1024) / 2 + if int(tmpfs_config['size']) > total_mem_mb: + raise ConfigError(f'tmpfs "{tmpfs}" size should not be more than 50% of total system memory!') + else: + raise ConfigError(f'tmpfs "{tmpfs}" has no size configured!') + if 'port' in container_config: for tmp in container_config['port']: if not {'source', 'destination'} <= set(container_config['port'][tmp]): @@ -362,6 +378,14 @@ def generate_run_arguments(name, container_config): prop = vol_config['propagation'] volume += f' --volume {svol}:{dvol}:{mode},{prop}' + # Mount tmpfs + tmpfs = '' + if 'tmpfs' in container_config: + for tmpfs_config in container_config['tmpfs'].values(): + dest = tmpfs_config['destination'] + size = tmpfs_config['size'] + tmpfs += f' --mount=type=tmpfs,tmpfs-size={size}M,destination={dest}' + host_pid = '' if 'allow_host_pid' in container_config: host_pid = '--pid host' @@ -373,7 +397,7 @@ def generate_run_arguments(name, container_config): container_base_cmd = f'--detach --interactive --tty --replace {capabilities} --cpus {cpu_quota} {sysctl_opt} ' \ f'--memory {memory}m --shm-size {shared_memory}m --memory-swap 0 --restart {restart} ' \ - f'--name {name} {hostname} {device} {port} {name_server} {volume} {env_opt} {label} {uid} {host_pid}' + f'--name {name} {hostname} {device} {port} {name_server} {volume} {tmpfs} {env_opt} {label} {uid} {host_pid}' entrypoint = '' if 'entrypoint' in container_config: diff --git a/src/conf_mode/interfaces_bridge.py b/src/conf_mode/interfaces_bridge.py index 637db442a..aff93af2a 100755 --- a/src/conf_mode/interfaces_bridge.py +++ b/src/conf_mode/interfaces_bridge.py @@ -74,8 +74,9 @@ def get_config(config=None): for interface in list(bridge['member']['interface']): # Check if member interface is already member of another bridge tmp = is_member(conf, interface, 'bridge') - if tmp and bridge['ifname'] not in tmp: - bridge['member']['interface'][interface].update({'is_bridge_member' : tmp}) + if ifname in tmp: + del tmp[ifname] + if tmp: bridge['member']['interface'][interface].update({'is_bridge_member' : tmp}) # Check if member interface is already member of a bond tmp = is_member(conf, interface, 'bonding') |