diff options
| author | sarthurdev <965089+sarthurdev@users.noreply.github.com> | 2025-06-27 00:22:39 +0200 |
|---|---|---|
| committer | sarthurdev <965089+sarthurdev@users.noreply.github.com> | 2026-08-28 13:47:52 +0200 |
| commit | 3c5c0cc104f2a01f752b6025e30faad051d1b4ce (patch) | |
| tree | 74e10ac977d383192136f54078f19a9ab2a656f7 | |
| parent | dbbec293b69a2d75292c2ac55c55402d7a29e3c4 (diff) | |
| download | vyos-1x-3c5c0cc104f2a01f752b6025e30faad051d1b4ce.tar.gz vyos-1x-3c5c0cc104f2a01f752b6025e30faad051d1b4ce.zip | |
podman: T9129: Use systemd quadlet for containers and networks
| -rw-r--r-- | data/templates/container/quadlet-network.j2 | 8 | ||||
| -rw-r--r-- | data/templates/container/quadlet-unit.j2 | 11 | ||||
| -rw-r--r-- | data/templates/container/systemd-unit.j2 | 17 | ||||
| -rw-r--r-- | smoketest/scripts/cli/test_container.py | 39 | ||||
| -rw-r--r-- | src/conf_mode/container.py | 432 |
5 files changed, 229 insertions, 278 deletions
diff --git a/data/templates/container/quadlet-network.j2 b/data/templates/container/quadlet-network.j2 new file mode 100644 index 000000000..f253be557 --- /dev/null +++ b/data/templates/container/quadlet-network.j2 @@ -0,0 +1,8 @@ +### Autogenerated by container.py ### +[Unit] +Description=VyOS Network {{ name }} + +[Network] +{% for opt in opts %} +{{ opt }} +{% endfor %} diff --git a/data/templates/container/quadlet-unit.j2 b/data/templates/container/quadlet-unit.j2 new file mode 100644 index 000000000..7546e3f1f --- /dev/null +++ b/data/templates/container/quadlet-unit.j2 @@ -0,0 +1,11 @@ +### Autogenerated by container.py ### +[Unit] +Description=VyOS Container {{ name }} + +[Container] +{% for opt in opts %} +{{ opt }} +{% endfor %} + +[Service] +Restart={{ restart }} diff --git a/data/templates/container/systemd-unit.j2 b/data/templates/container/systemd-unit.j2 deleted file mode 100644 index d379f0a07..000000000 --- a/data/templates/container/systemd-unit.j2 +++ /dev/null @@ -1,17 +0,0 @@ -### Autogenerated by container.py ### -[Unit] -Description=VyOS Container {{ name }} - -[Service] -Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=on-failure -ExecStartPre=/bin/rm -f %t/%n.pid %t/%n.cid -ExecStart=/usr/bin/podman run \ - --conmon-pidfile %t/%n.pid --cidfile %t/%n.cid --cgroups=no-conmon \ - {{ run_args }} -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/%n.cid -t 5 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/%n.cid -ExecStopPost=/bin/rm -f %t/%n.cid -PIDFile=%t/%n.pid -KillMode=control-group -Type=forking diff --git a/smoketest/scripts/cli/test_container.py b/smoketest/scripts/cli/test_container.py index 2ad9f4c99..95acfd23b 100644 --- a/smoketest/scripts/cli/test_container.py +++ b/smoketest/scripts/cli/test_container.py @@ -29,7 +29,6 @@ from vyos.utils.process import process_named_running base_path = ['container'] PROCESS_NAME = 'conmon' -PROCESS_PIDFILE = '/run/vyos-container-{0}.service.pid' busybox_image = 'busybox:stable' busybox_image_path = '/usr/share/vyos/busybox-stable.tar' @@ -68,11 +67,15 @@ class TestContainer(VyOSUnitTestSHIM.TestCase): self.assertIsNone(process_named_running(PROCESS_NAME)) # Ensure systemd units are removed - units = glob.glob('/run/systemd/system/vyos-container-*') + units = glob.glob('/run/containers/systemd/vyos*') self.assertEqual(units, []) # always forward to base class super().tearDown() + def is_running(self, name): + command = ['systemctl', 'show', f'vyos-container-{name}', '--property=ActiveState', '--value'] + return cmdl(command).strip() == 'active' + def test_basic(self): cont_name = 'c1' @@ -91,12 +94,7 @@ class TestContainer(VyOSUnitTestSHIM.TestCase): # commit changes self.cli_commit() - pid = 0 - with open(PROCESS_PIDFILE.format(cont_name), 'r') as f: - pid = int(f.read()) - - # Check for running process - self.assertEqual(process_named_running(PROCESS_NAME), pid) + self.assertTrue(self.is_running(cont_name)) # verify tmp = cmdl(['podman', 'exec', '-it', cont_name, 'sysctl', 'kernel.msgmax'], sudo=True) @@ -107,6 +105,11 @@ class TestContainer(VyOSUnitTestSHIM.TestCase): self.assertEqual(l['Config']['Healthcheck']['Test'], ['NONE']) self.assertEqual(l['HostConfig']['CgroupMode'], 'host') + # cleanup + self.cli_delete(['interfaces', 'ethernet', 'eth0', 'address']) + self.cli_delete(['protocols', 'static']) + self.cli_delete(['system', 'name-server']) + def test_healthcheck(self): cont_name = 'health-test' @@ -171,12 +174,7 @@ class TestContainer(VyOSUnitTestSHIM.TestCase): self.cli_commit() - pid = 0 - with open(PROCESS_PIDFILE.format(cont_name), 'r') as f: - pid = int(f.read()) - - # Check for running process - self.assertEqual(process_named_running(PROCESS_NAME), pid) + self.assertTrue(self.is_running(cont_name)) def test_network_types(self): self.cli_set(['interfaces', 'ethernet', 'eth0', 'vif', '100']) @@ -203,6 +201,11 @@ class TestContainer(VyOSUnitTestSHIM.TestCase): self.cli_commit() + # Force-start the network quadlets, as not mapped to containers + for network in ['macvlan1', 'macvlan2', 'macvlan3', 'bridge1', 'bridge2']: + systemd_unit = f'vyos-{network}-network.service' + cmdl(['systemctl', 'start', systemd_unit], sudo=True) + n = cmd_to_json(['network', 'inspect', 'macvlan1']) self.assertEqual(n['driver'], 'macvlan') self.assertEqual(n['network_interface'], 'eth0') @@ -236,6 +239,9 @@ 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') + # Cleanup + self.cli_delete(['interfaces', 'ethernet', 'eth0', 'vif']) + def test_user_defined_mac(self): # Bridge Network self.cli_set(base_path + ['network', 'bridge1', 'prefix', '10.0.1.0/24']) @@ -277,10 +283,7 @@ class TestContainer(VyOSUnitTestSHIM.TestCase): # process name alone can't distinguish which container it belongs # to - verify each container's own recorded PID is still alive for name in (name_1, name_2): - pid = 0 - with open(PROCESS_PIDFILE.format(name)) as f: - pid = int(f.read()) - self.assertTrue(os.path.exists(f'/proc/{pid}')) + self.assertTrue(self.is_running(name)) def test_colliding_host_interface_names(self): # T7736: the host-side veth name is "veth-<name[:5]>-<hash[:4]>" for diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py index 764c26d84..5a6ac216a 100644 --- a/src/conf_mode/container.py +++ b/src/conf_mode/container.py @@ -17,10 +17,8 @@ import os from decimal import Decimal -from hashlib import sha256 from ipaddress import ip_address from ipaddress import ip_network -from json import dumps as json_write import psutil from vyos.base import Warning @@ -34,9 +32,7 @@ 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 -from vyos.utils.file import write_file from vyos.utils.dict import dict_search -from vyos.utils.process import call from vyos.utils.process import cmdl from vyos.utils.process import run from vyos.utils.network import gen_mac @@ -56,7 +52,7 @@ airbag.enable() config_containers = '/etc/containers/containers.conf' config_registry = '/etc/containers/registries.conf' config_storage = '/etc/containers/storage.conf' -systemd_unit_path = '/run/systemd/system' +quadlet_unit_path = '/run/containers/systemd' def _cmdl(command): @@ -375,212 +371,227 @@ def verify(container): return None +def generate_network_options(name, network_config): + out = [ + f'NetworkName={name}', + 'Internal=false', + 'IPAMDriver=host-local', + ] -def generate_run_arguments(name, container_config, host_ident, network_config): - image = container_config['image'] - cpu_quota = container_config['cpu_quota'] - memory = container_config['memory'] - shared_memory = container_config['shared_memory'] - restart = container_config['restart'] - log_driver = container_config['log_driver'] + ifname = f'pod-{name}' + driver = 'bridge' - # Add sysctl options - sysctl_opt = '' - if 'sysctl' in container_config and 'parameter' in container_config['sysctl']: - for k, v in container_config['sysctl']['parameter'].items(): - sysctl_opt += f" --sysctl \"{k}={v['value']}\"" + type_config = dict_search('type', network_config) - # Add capability options. Should be in uppercase - capabilities = '' - if 'capability' in container_config: - for cap in container_config['capability']: - cap = cap.upper().replace('-', '_') - capabilities += f' --cap-add={cap}' + if dict_search('macvlan', type_config): + ifname = dict_search('macvlan.parent', type_config) + driver = 'macvlan' + macvlan_mode = dict_search('macvlan.mode', type_config) + out.append(f'Options=mode={macvlan_mode}') - # Grant root capabilities to the container - privileged = '' - if 'privileged' in container_config: - privileged = '--privileged' + out.append(f'Driver={driver}') + out.append(f'PodmanArgs=--interface-name={ifname}') - # Add a host device to the container /dev/x:/dev/x - device = '' - if 'device' in container_config: - for dev, dev_config in container_config['device'].items(): - source_dev = dev_config['source'] - dest_dev = dev_config['destination'] - device += f' --device={source_dev}:{dest_dev}' + if 'no_name_server' in network_config: + out.append('DisableDNS=true') - # Check/set environment options "-e foo=bar" - env_opt = '' - if 'environment' in container_config: - for k, v in container_config['environment'].items(): - env_opt += f" --env \"{k}={v['value']}\"" + mtu = network_config['mtu'] if 'mtu' in network_config else '1500' + out.append(f'Options=mtu={mtu}') - # Check/set label options "--label foo=bar" - label = '' - if 'label' in container_config: - for k, v in container_config['label'].items(): - label += f" --label \"{k}={v['value']}\"" + ipv6 = False - hostname = '' - if 'host_name' in container_config: - hostname = container_config['host_name'] - hostname = f'--hostname {hostname}' + for prefix in network_config['prefix']: + v6 = is_ipv6(prefix) + gateway4, gateway6 = None, None + if 'gateway' in network_config: + for gw in network_config['gateway']: + if is_ipv6(gw): + gateway6 = gw + else: + gateway4 = gw - # Publish ports - port = '' - if 'port' in container_config: - protocol = '' - for portmap in container_config['port']: - protocol = container_config['port'][portmap]['protocol'] - sport = container_config['port'][portmap]['source'] - dport = container_config['port'][portmap]['destination'] - listen_addresses = container_config['port'][portmap].get('listen_address', []) + if v6 and not gateway6: + gateway6 = inc_ip(prefix, 1) + elif not gateway4: + gateway4 = inc_ip(prefix, 1) - # If listen_addresses is not empty, include them in the publish command - if listen_addresses: - for listen_address in listen_addresses: - port += f' --publish {bracketize_ipv6(listen_address)}:{sport}:{dport}/{protocol}' - else: - # If listen_addresses is empty, just include the standard publish command - port += f' --publish {sport}:{dport}/{protocol}' + out.append(f'Subnet={prefix}') + out.append('Gateway=' + (gateway6 if v6 else gateway4)) - # Set uid and gid - uid = '' - if 'uid' in container_config: - uid = container_config['uid'] - if 'gid' in container_config: - uid += ':' + container_config['gid'] - uid = f'--user {uid}' + if v6: + ipv6 = True - # Bind volume - volume = '' - if 'volume' in container_config: - for vol, vol_config in container_config['volume'].items(): - svol = vol_config['source'] - dvol = vol_config['destination'] - mode = vol_config['mode'] - prop = vol_config['propagation'] - volume += f' --volume {svol}:{dvol}:{mode},{prop}' + if ipv6: + out.append('IPv6=true') - # 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}' + return out - host_pid = '' - if 'allow_host_pid' in container_config: - host_pid = '--pid host' +def generate_quadlet_options(name, container_config, host_ident, network_config): + out = [ + f'ContainerName={name}', + f'Image={container_config["image"]}', + f'LogDriver={container_config["log_driver"]}', + f'PodmanArgs=--memory={container_config["memory"]}m', + f'ShmSize={container_config["shared_memory"]}m', + f'PodmanArgs=--cpus={container_config["cpu_quota"]}', + 'PodmanArgs=--interactive', + 'PodmanArgs=--tty', + ] - cgroupns = '' if 'allow_host_cgroups' in container_config: - cgroupns = '--cgroupns host' + out.append('PodmanArgs=--cgroupns host') - name_server = [] - if 'name_server' in container_config: - for ns in container_config['name_server']: - name_server.append(f'--dns {ns}') - if name_server: - name_server = ' '.join(name_server) - else: - name_server = '' + if 'allow_host_networks' in container_config: + out.append('Network=host') + + if 'allow_host_pid' in container_config: + out.append('PodmanArgs=--pid host') - container_base_cmd = f'--detach --interactive --tty --replace {capabilities} {privileged} --cpus {cpu_quota} {sysctl_opt} ' \ - f'--memory {memory}m --shm-size {shared_memory}m --memory-swap 0 --restart {restart} --log-driver={log_driver} ' \ - f'--name {name} {hostname} {device} {port} {name_server} {volume} {tmpfs} {env_opt} {label} {uid} {host_pid} {cgroupns}' + if 'capability' in container_config: + for cap in container_config['capability']: + cap = cap.upper().replace('-', '_') + out.append(f'AddCapability={cap}') + + if 'command' in container_config: + command = container_config['command'].strip() + + if 'arguments' in container_config: + command += ' ' + container_config['arguments'].strip() + + out.append(f'Exec={command}') + + if 'device' in container_config: + for dev, dev_config in container_config['device'].items(): + source_dev = dev_config['source'] + dest_dev = dev_config['destination'] + out.append(f'AddDevice={source_dev}:{dest_dev}') - entrypoint = '' if 'entrypoint' in container_config: - # it needs to be json-formatted with single quote on the outside - entrypoint = json_write(container_config['entrypoint'].split()).replace('"', """) - entrypoint = f'--entrypoint '{entrypoint}'' + entrypoint = container_config['entrypoint'] + out.append(f'Entrypoint={entrypoint}') + + if 'environment' in container_config: + for k, v in container_config['environment'].items(): + out.append(f'Environment={k}={v["value"]}') - healthcheck = ' --no-healthcheck' if 'health_check' in container_config: - healthcheck = '' if 'command' in container_config['health_check']: health_cmd = container_config['health_check']['command'] - healthcheck += f' --health-cmd="{health_cmd}"' + out.append(f'HealthCmd={health_cmd}') if 'interval' in container_config['health_check']: health_int = container_config['health_check']['interval'] if health_int != 'disable': health_int = f'{health_int}s' - healthcheck += f' --health-interval={health_int}' + out.append(f'HealthInterval={health_int}') if 'timeout' in container_config['health_check']: health_to = container_config['health_check']['timeout'] - healthcheck += f' --health-timeout={health_to}s' + out.append(f'HealthTimeout={health_to}s') if 'retry' in container_config['health_check']: health_rt = container_config['health_check']['retry'] - healthcheck += f' --health-retries={health_rt}' + out.append(f'HealthRetries={health_rt}') + else: + out.append('PodmanArgs=--no-healthcheck') - command = '' - if 'command' in container_config: - command = container_config['command'].strip() + if 'host_name' in container_config: + hostname = container_config['host_name'] + out.append(f'HostName={hostname}') - command_arguments = '' - if 'arguments' in container_config: - command_arguments = container_config['arguments'].strip() + if 'label' in container_config: + for k, v in container_config['label'].items(): + out.append(f'Label={k}={v["value"]}') - net = '' - if 'allow_host_networks' in container_config: - net = '--net host' - else: - ip_param = '' - addr_info = '' - network_opts = [] + if 'name_server' in container_config: + for ns in container_config['name_server']: + out.append(f'DNS={ns}') + + if 'network' in container_config: for network in container_config['network']: - network_name = network + addr_info = '' + network_opts = [] + + if 'address' in container_config['network'][network]: + addr_info = ''.join(container_config['network'][network]['address']) + for address in container_config['network'][network]['address']: + prefix = 'ip' if is_ipv4(address) else 'ip6' + network_opts.append(f'{prefix}={address}') + + get_mac = dict_search(f'network.{network}.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 + + network_opts.append(f'mac={mac_add}') + # T7736: give the host-side veth a name that can never collide # with a VyOS "virtual-ethernet vethN" interface. type_config = dict_search(f'{network}.type', network_config) is_macvlan = dict_search('macvlan', type_config) is not None - net_opt = network if not is_macvlan: ifname = get_container_host_ifname(name) - net_opt += f':host_interface_name={ifname}' - network_opts.append(net_opt) + network_opts.append(f'host_interface_name={ifname}') - if 'address' not in container_config['network'][network]: - continue - for address in container_config['network'][network]['address']: - if is_ipv6(address): - ip_param += f' --ip6 {address}' - else: - ip_param += f' --ip {address}' + opts_str = (':' + ','.join(network_opts)) if network_opts else '' + out.append(f'Network=vyos-{network}.network{opts_str}') + + # Replace mac-auto with the generated mac address + if get_mac == 'auto': + mac_config_path = [ + 'container', + 'name', + name, + 'network', + network, + 'mac', + ] - addr_info = ''.join(container_config['network'][network]['address']) + delete_cli_node(mac_config_path) + add_cli_node(mac_config_path, value=mac_add) - networks = ' '.join(f'--network {opt}' for opt in network_opts) + if 'port' in container_config: + protocol = '' + for portmap in container_config['port']: + protocol = container_config['port'][portmap]['protocol'] + sport = container_config['port'][portmap]['source'] + dport = container_config['port'][portmap]['destination'] + listen_addresses = container_config['port'][portmap].get('listen_address', []) - 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 + # If listen_addresses is not empty, include them in the publish command + if listen_addresses: + for listen_address in listen_addresses: + out.append(f'PublishPort={bracketize_ipv6(listen_address)}:{sport}:{dport}/{protocol}') + else: + # If listen_addresses is empty, just include the standard publish command + out.append(f'PublishPort={sport}:{dport}/{protocol}') - mac_address = f'--mac-address {mac_add}' + if 'privileged' in container_config: + out.append('PodmanArgs=--privileged') - # Replace mac-auto with the generated mac address - if get_mac == 'auto': - mac_config_path = [ - 'container', - 'name', - name, - 'network', - network_name, - 'mac', - ] + if 'sysctl' in container_config and 'parameter' in container_config['sysctl']: + for k, v in container_config['sysctl']['parameter'].items(): + out.append(f'Sysctl={k}={v["value"]}') - delete_cli_node(mac_config_path) - add_cli_node(mac_config_path, value=mac_add) + if 'tmpfs' in container_config: + for tmpfs_config in container_config['tmpfs'].values(): + dest = tmpfs_config['destination'] + size = tmpfs_config['size'] + out.append(f'Mount=type=tmpfs,tmpfs-size={size}M,destination={dest}') - net = f'{networks} {ip_param} {mac_address}' + if 'uid' in container_config: + uid = container_config['uid'] + if 'gid' in container_config: + uid += ':' + container_config['gid'] + out.append(f'User={uid}') - return f'{container_base_cmd} {healthcheck} {net} {entrypoint} {image} {command} {command_arguments}'.strip() + if 'volume' in container_config: + for _, vol_config in container_config['volume'].items(): + svol = vol_config['source'] + dvol = vol_config['destination'] + mode = vol_config['mode'] + prop = vol_config['propagation'] + out.append(f'Volume={svol}:{dvol}:{mode},{prop}') + return out def generate(container): # bail out early - looks like removal from running config @@ -590,71 +601,16 @@ def generate(container): os.unlink(file) return None - if 'network' in container: - for network, network_config in container['network'].items(): - type_config = dict_search('type', network_config) - if dict_search('macvlan', type_config): - net_interface = dict_search('macvlan.parent', type_config) - driver = 'macvlan' - mode = dict_search('macvlan.mode', type_config) - elif dict_search('bridge', type_config) is not None: - net_interface = f'pod-{network}' - driver = 'bridge' - else: - net_interface = f'pod-{network}' - driver = 'bridge' - tmp = { - 'name': network, - 'id': sha256(f'{network}'.encode()).hexdigest(), - 'driver': driver, - 'network_interface': net_interface, - 'subnets': [], - 'ipv6_enabled': False, - 'internal': False, - 'dns_enabled': True, - 'ipam_options': { - 'driver': 'host-local' - }, - 'options': { - **({'mode': mode} if driver == 'macvlan' else {}), - 'mtu': '1500' - } - } - - if 'no_name_server' in network_config: - tmp['dns_enabled'] = False - - if 'mtu' in network_config: - tmp['options']['mtu'] = network_config['mtu'] - - for prefix in network_config['prefix']: - gateway4, gateway6 = None, None - if dict_search('gateway', network_config): - for gw in network_config['gateway']: - if is_ipv6(gw): - gateway6 = gw - else: - gateway4 = gw - - if is_ipv6(prefix) and not gateway6: - gateway6 = inc_ip(prefix, 1) - elif not gateway4: - gateway4 = inc_ip(prefix, 1) - - if is_ipv6(prefix): - tmp['ipv6_enabled'] = True - net = {'subnet': prefix, 'gateway': gateway6} - else: - net = {'subnet': prefix, 'gateway': gateway4} - - tmp['subnets'].append(net) - - write_file(f'/etc/containers/networks/{network}.json', json_write(tmp, indent=2)) - render(config_containers, 'container/containers.conf.j2', container) render(config_registry, 'container/registries.conf.j2', container) render(config_storage, 'container/storage.conf.j2', container) + if 'network' in container: + for network, network_config in container['network'].items(): + file_path = os.path.join(quadlet_unit_path, f'vyos-{network}.network') + opts = generate_network_options(network, network_config) + render(file_path, 'container/quadlet-network.j2', {'name': network, 'opts': opts}) + if 'name' in container: host_ident = get_host_identity() network_config = container.get('network', {}) @@ -662,10 +618,10 @@ def generate(container): if 'disable' in container_config: continue - file_path = os.path.join(systemd_unit_path, f'vyos-container-{name}.service') - run_args = generate_run_arguments(name, container_config, host_ident, network_config) - render(file_path, 'container/systemd-unit.j2', {'name': name, 'run_args': run_args, }, - formatter=lambda _: _.replace(""", '"').replace("'", "'")) + file_path = os.path.join(quadlet_unit_path, f'vyos-container-{name}.container') + quadlet_opts = generate_quadlet_options(name, container_config, host_ident, network_config) + restart = container_config['restart'] + render(file_path, 'container/quadlet-unit.j2', {'name': name, 'opts': quadlet_opts, 'restart': restart}) return None @@ -674,21 +630,18 @@ def apply(container): # Delete old containers if needed. We can't delete running container # Option "--force" allows to delete containers with any status if 'container_remove' in container: - for name in container['container_remove']: - file_path = os.path.join(systemd_unit_path, f'vyos-container-{name}.service') - call(f'systemctl stop vyos-container-{name}.service') - if os.path.exists(file_path): - os.unlink(file_path) - - call('systemctl daemon-reload') + quadlet_paths = [f'vyos-container-{name}.container' for name in container['container_remove']] + run(['podman', 'quadlet', 'rm', '-f'] + quadlet_paths) # Delete old networks if needed if 'network_remove' in container: - for network in container['network_remove']: - call(f'podman network rm {network} >/dev/null 2>&1') + quadlet_paths = [f'vyos-{name}.network' for name in container['network_remove']] + run(['podman', 'quadlet', 'rm', '-f'] + quadlet_paths) + run(['podman', 'network', 'rm'] + container['network_remove']) # `quadlet rm` does not remove the instance + + run(['systemctl', 'daemon-reload']) # Add container - disabled_new = False if 'name' in container: for name, container_config in container['name'].items(): image = container_config['image'] @@ -702,18 +655,11 @@ def apply(container): # check if there is a container by that name running tmp = _cmdl(['podman', 'ps', '-a', '--format', '{{.Names}}']) if name in tmp: - file_path = os.path.join(systemd_unit_path, f'vyos-container-{name}.service') - call(f'systemctl stop vyos-container-{name}.service') - if os.path.exists(file_path): - disabled_new = True - os.unlink(file_path) + run(['podman', 'quadlet', 'rm', '-f', f'vyos-container-{name}.container']) continue if 'container_restart' in container and name in container['container_restart']: - cmdl(['systemctl', 'restart', f'vyos-container-{name}.service']) - - if disabled_new: - call('systemctl daemon-reload') + cmdl(['systemctl', 'restart', f'vyos-container-{name}']) # Re-Start network and assign it to given VRF if requested. restart_network(container) |
