summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/container/containers.conf.j28
-rwxr-xr-xdata/templates/firewall/nftables.j214
-rw-r--r--interface-definitions/container.xml.in43
-rw-r--r--interface-definitions/include/firewall/global-options.xml.i49
-rw-r--r--interface-definitions/include/version/container-version.xml.i2
-rw-r--r--interface-definitions/include/version/firewall-version.xml.i2
-rw-r--r--op-mode-definitions/container.xml.in7
-rwxr-xr-xpython/vyos/template.py23
-rw-r--r--smoketest/config-tests/firewall-bridged-global-options21
-rw-r--r--smoketest/configs/firewall-bridged-global-options60
-rwxr-xr-xsmoketest/scripts/cli/test_container.py10
-rwxr-xr-xsmoketest/scripts/cli/test_firewall.py8
-rwxr-xr-xsrc/conf_mode/container.py3
-rwxr-xr-xsrc/conf_mode/vrf.py7
-rw-r--r--src/migration-scripts/container/2-to-331
-rw-r--r--src/migration-scripts/firewall/18-to-1935
-rw-r--r--src/migration-scripts/quagga/8-to-918
-rwxr-xr-xsrc/op_mode/container.py42
-rw-r--r--src/tests/test_template.py4
19 files changed, 327 insertions, 60 deletions
diff --git a/data/templates/container/containers.conf.j2 b/data/templates/container/containers.conf.j2
index 65436801e..414c3e849 100644
--- a/data/templates/container/containers.conf.j2
+++ b/data/templates/container/containers.conf.j2
@@ -170,14 +170,6 @@ default_sysctls = [
#
#label = true
-# Logging driver for the container. Available options: k8s-file and journald.
-#
-{% if log_driver is vyos_defined %}
-log_driver = "{{ log_driver }}"
-{% else %}
-#log_driver = "k8s-file"
-{% endif %}
-
# Maximum size allowed for the container log file. Negative numbers indicate
# that no size limit is imposed. If positive, it must be >= 8192 to match or
# exceed conmon's read buffer. The file is truncated and re-opened so the
diff --git a/data/templates/firewall/nftables.j2 b/data/templates/firewall/nftables.j2
index bf051bb57..39ef72059 100755
--- a/data/templates/firewall/nftables.j2
+++ b/data/templates/firewall/nftables.j2
@@ -410,15 +410,11 @@ table bridge vyos_filter {
{% for prior, conf in bridge.output.items() %}
chain VYOS_OUTPUT_{{ prior }} {
type filter hook output priority {{ prior }}; policy accept;
-{% if global_options.apply_to_bridged_traffic is vyos_defined %}
-{% if 'invalid_connections' in global_options.apply_to_bridged_traffic %}
- ct state invalid udp sport 67 udp dport 68 counter accept
- ct state invalid ether type arp counter accept
- ct state invalid ether type 8021q counter accept
- ct state invalid ether type 8021ad counter accept
- ct state invalid ether type 0x8863 counter accept
- ct state invalid ether type 0x8864 counter accept
- ct state invalid ether type 0x0842 counter accept
+{% if global_options.apply_to_bridged_traffic.accept_invalid is vyos_defined %}
+{% if 'ethernet_type' in global_options.apply_to_bridged_traffic.accept_invalid %}
+{% for ether_type in global_options.apply_to_bridged_traffic.accept_invalid.ethernet_type %}
+ {{ ether_type | nft_accept_invalid() }}
+{% endfor %}
{% endif %}
{% endif %}
{% if global_options.state_policy is vyos_defined %}
diff --git a/interface-definitions/container.xml.in b/interface-definitions/container.xml.in
index 434bf7528..f20fd7690 100644
--- a/interface-definitions/container.xml.in
+++ b/interface-definitions/container.xml.in
@@ -532,6 +532,30 @@
</leafNode>
</children>
</tagNode>
+ <leafNode name="log-driver">
+ <properties>
+ <help>Configure container log driver</help>
+ <completionHelp>
+ <list>k8s-file journald none</list>
+ </completionHelp>
+ <valueHelp>
+ <format>k8s-file</format>
+ <description>Logs to plain-text file</description>
+ </valueHelp>
+ <valueHelp>
+ <format>journald</format>
+ <description>Logs to systemd's journal</description>
+ </valueHelp>
+ <valueHelp>
+ <format>none</format>
+ <description>Disable logging for the container</description>
+ </valueHelp>
+ <constraint>
+ <regex>(k8s-file|journald|none)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>journald</defaultValue>
+ </leafNode>
</children>
</tagNode>
<tagNode name="network">
@@ -627,25 +651,6 @@
</node>
</children>
</tagNode>
- <leafNode name="log-driver">
- <properties>
- <help>Configure container log driver</help>
- <completionHelp>
- <list>k8s-file journald</list>
- </completionHelp>
- <valueHelp>
- <format>k8s-file</format>
- <description>Logs to plain-text json file</description>
- </valueHelp>
- <valueHelp>
- <format>journald</format>
- <description>Logs to systemd's journal</description>
- </valueHelp>
- <constraint>
- <regex>(k8s-file|journald)</regex>
- </constraint>
- </properties>
- </leafNode>
</children>
</node>
</interfaceDefinition>
diff --git a/interface-definitions/include/firewall/global-options.xml.i b/interface-definitions/include/firewall/global-options.xml.i
index 794da4f9d..e19f3a7c5 100644
--- a/interface-definitions/include/firewall/global-options.xml.i
+++ b/interface-definitions/include/firewall/global-options.xml.i
@@ -49,12 +49,53 @@
<help>Apply configured firewall rules to traffic switched by bridges</help>
</properties>
<children>
- <leafNode name="invalid-connections">
+ <node name="accept-invalid">
<properties>
- <help>Accept ARP, 802.1q, 802.1ad, DHCP, PPPoE and WoL despite being marked as invalid connections</help>
- <valueless/>
+ <help>Accept connections despite they are marked as invalid</help>
</properties>
- </leafNode>
+ <children>
+ <leafNode name="ethernet-type">
+ <properties>
+ <help>Ethernet type</help>
+ <completionHelp>
+ <list>arp dhcp pppoe 802.1q 802.1ad pppoe-discovery wol</list>
+ </completionHelp>
+ <valueHelp>
+ <format>arp</format>
+ <description>Adress Resolution Protocol (ARP)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dhcp</format>
+ <description>Dynamic Host Configuration Protocol (DHCP)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>pppoe</format>
+ <description>Point to Point over Ethernet (PPPoE) Session</description>
+ </valueHelp>
+ <valueHelp>
+ <format>pppoe-discovery</format>
+ <description>PPPoE Discovery</description>
+ </valueHelp>
+ <valueHelp>
+ <format>802.1q</format>
+ <description>Customer VLAN tag type (802.1Q)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>802.1ad</format>
+ <description>Service VLAN tag type (802.1ad)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>wol</format>
+ <description>Wake-on-LAN magic packet</description>
+ </valueHelp>
+ <constraint>
+ <regex>(arp|dhcp|pppoe|pppoe-discovery|802.1q|802.1ad|wol)</regex>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
<leafNode name="ipv4">
<properties>
<help>Apply configured IPv4 firewall rules</help>
diff --git a/interface-definitions/include/version/container-version.xml.i b/interface-definitions/include/version/container-version.xml.i
index ed6e942cd..046bacfdc 100644
--- a/interface-definitions/include/version/container-version.xml.i
+++ b/interface-definitions/include/version/container-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/container-version.xml.i -->
-<syntaxVersion component='container' version='2'></syntaxVersion>
+<syntaxVersion component='container' version='3'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/include/version/firewall-version.xml.i b/interface-definitions/include/version/firewall-version.xml.i
index 1a8098297..1f3b779d5 100644
--- a/interface-definitions/include/version/firewall-version.xml.i
+++ b/interface-definitions/include/version/firewall-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/firewall-version.xml.i -->
-<syntaxVersion component='firewall' version='18'></syntaxVersion>
+<syntaxVersion component='firewall' version='19'></syntaxVersion>
<!-- include end -->
diff --git a/op-mode-definitions/container.xml.in b/op-mode-definitions/container.xml.in
index df28a792c..70ab4b6e5 100644
--- a/op-mode-definitions/container.xml.in
+++ b/op-mode-definitions/container.xml.in
@@ -89,7 +89,7 @@
<path>container name</path>
</completionHelp>
</properties>
- <command>podman logs --follow --names "$4"</command>
+ <command>${vyos_op_scripts_dir}/container.py show_log --follow --name "$4"</command>
</tagNode>
</children>
</node>
@@ -132,8 +132,7 @@
<path>container name</path>
</completionHelp>
</properties>
- <!-- no admin check -->
- <command>podman logs --names "$4"</command>
+ <command>${vyos_op_scripts_dir}/container.py show_log --name "$4"</command>
</tagNode>
<node name="network">
<properties>
@@ -162,7 +161,7 @@
<path>container name</path>
</completionHelp>
</properties>
- <command>podman logs --names "$4"</command>
+ <command>${vyos_op_scripts_dir}/container.py show_log --name "$4"</command>
</tagNode>
</children>
</node>
diff --git a/python/vyos/template.py b/python/vyos/template.py
index bf7928914..bf2f13183 100755
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -674,6 +674,29 @@ def nft_nested_group(out_list, includes, groups, key):
add_includes(name)
return out_list
+@register_filter('nft_accept_invalid')
+def nft_accept_invalid(ether_type):
+ ether_type_mapping = {
+ 'dhcp': 'udp sport 67 udp dport 68',
+ 'arp': 'arp',
+ 'pppoe-discovery': '0x8863',
+ 'pppoe': '0x8864',
+ '802.1q': '8021q',
+ '802.1ad': '8021ad',
+ 'wol': '0x0842',
+ }
+ if ether_type not in ether_type_mapping:
+ raise RuntimeError(f'Ethernet type "{ether_type}" not found in ' \
+ 'available ethernet types!')
+ out = 'ct state invalid '
+
+ if ether_type != 'dhcp':
+ out += 'ether type '
+
+ out += f'{ether_type_mapping[ether_type]} counter accept'
+
+ return out
+
@register_filter('nat_rule')
def nat_rule(rule_conf, rule_id, nat_type, ipv6=False):
from vyos.nat import parse_nat_rule
diff --git a/smoketest/config-tests/firewall-bridged-global-options b/smoketest/config-tests/firewall-bridged-global-options
new file mode 100644
index 000000000..1d960d6c1
--- /dev/null
+++ b/smoketest/config-tests/firewall-bridged-global-options
@@ -0,0 +1,21 @@
+set firewall bridge prerouting filter rule 10 action 'accept'
+set firewall bridge prerouting filter rule 10 ethernet-type 'arp'
+set firewall global-options apply-to-bridged-traffic accept-invalid ethernet-type 'dhcp'
+set firewall global-options apply-to-bridged-traffic accept-invalid ethernet-type 'arp'
+set firewall global-options apply-to-bridged-traffic accept-invalid ethernet-type 'pppoe-discovery'
+set firewall global-options apply-to-bridged-traffic accept-invalid ethernet-type 'pppoe'
+set firewall global-options apply-to-bridged-traffic accept-invalid ethernet-type '802.1q'
+set firewall global-options apply-to-bridged-traffic accept-invalid ethernet-type '802.1ad'
+set firewall global-options apply-to-bridged-traffic accept-invalid ethernet-type 'wol'
+set firewall global-options state-policy established action 'accept'
+set firewall global-options state-policy invalid action 'drop'
+set firewall global-options state-policy related action 'accept'
+set interfaces ethernet eth0 duplex 'auto'
+set interfaces ethernet eth0 speed 'auto'
+set interfaces ethernet eth1 duplex 'auto'
+set interfaces ethernet eth1 speed 'auto'
+set system console device ttyS0 speed '115200'
+set system domain-name 'vyos-ci-test.net'
+set system host-name 'vyos'
+set system login user vyos authentication encrypted-password '$6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0'
+set system login user vyos authentication plaintext-password ''
diff --git a/smoketest/configs/firewall-bridged-global-options b/smoketest/configs/firewall-bridged-global-options
new file mode 100644
index 000000000..a7e1428d8
--- /dev/null
+++ b/smoketest/configs/firewall-bridged-global-options
@@ -0,0 +1,60 @@
+firewall {
+ bridge {
+ prerouting {
+ filter {
+ rule 10 {
+ action "accept"
+ ethernet-type "arp"
+ }
+ }
+ }
+ }
+ global-options {
+ apply-to-bridged-traffic {
+ invalid-connections {
+ }
+ }
+ state-policy {
+ established {
+ action "accept"
+ }
+ invalid {
+ action "drop"
+ }
+ related {
+ action "accept"
+ }
+ }
+ }
+}
+interfaces {
+ ethernet eth0 {
+ duplex "auto"
+ speed "auto"
+ }
+ ethernet eth1 {
+ duplex auto
+ speed auto
+ }
+}
+system {
+ console {
+ device ttyS0 {
+ speed 115200
+ }
+ }
+ domain-name vyos-ci-test.net
+ host-name vyos
+ login {
+ user vyos {
+ authentication {
+ encrypted-password $6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0
+ plaintext-password ""
+ }
+ }
+ }
+}
+
+// Warning: Do not remove the following line.
+// vyos-config-version: "bgp@6:broadcast-relay@1:cluster@2:config-management@1:conntrack@6:conntrack-sync@2:container@2:dhcp-relay@2:dhcp-server@11:dhcpv6-server@6:dns-dynamic@4:dns-forwarding@4:firewall@18:flow-accounting@2:https@7:ids@2:interfaces@33:ipoe-server@4:ipsec@13:isis@3:l2tp@9:lldp@3:mdns@1:monitoring@2:nat@8:nat66@3:nhrp@1:ntp@3:openconnect@3:openvpn@4:ospf@2:pim@1:policy@9:pppoe-server@11:pptp@5:qos@3:quagga@12:reverse-proxy@3:rip@1:rpki@2:salt@1:snmp@3:ssh@2:sstp@6:system@29:vpp@1:vrf@3:vrrp@4:vyos-accel-ppp@2:wanloadbalance@4:webproxy@2"
+// Release version: 2025.06.17-0020-rolling
diff --git a/smoketest/scripts/cli/test_container.py b/smoketest/scripts/cli/test_container.py
index daad3a909..892d7ae0b 100755
--- a/smoketest/scripts/cli/test_container.py
+++ b/smoketest/scripts/cli/test_container.py
@@ -95,7 +95,7 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
'4096',
]
)
-
+ self.cli_set(base_path + ['name', cont_name, 'log-driver', 'journald'])
# commit changes
self.cli_commit()
@@ -110,13 +110,9 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
tmp = cmd(f'sudo podman exec -it {cont_name} sysctl kernel.msgmax')
self.assertEqual(tmp, 'kernel.msgmax = 4096')
- def test_log_driver(self):
- self.cli_set(base_path + ['log-driver', 'journald'])
-
- self.cli_commit()
+ l = cmd_to_json(f'sudo podman container inspect {cont_name}')
+ self.assertEqual(l['HostConfig']['LogConfig']['Type'], 'journald')
- tmp = cmd('podman info --format "{{ .Host.LogDriver }}"')
- self.assertEqual(tmp, 'journald')
def test_name_server(self):
cont_name = 'dns-test'
diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py
index 2d850dfdf..455c704d0 100755
--- a/smoketest/scripts/cli/test_firewall.py
+++ b/smoketest/scripts/cli/test_firewall.py
@@ -728,7 +728,13 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'group', 'ipv6-address-group', 'AGV6', 'address', '2001:db1::1'])
self.cli_set(['firewall', 'global-options', 'state-policy', 'established', 'action', 'accept'])
self.cli_set(['firewall', 'global-options', 'apply-to-bridged-traffic', 'ipv4'])
- self.cli_set(['firewall', 'global-options', 'apply-to-bridged-traffic', 'invalid-connections'])
+ self.cli_set(['firewall', 'global-options', 'apply-to-bridged-traffic', 'accept-invalid', 'ethernet-type', 'dhcp'])
+ self.cli_set(['firewall', 'global-options', 'apply-to-bridged-traffic', 'accept-invalid', 'ethernet-type', 'arp'])
+ self.cli_set(['firewall', 'global-options', 'apply-to-bridged-traffic', 'accept-invalid', 'ethernet-type', 'pppoe'])
+ self.cli_set(['firewall', 'global-options', 'apply-to-bridged-traffic', 'accept-invalid', 'ethernet-type', 'pppoe-discovery'])
+ self.cli_set(['firewall', 'global-options', 'apply-to-bridged-traffic', 'accept-invalid', 'ethernet-type', '802.1q'])
+ self.cli_set(['firewall', 'global-options', 'apply-to-bridged-traffic', 'accept-invalid', 'ethernet-type', '802.1ad'])
+ self.cli_set(['firewall', 'global-options', 'apply-to-bridged-traffic', 'accept-invalid', 'ethernet-type', 'wol'])
self.cli_set(['firewall', 'bridge', 'name', name, 'default-action', 'accept'])
self.cli_set(['firewall', 'bridge', 'name', name, 'default-log'])
diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py
index 83e6dee11..a381ace5c 100755
--- a/src/conf_mode/container.py
+++ b/src/conf_mode/container.py
@@ -310,6 +310,7 @@ def generate_run_arguments(name, container_config):
memory = container_config['memory']
shared_memory = container_config['shared_memory']
restart = container_config['restart']
+ log_driver = container_config['log_driver']
# Add sysctl options
sysctl_opt = ''
@@ -408,7 +409,7 @@ def generate_run_arguments(name, container_config):
name_server += f'--dns {ns}'
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} ' \
+ 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}'
entrypoint = ''
diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py
index 8baf55857..6e9d4147a 100755
--- a/src/conf_mode/vrf.py
+++ b/src/conf_mode/vrf.py
@@ -18,6 +18,8 @@ from sys import exit
from jmespath import search
from json import loads
+import vyos.defaults
+
from vyos.config import Config
from vyos.configdict import node_changed
from vyos.configverify import verify_route_map
@@ -163,6 +165,11 @@ def verify(vrf):
if 'table' not in vrf_config:
raise ConfigError(f'VRF "{name}" table id is mandatory!')
+ if int(vrf_config['table']) == vyos.defaults.rt_global_vrf:
+ raise ConfigError(
+ f'VRF "{name}" table id {vrf_config["table"]} cannot be used!'
+ )
+
# routing table id can't be changed - OS restriction
if interface_exists(name):
tmp = get_vrf_tableid(name)
diff --git a/src/migration-scripts/container/2-to-3 b/src/migration-scripts/container/2-to-3
new file mode 100644
index 000000000..54c6ec4c8
--- /dev/null
+++ b/src/migration-scripts/container/2-to-3
@@ -0,0 +1,31 @@
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+# T7473: container: allow log-driver to be set per container
+
+from vyos.configtree import ConfigTree
+
+def migrate(config: ConfigTree) -> None:
+ log_base = ['container', 'log-driver']
+ container_base = ['container', 'name']
+
+ if not config.exists(log_base):
+ return
+ else:
+ log_driver = config.return_value(log_base)
+ for container in config.list_nodes(container_base):
+ # Set the log-driver for each container
+ config.set(container_base + [container, 'log-driver'], value=log_driver)
+ config.delete(log_base)
diff --git a/src/migration-scripts/firewall/18-to-19 b/src/migration-scripts/firewall/18-to-19
new file mode 100644
index 000000000..3564e0e01
--- /dev/null
+++ b/src/migration-scripts/firewall/18-to-19
@@ -0,0 +1,35 @@
+# Copyright (C) 2024-2025 VyOS maintainers and contributors
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+# From
+# set firewall global-options apply-to-bridged-traffic invalid-connections
+# To
+# set firewall global-options apply-to-bridged-traffic accept-invalid ethernet-type <ethertype>
+
+from vyos.configtree import ConfigTree
+
+base = ['firewall', 'global-options', 'apply-to-bridged-traffic']
+
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base + ['invalid-connections']):
+ # Nothing to do
+ return
+
+ ether_types = ['dhcp', 'arp', 'pppoe-discovery', 'pppoe', '802.1q', '802.1ad', 'wol']
+
+ for ether_type in ether_types:
+ config.set(base + ['accept-invalid', 'ethernet-type'], value=ether_type, replace=False)
+
+ config.delete(base + ['invalid-connections'])
diff --git a/src/migration-scripts/quagga/8-to-9 b/src/migration-scripts/quagga/8-to-9
index eece6c15d..c28e07e5c 100644
--- a/src/migration-scripts/quagga/8-to-9
+++ b/src/migration-scripts/quagga/8-to-9
@@ -16,6 +16,7 @@
# - T2450: drop interface-route and interface-route6 from "protocols static"
from vyos.configtree import ConfigTree
+from vyos.template import is_ip
def migrate_interface_route(config, base, path, route_route6):
""" Generic migration function which can be called on every instance of
@@ -31,11 +32,18 @@ def migrate_interface_route(config, base, path, route_route6):
tmp = base + path + [route, 'next-hop-interface']
for interface in config.list_nodes(tmp):
- new_base = base + [route_route6, route, 'interface']
- config.set(new_base)
- config.set_tag(base + [route_route6])
- config.set_tag(new_base)
- config.copy(tmp + [interface], new_base + [interface])
+ if is_ip(interface): # not prohibited in 1.3.x, hence allowed
+ new_base = base + [route_route6, route, 'next-hop']
+ config.set(new_base)
+ config.set_tag(base + [route_route6])
+ config.set_tag(new_base)
+ config.copy(tmp + [interface], new_base + [interface])
+ else:
+ new_base = base + [route_route6, route, 'interface']
+ config.set(new_base)
+ config.set_tag(base + [route_route6])
+ config.set_tag(new_base)
+ config.copy(tmp + [interface], new_base + [interface])
config.delete(base + path)
diff --git a/src/op_mode/container.py b/src/op_mode/container.py
index 05f65df1f..f93df0fc4 100755
--- a/src/op_mode/container.py
+++ b/src/op_mode/container.py
@@ -16,6 +16,7 @@
import json
import sys
+import subprocess
from vyos.utils.process import cmd
from vyos.utils.process import rc_cmd
@@ -109,6 +110,47 @@ def restart(name: str):
print(f'Container "{name}" restarted!')
return output
+def show_log(name: str, follow: bool = False, raw: bool = False):
+ """
+ Show or monitor logs for a specific container.
+ Use --follow to continuously stream logs.
+ """
+ from vyos.configquery import ConfigTreeQuery
+ conf = ConfigTreeQuery()
+ container = conf.get_config_dict(['container', 'name', name], get_first_key=True, with_recursive_defaults=True)
+ log_type = container.get('log-driver')
+ if log_type == 'k8s-file':
+ if follow:
+ log_command_list = ['sudo', 'podman', 'logs', '--follow', '--names', name]
+ else:
+ log_command_list = ['sudo', 'podman', 'logs', '--names', name]
+ elif log_type == 'journald':
+ if follow:
+ log_command_list = ['journalctl', '--follow', '--unit', f'vyos-container-{name}.service']
+ else:
+ log_command_list = ['journalctl', '-e', '--no-pager', '--unit', f'vyos-container-{name}.service']
+ elif log_type == 'none':
+ print(f'Container "{name}" has disabled logs.')
+ return None
+ else:
+ raise vyos.opmode.InternalError(f'Unknown log type "{log_type}" for container "{name}".')
+
+ process = None
+ try:
+ process = subprocess.Popen(log_command_list,
+ stdout=sys.stdout,
+ stderr=sys.stderr)
+ process.wait()
+ except KeyboardInterrupt:
+ if process:
+ process.terminate()
+ process.wait()
+ return None
+ except Exception as e:
+ raise vyos.opmode.InternalError(f"Error starting logging command: {e} ")
+ return None
+
+
if __name__ == '__main__':
try:
res = vyos.opmode.run(sys.modules[__name__])
diff --git a/src/tests/test_template.py b/src/tests/test_template.py
index 4660c0038..09315d398 100644
--- a/src/tests/test_template.py
+++ b/src/tests/test_template.py
@@ -199,8 +199,12 @@ class TestVyOSTemplate(TestCase):
vyos.template.get_default_config_file('UNKNOWN')
with self.assertRaises(RuntimeError):
vyos.template.get_default_port('UNKNOWN')
+ with self.assertRaises(RuntimeError):
+ vyos.template.nft_accept_invalid('UNKNOWN')
self.assertEqual(vyos.template.get_default_config_file('sshd_user_ca'),
config_files['sshd_user_ca'])
self.assertEqual(vyos.template.get_default_port('certbot_haproxy'),
internal_ports['certbot_haproxy'])
+ self.assertEqual(vyos.template.nft_accept_invalid('arp'),
+ 'ct state invalid ether type arp counter accept')