summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorl0crian1 <143656816+l0crian1@users.noreply.github.com>2025-10-02 17:52:06 -0400
committerGitHub <noreply@github.com>2025-10-02 23:52:06 +0200
commit31d0ac632882e0faf8334823acc0a29147d22cc0 (patch)
treeee02ae237354310e3dfd3e51bad8615c78579c09
parent3a5947fb673aaabcd79967ce08d04b6785f9f550 (diff)
downloadvyos-1x-31d0ac632882e0faf8334823acc0a29147d22cc0.tar.gz
vyos-1x-31d0ac632882e0faf8334823acc0a29147d22cc0.zip
container: T7863: Add user-defined MAC option for containers (#4762)
-rw-r--r--interface-definitions/container.xml.in18
-rwxr-xr-xsmoketest/scripts/cli/test_container.py19
-rwxr-xr-xsrc/conf_mode/container.py33
3 files changed, 69 insertions, 1 deletions
diff --git a/interface-definitions/container.xml.in b/interface-definitions/container.xml.in
index d4efc08be..c0afde48f 100644
--- a/interface-definitions/container.xml.in
+++ b/interface-definitions/container.xml.in
@@ -312,6 +312,24 @@
<multi/>
</properties>
</leafNode>
+ <leafNode name="mac">
+ <properties>
+ <help>Media Access Control (MAC) address</help>
+ <valueHelp>
+ <format>macaddr</format>
+ <description>Hardware (MAC) address</description>
+ </valueHelp>
+ <valueHelp>
+ <format>auto</format>
+ <description>Generate a random MAC address for the container</description>
+ </valueHelp>
+ <constraint>
+ <validator name="mac-address"/>
+ <regex>(auto)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>auto</defaultValue>
+ </leafNode>
</children>
</tagNode>
<tagNode name="port">
diff --git a/smoketest/scripts/cli/test_container.py b/smoketest/scripts/cli/test_container.py
index cf12151ef..fd8272f44 100755
--- a/smoketest/scripts/cli/test_container.py
+++ b/smoketest/scripts/cli/test_container.py
@@ -224,6 +224,25 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
self.assertEqual(n['subnets'][0]['subnet'], '10.0.2.0/24')
self.assertEqual(n['subnets'][0]['gateway'], '10.0.2.1')
+ def test_user_defined_mac(self):
+ # Bridge Network
+ self.cli_set(base_path + ['network', 'bridge1', 'prefix', '10.0.1.0/24'])
+ self.cli_set(base_path + ['network', 'bridge1', 'type', 'bridge'])
+
+ self.cli_set(base_path + ['name', "test1", 'image', busybox_image])
+ self.cli_set(base_path + ['name', "test1", 'network', 'bridge1', 'address', '10.0.1.11'])
+ self.cli_set(base_path + ['name', "test1", 'network', 'bridge1', 'mac', '02:00:00:00:00:01'])
+
+ self.cli_set(base_path + ['name', "test2", 'image', busybox_image])
+ self.cli_set(base_path + ['name', "test2", 'network', 'bridge1', 'address', '10.0.1.12'])
+ self.cli_set(base_path + ['name', "test2", 'network', 'bridge1', 'mac', '02:00:00:00:00:02'])
+ self.cli_commit()
+
+ n = cmd_to_json(f'sudo podman container inspect test1')
+ self.assertEqual(n['NetworkSettings']['Networks']['bridge1']['MacAddress'], '02:00:00:00:00:01')
+ n = cmd_to_json(f'sudo podman container inspect test2')
+ self.assertEqual(n['NetworkSettings']['Networks']['bridge1']['MacAddress'], '02:00:00:00:00:02')
+
def test_ipv4_network(self):
prefix = '192.0.2.0/24'
base_name = 'ipv4'
diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py
index 4fe1e432c..b8c3d3d0c 100755
--- a/src/conf_mode/container.py
+++ b/src/conf_mode/container.py
@@ -30,6 +30,8 @@ from vyos.configdict import node_changed
from vyos.configdict import is_node_changed
from vyos.configverify import verify_vrf
from vyos.ifconfig import Interface
+from vyos.utils.configfs import delete_cli_node
+from vyos.utils.configfs import add_cli_node
from vyos.utils.cpu import get_core_count
from vyos.utils.file import write_file
from vyos.utils.dict import dict_search
@@ -117,6 +119,10 @@ def verify(container):
# Add new container
if 'name' in container:
+ net_dict = {}
+ net_dict['mac'] = {}
+ net_dict['address'] = {}
+
for name, container_config in container['name'].items():
# Container image is a mandatory option
if 'image' not in container_config:
@@ -155,6 +161,13 @@ def verify(container):
if 'name_server' in container_config and 'no_name_server' not in container['network'][network_name]:
raise ConfigError(f'Setting name server has no effect when attached container network has DNS enabled!')
+ mac = dict_search(f'network.{network_name}.mac', container_config)
+ if mac:
+ if mac in net_dict['mac'].keys():
+ raise ConfigError(f'MAC address "{mac}" is already used by container "{net_dict["mac"][mac]}"!')
+ if mac != 'auto':
+ net_dict['mac'][mac] = name
+
if 'address' in container_config['network'][network_name]:
cnt_ipv4 = 0
cnt_ipv6 = 0
@@ -181,6 +194,10 @@ def verify(container):
if ip_address(address) == ip_network(network)[1]:
raise ConfigError(f'IP address "{address}" can not be used for a container, ' \
'reserved for the container engine!')
+
+ if address in net_dict['address'].keys():
+ raise ConfigError(f'IP address "{address}" is already used by container "{net_dict["address"][address]}"!')
+ net_dict['address'][address] = name
if cnt_ipv4 > 1 or cnt_ipv6 > 1:
raise ConfigError(f'Only one IP address per address family can be used for ' \
@@ -477,6 +494,7 @@ def generate_run_arguments(name, container_config, host_ident):
addr_info = ''
networks = ",".join(container_config['network'])
for network in container_config['network']:
+ network_name = network
if 'address' not in container_config['network'][network]:
continue
for address in container_config['network'][network]['address']:
@@ -487,7 +505,20 @@ def generate_run_arguments(name, container_config, host_ident):
addr_info = ''.join(container_config['network'][network]['address'])
- mac_address = f'--mac-address {gen_mac(name, addr_info, host_ident)}'
+ get_mac = dict_search(f'network.{network_name}.mac', container_config)
+ if get_mac == 'auto' or get_mac is None:
+ mac_add = gen_mac(name, addr_info, host_ident)
+ else:
+ mac_add = get_mac
+
+ mac_address = f'--mac-address {mac_add}'
+
+ # Replace mac-auto with the generated mac address
+ if get_mac == 'auto':
+ mac_config_path = ['container', 'name', name, 'network', network_name, 'mac']
+
+ delete_cli_node(mac_config_path)
+ add_cli_node(mac_config_path, value=mac_add)
return f'{container_base_cmd} --no-healthcheck --net {networks} {ip_param} {mac_address} {entrypoint} {image} {command} {command_arguments}'.strip()