diff options
| author | Viacheslav Hletenko <v.gletenko@vyos.io> | 2025-11-04 17:53:11 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-11-04 17:53:11 +0200 |
| commit | cc60275ab0db2c45f2ad7387d6a91a96fa9d6337 (patch) | |
| tree | a59baa904529193481c12fb112a2de29b9d08608 | |
| parent | 6d0324506a5b0419eb0ae001d8d006a414030405 (diff) | |
| parent | ae6d3437dc3247c396444b7f65eb49a98b0dd800 (diff) | |
| download | vyos-1x-cc60275ab0db2c45f2ad7387d6a91a96fa9d6337.tar.gz vyos-1x-cc60275ab0db2c45f2ad7387d6a91a96fa9d6337.zip | |
Merge pull request #4828 from c-po/restart-container-vrf
container: T7305: fix VRF loss when restarting pods
| -rw-r--r-- | python/vyos/container.py | 41 | ||||
| -rw-r--r-- | python/vyos/ifconfig/interface.py | 2 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_container.py | 27 | ||||
| -rwxr-xr-x | src/conf_mode/container.py | 25 | ||||
| -rwxr-xr-x | src/op_mode/container.py | 9 |
5 files changed, 82 insertions, 22 deletions
diff --git a/python/vyos/container.py b/python/vyos/container.py new file mode 100644 index 000000000..475d796f2 --- /dev/null +++ b/python/vyos/container.py @@ -0,0 +1,41 @@ +# Copyright VyOS maintainers and contributors <maintainers@vyos.io> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from vyos.config import Config +from vyos.ifconfig import Interface +from vyos.utils.dict import dict_search +from vyos.utils.network import interface_exists + +def restart_network(config: Config) -> None: + """ + Start network and assign it to given VRF if requested. + + This can only be done after the containers got started as the podman network + interface will only be enabled by the first container and yet I do not know + how to enable the network interface in advance. + """ + if 'network' in config: + for network, network_config in config['network'].items(): + type_config = dict_search('type', network_config) + if not dict_search('macvlan', type_config): + network_name = f'pod-{network}' + # T5147: Networks are started only as soon as there is a consumer. + # If only a network is created in the first place, no need to assign + # it to a VRF as there's no consumer, yet. + if interface_exists(network_name): + tmp = Interface(network_name) + tmp.set_vrf(network_config.get('vrf', '')) + tmp.add_ipv6_eui64_address('fe80::/64') + + return None diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 761ba1799..ad634c4d7 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -619,7 +619,7 @@ class Interface(Control): if 'netns' in self.config: return False - tmp = self.get_interface('vrf') + tmp = self.get_vrf() if tmp == vrf: return False diff --git a/smoketest/scripts/cli/test_container.py b/smoketest/scripts/cli/test_container.py index b41a7dd53..ae85273e8 100755 --- a/smoketest/scripts/cli/test_container.py +++ b/smoketest/scripts/cli/test_container.py @@ -23,6 +23,7 @@ from base_vyostest_shim import VyOSUnitTestSHIM from ipaddress import ip_interface from vyos.configsession import ConfigSessionError +from vyos.utils.network import get_interface_vrf from vyos.utils.process import cmd from vyos.utils.process import process_named_running @@ -51,6 +52,7 @@ class TestContainer(VyOSUnitTestSHIM.TestCase): # ensure we can also run this test on a live system - so lets clean # out the current configuration :) cls.cli_delete(cls, base_path) + cls.cli_delete(cls, ['vrf']) @classmethod def tearDownClass(cls): @@ -133,7 +135,6 @@ class TestContainer(VyOSUnitTestSHIM.TestCase): self.assertEqual(l['Config']['Healthcheck']['Timeout'], 1000000000) self.assertEqual(l['Config']['Healthcheck']['Retries'], 2) - def test_name_server(self): cont_name = 'dns-test' net_name = 'net-test' @@ -517,6 +518,30 @@ class TestContainer(VyOSUnitTestSHIM.TestCase): # We expect the same amount of containers from the API that we started above self.assertEqual(len(container_list), len(tmp)) + def test_network_vrf(self): + cont_name = 'vrf-test50' + net_name = 'vrf-test50' + vrf_name = 'red-15' + + # create temporary VRF for testing + self.cli_set(['vrf', 'name', vrf_name, 'table', '100']) + + self.cli_set(base_path + ['name', cont_name, 'image', busybox_image]) + self.cli_set(base_path + ['name', cont_name, 'network', net_name]) + self.cli_set(base_path + ['network', net_name, 'prefix', '192.168.0.0/24']) + self.cli_set(base_path + ['network', net_name, 'vrf', vrf_name]) + + self.cli_commit() + + tmp = get_interface_vrf(f'pod-{net_name}') + self.assertEqual(tmp, vrf_name) + + # Restart container and validate VRF assignment + self.op_mode(['restart', 'container', cont_name]) + tmp = get_interface_vrf(f'pod-{net_name}') + self.assertEqual(tmp, vrf_name) + + self.cli_delete(['vrf', 'name', vrf_name]) if __name__ == '__main__': unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on()) diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py index a57a9d054..af09ac52a 100755 --- a/src/conf_mode/container.py +++ b/src/conf_mode/container.py @@ -29,7 +29,7 @@ from vyos.configdict import dict_merge 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.container import restart_network from vyos.utils.configfs import delete_cli_node from vyos.utils.configfs import add_cli_node from vyos.utils.cpu import get_core_count @@ -161,7 +161,7 @@ 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) + 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]}"!') @@ -194,7 +194,7 @@ 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 @@ -673,23 +673,8 @@ def apply(container): if disabled_new: call('systemctl daemon-reload') - # Start network and assign it to given VRF if requested. this can only be done - # after the containers got started as the podman network interface will - # only be enabled by the first container and yet I do not know how to enable - # the network interface in advance - if 'network' in container: - for network, network_config in container['network'].items(): - type_config = dict_search('type', network_config) - if not dict_search('macvlan', type_config): - network_name = f'pod-{network}' - # T5147: Networks are started only as soon as there is a consumer. - # If only a network is created in the first place, no need to assign - # it to a VRF as there's no consumer, yet. - if interface_exists(network_name): - tmp = Interface(network_name) - tmp.set_vrf(network_config.get('vrf', '')) - tmp.add_ipv6_eui64_address('fe80::/64') - + # Re-Start network and assign it to given VRF if requested. + restart_network(container) return None diff --git a/src/op_mode/container.py b/src/op_mode/container.py index a34901cb4..281c95e2c 100755 --- a/src/op_mode/container.py +++ b/src/op_mode/container.py @@ -166,6 +166,8 @@ def show_network(raw: bool): def restart(name: str): from vyos.utils.process import rc_cmd + from vyos.config import Config + from vyos.container import restart_network rc, output = rc_cmd(f'systemctl restart vyos-container-{name}.service') if rc != 0: @@ -173,6 +175,13 @@ def restart(name: str): if rc2 != 0: print(output) return None + if rc == 0: + conf = Config() + container = conf.get_config_dict(['container'], key_mangling=('-', '_'), + no_tag_node_value_mangle=True, + get_first_key=True, + with_recursive_defaults=True) + restart_network(container) print(f'Container "{name}" restarted!') return output |
