summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/conf_mode/protocols_bfd.py3
-rwxr-xr-xsrc/conf_mode/protocols_bgp.py10
-rwxr-xr-xsrc/conf_mode/service_dhcp-server.py28
-rwxr-xr-xsrc/conf_mode/system_option.py11
-rwxr-xr-xsrc/conf_mode/system_sflow.py11
-rwxr-xr-xsrc/conf_mode/vrf.py47
-rwxr-xr-xsrc/migration-scripts/bgp/4-to-567
-rwxr-xr-xsrc/migration-scripts/https/5-to-64
-rwxr-xr-xsrc/migration-scripts/policy/4-to-548
-rwxr-xr-xsrc/migration-scripts/qos/1-to-248
-rwxr-xr-xsrc/op_mode/image_installer.py12
-rwxr-xr-xsrc/op_mode/multicast.py72
-rwxr-xr-xsrc/op_mode/show_openvpn.py6
13 files changed, 308 insertions, 59 deletions
diff --git a/src/conf_mode/protocols_bfd.py b/src/conf_mode/protocols_bfd.py
index dab784662..37421efb4 100755
--- a/src/conf_mode/protocols_bfd.py
+++ b/src/conf_mode/protocols_bfd.py
@@ -72,6 +72,9 @@ def verify(bfd):
if 'source' in peer_config and 'interface' in peer_config['source']:
raise ConfigError('BFD multihop and source interface cannot be used together')
+ if 'minimum_ttl' in peer_config and 'multihop' not in peer_config:
+ raise ConfigError('Minimum TTL is only available for multihop BFD sessions!')
+
if 'profile' in peer_config:
profile_name = peer_config['profile']
if 'profile' not in bfd or profile_name not in bfd['profile']:
diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py
index f6f3370c3..d90dfe45b 100755
--- a/src/conf_mode/protocols_bgp.py
+++ b/src/conf_mode/protocols_bgp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2023 VyOS maintainers and contributors
+# Copyright (C) 2020-2024 VyOS maintainers and contributors
#
# 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
@@ -509,6 +509,14 @@ def verify(bgp):
if verify_vrf_as_import(vrf_name, afi, bgp['dependent_vrfs']):
raise ConfigError(
'Command "import vrf" conflicts with "route-target vpn both" command!')
+ if dict_search('route_target.vpn.export', afi_config):
+ raise ConfigError(
+ 'Command "route-target vpn export" conflicts '\
+ 'with "route-target vpn both" command!')
+ if dict_search('route_target.vpn.import', afi_config):
+ raise ConfigError(
+ 'Command "route-target vpn import" conflicts '\
+ 'with "route-target vpn both" command!')
if dict_search('route_target.vpn.import', afi_config):
if verify_vrf_as_import(vrf_name, afi, bgp['dependent_vrfs']):
diff --git a/src/conf_mode/service_dhcp-server.py b/src/conf_mode/service_dhcp-server.py
index 9632b91fc..91ea354b6 100755
--- a/src/conf_mode/service_dhcp-server.py
+++ b/src/conf_mode/service_dhcp-server.py
@@ -246,19 +246,21 @@ def verify(dhcp):
raise ConfigError(f'Either MAC address or Client identifier (DUID) is required for '
f'static mapping "{mapping}" within shared-network "{network}, {subnet}"!')
- if mapping_config['ip_address'] in used_ips:
- raise ConfigError(f'Configured IP address for static mapping "{mapping}" already exists on another static mapping')
- used_ips.append(mapping_config['ip_address'])
-
- if 'mac' in mapping_config:
- if mapping_config['mac'] in used_mac:
- raise ConfigError(f'Configured MAC address for static mapping "{mapping}" already exists on another static mapping')
- used_mac.append(mapping_config['mac'])
-
- if 'duid' in mapping_config:
- if mapping_config['duid'] in used_duid:
- raise ConfigError(f'Configured DUID for static mapping "{mapping}" already exists on another static mapping')
- used_duid.append(mapping_config['duid'])
+ if 'disable' not in mapping_config:
+ if mapping_config['ip_address'] in used_ips:
+ raise ConfigError(f'Configured IP address for static mapping "{mapping}" already exists on another static mapping')
+ used_ips.append(mapping_config['ip_address'])
+
+ if 'disable' not in mapping_config:
+ if 'mac' in mapping_config:
+ if mapping_config['mac'] in used_mac:
+ raise ConfigError(f'Configured MAC address for static mapping "{mapping}" already exists on another static mapping')
+ used_mac.append(mapping_config['mac'])
+
+ if 'duid' in mapping_config:
+ if mapping_config['duid'] in used_duid:
+ raise ConfigError(f'Configured DUID for static mapping "{mapping}" already exists on another static mapping')
+ used_duid.append(mapping_config['duid'])
# There must be one subnet connected to a listen interface.
# This only counts if the network itself is not disabled!
diff --git a/src/conf_mode/system_option.py b/src/conf_mode/system_option.py
index d92121b3d..3b5b67437 100755
--- a/src/conf_mode/system_option.py
+++ b/src/conf_mode/system_option.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2023 VyOS maintainers and contributors
+# Copyright (C) 2019-2024 VyOS maintainers and contributors
#
# 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
@@ -22,6 +22,7 @@ from time import sleep
from vyos.config import Config
from vyos.configverify import verify_source_interface
+from vyos.system import grub_util
from vyos.template import render
from vyos.utils.process import cmd
from vyos.utils.process import is_systemd_service_running
@@ -39,7 +40,6 @@ time_format_to_locale = {
'24-hour': 'en_GB.UTF-8'
}
-
def get_config(config=None):
if config:
conf = config
@@ -87,6 +87,13 @@ def verify(options):
def generate(options):
render(curlrc_config, 'system/curlrc.j2', options)
render(ssh_config, 'system/ssh_config.j2', options)
+
+ cmdline_options = []
+ if 'kernel' in options:
+ if 'disable_mitigations' in options['kernel']:
+ cmdline_options.append('mitigations=off')
+ grub_util.update_kernel_cmdline_options(' '.join(cmdline_options))
+
return None
def apply(options):
diff --git a/src/conf_mode/system_sflow.py b/src/conf_mode/system_sflow.py
index 2df1bbb7a..41119b494 100755
--- a/src/conf_mode/system_sflow.py
+++ b/src/conf_mode/system_sflow.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-2024 VyOS maintainers and contributors
#
# 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
@@ -19,6 +19,7 @@ import os
from sys import exit
from vyos.config import Config
+from vyos.configverify import verify_vrf
from vyos.template import render
from vyos.utils.process import call
from vyos.utils.network import is_addr_assigned
@@ -46,7 +47,6 @@ def get_config(config=None):
return sflow
-
def verify(sflow):
if not sflow:
return None
@@ -68,9 +68,8 @@ def verify(sflow):
if 'server' not in sflow:
raise ConfigError('You need to configure at least one sFlow server!')
- # return True if all checks were passed
- return True
-
+ verify_vrf(sflow)
+ return None
def generate(sflow):
if not sflow:
@@ -81,7 +80,6 @@ def generate(sflow):
# Reload systemd manager configuration
call('systemctl daemon-reload')
-
def apply(sflow):
if not sflow:
# Stop flow-accounting daemon and remove configuration file
@@ -93,7 +91,6 @@ def apply(sflow):
# Start/reload flow-accounting daemon
call(f'systemctl restart {systemd_service}')
-
if __name__ == '__main__':
try:
config = get_config()
diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py
index 9b1b6355f..f2c544aa6 100755
--- a/src/conf_mode/vrf.py
+++ b/src/conf_mode/vrf.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2023 VyOS maintainers and contributors
+# Copyright (C) 2020-2024 VyOS maintainers and contributors
#
# 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
@@ -27,13 +27,12 @@ from vyos.ifconfig import Interface
from vyos.template import render
from vyos.template import render_to_string
from vyos.utils.dict import dict_search
+from vyos.utils.kernel import check_kmod
from vyos.utils.network import get_interface_config
from vyos.utils.network import get_vrf_members
from vyos.utils.network import interface_exists
from vyos.utils.process import call
from vyos.utils.process import cmd
-from vyos.utils.process import popen
-from vyos.utils.process import run
from vyos.utils.system import sysctl_write
from vyos import ConfigError
from vyos import frr
@@ -41,17 +40,29 @@ from vyos import airbag
airbag.enable()
config_file = '/etc/iproute2/rt_tables.d/vyos-vrf.conf'
-nft_vrf_config = '/tmp/nftables-vrf-zones'
-
-def has_rule(af : str, priority : int, table : str):
- """ Check if a given ip rule exists """
+k_mod = ['vrf']
+
+def has_rule(af : str, priority : int, table : str=None):
+ """
+ Check if a given ip rule exists
+ $ ip --json -4 rule show
+ [{'l3mdev': None, 'priority': 1000, 'src': 'all'},
+ {'action': 'unreachable', 'l3mdev': None, 'priority': 2000, 'src': 'all'},
+ {'priority': 32765, 'src': 'all', 'table': 'local'},
+ {'priority': 32766, 'src': 'all', 'table': 'main'},
+ {'priority': 32767, 'src': 'all', 'table': 'default'}]
+ """
if af not in ['-4', '-6']:
raise ValueError()
- command = f'ip -j {af} rule show'
+ command = f'ip --detail --json {af} rule show'
for tmp in loads(cmd(command)):
- if {'priority', 'table'} <= set(tmp):
+ if 'priority' in tmp and 'table' in tmp:
if tmp['priority'] == priority and tmp['table'] == table:
return True
+ elif 'priority' in tmp and table in tmp:
+ # l3mdev table has a different layout
+ if tmp['priority'] == priority:
+ return True
return False
def vrf_interfaces(c, match):
@@ -173,8 +184,6 @@ def verify(vrf):
def generate(vrf):
# Render iproute2 VR helper names
render(config_file, 'iproute2/vrf.conf.j2', vrf)
- # Render nftables zones config
- render(nft_vrf_config, 'firewall/nftables-vrf-zones.j2', vrf)
# Render VRF Kernel/Zebra route-map filters
vrf['frr_zebra_config'] = render_to_string('frr/zebra.vrf.route-map.frr.j2', vrf)
@@ -227,14 +236,6 @@ def apply(vrf):
sysctl_write('net.vrf.strict_mode', strict_mode)
if 'name' in vrf:
- # Separate VRFs in conntrack table
- # check if table already exists
- _, err = popen('nft list table inet vrf_zones')
- # If not, create a table
- if err and os.path.exists(nft_vrf_config):
- cmd(f'nft -f {nft_vrf_config}')
- os.unlink(nft_vrf_config)
-
# Linux routing uses rules to find tables - routing targets are then
# looked up in those tables. If the lookup got a matching route, the
# process ends.
@@ -318,17 +319,11 @@ def apply(vrf):
frr_cfg.add_before(frr.default_add_before, vrf['frr_zebra_config'])
frr_cfg.commit_configuration(zebra_daemon)
- # return to default lookup preference when no VRF is configured
- if 'name' not in vrf:
- # Remove VRF zones table from nftables
- tmp = run('nft list table inet vrf_zones')
- if tmp == 0:
- cmd('nft delete table inet vrf_zones')
-
return None
if __name__ == '__main__':
try:
+ check_kmod(k_mod)
c = get_config()
verify(c)
generate(c)
diff --git a/src/migration-scripts/bgp/4-to-5 b/src/migration-scripts/bgp/4-to-5
new file mode 100755
index 000000000..c4eb9ec72
--- /dev/null
+++ b/src/migration-scripts/bgp/4-to-5
@@ -0,0 +1,67 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2024 VyOS maintainers and contributors
+#
+# 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/>.
+
+# Delete 'protocols bgp address-family ipv6-unicast route-target vpn
+# import/export', if 'protocols bgp address-family ipv6-unicast
+# route-target vpn both' exists
+
+from sys import argv
+from sys import exit
+
+from vyos.configtree import ConfigTree
+
+if len(argv) < 2:
+ print("Must specify file name!")
+ exit(1)
+
+file_name = argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+config = ConfigTree(config_file)
+
+bgp_base = ['protocols', 'bgp']
+# Delete 'import/export' in default vrf if 'both' exists
+if config.exists(bgp_base):
+ for address_family in ['ipv4-unicast', 'ipv6-unicast']:
+ rt_path = bgp_base + ['address-family', address_family, 'route-target',
+ 'vpn']
+ if config.exists(rt_path + ['both']):
+ if config.exists(rt_path + ['import']):
+ config.delete(rt_path + ['import'])
+ if config.exists(rt_path + ['export']):
+ config.delete(rt_path + ['export'])
+
+# Delete import/export in vrfs if both exists
+if config.exists(['vrf', 'name']):
+ for vrf in config.list_nodes(['vrf', 'name']):
+ vrf_base = ['vrf', 'name', vrf]
+ for address_family in ['ipv4-unicast', 'ipv6-unicast']:
+ rt_path = vrf_base + bgp_base + ['address-family', address_family,
+ 'route-target', 'vpn']
+ if config.exists(rt_path + ['both']):
+ if config.exists(rt_path + ['import']):
+ config.delete(rt_path + ['import'])
+ if config.exists(rt_path + ['export']):
+ config.delete(rt_path + ['export'])
+
+try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+except OSError as e:
+ print(f'Failed to save the modified config: {e}')
+ exit(1)
diff --git a/src/migration-scripts/https/5-to-6 b/src/migration-scripts/https/5-to-6
index 6d6efd32c..0090adccb 100755
--- a/src/migration-scripts/https/5-to-6
+++ b/src/migration-scripts/https/5-to-6
@@ -43,11 +43,11 @@ if not config.exists(base):
# Nothing to do
sys.exit(0)
-if config.exists(base + ['certificates']):
+if config.exists(base + ['certificates', 'certbot']):
# both domain-name and email must be set on CLI - ensured by previous verify()
domain_names = config.return_values(base + ['certificates', 'certbot', 'domain-name'])
email = config.return_value(base + ['certificates', 'certbot', 'email'])
- config.delete(base + ['certificates'])
+ config.delete(base + ['certificates', 'certbot'])
# Set default certname based on domain-name
cert_name = 'https-' + domain_names[0].split('.')[0]
diff --git a/src/migration-scripts/policy/4-to-5 b/src/migration-scripts/policy/4-to-5
index f6f889c35..5b8fee17e 100755
--- a/src/migration-scripts/policy/4-to-5
+++ b/src/migration-scripts/policy/4-to-5
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-2024 VyOS maintainers and contributors
#
# 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
@@ -37,7 +37,53 @@ base4 = ['policy', 'route']
base6 = ['policy', 'route6']
config = ConfigTree(config_file)
+
+def delete_orphaned_interface_policy(config, iftype, ifname, vif=None, vifs=None, vifc=None):
+ """Delete unexpected policy on interfaces in cases when
+ policy does not exist but inreface has a policy configuration
+ Example T5941:
+ set interfaces bonding bond0 vif 995 policy
+ """
+ if_path = ['interfaces', iftype, ifname]
+
+ if vif:
+ if_path += ['vif', vif]
+ elif vifs:
+ if_path += ['vif-s', vifs]
+ if vifc:
+ if_path += ['vif-c', vifc]
+
+ if not config.exists(if_path + ['policy']):
+ return
+
+ config.delete(if_path + ['policy'])
+
+
if not config.exists(base4) and not config.exists(base6):
+ # Delete orphaned nodes on interfaces T5941
+ for iftype in config.list_nodes(['interfaces']):
+ for ifname in config.list_nodes(['interfaces', iftype]):
+ delete_orphaned_interface_policy(config, iftype, ifname)
+
+ if config.exists(['interfaces', iftype, ifname, 'vif']):
+ for vif in config.list_nodes(['interfaces', iftype, ifname, 'vif']):
+ delete_orphaned_interface_policy(config, iftype, ifname, vif=vif)
+
+ if config.exists(['interfaces', iftype, ifname, 'vif-s']):
+ for vifs in config.list_nodes(['interfaces', iftype, ifname, 'vif-s']):
+ delete_orphaned_interface_policy(config, iftype, ifname, vifs=vifs)
+
+ if config.exists(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
+ for vifc in config.list_nodes(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
+ delete_orphaned_interface_policy(config, iftype, ifname, vifs=vifs, vifc=vifc)
+
+ try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+ except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ exit(1)
+
# Nothing to do
exit(0)
diff --git a/src/migration-scripts/qos/1-to-2 b/src/migration-scripts/qos/1-to-2
index cca32d06e..666811e5a 100755
--- a/src/migration-scripts/qos/1-to-2
+++ b/src/migration-scripts/qos/1-to-2
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-2024 VyOS maintainers and contributors
#
# 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
@@ -40,7 +40,53 @@ with open(file_name, 'r') as f:
base = ['traffic-policy']
config = ConfigTree(config_file)
+
+def delete_orphaned_interface_policy(config, iftype, ifname, vif=None, vifs=None, vifc=None):
+ """Delete unexpected traffic-policy on interfaces in cases when
+ policy does not exist but inreface has a policy configuration
+ Example T5941:
+ set interfaces bonding bond0 vif 995 traffic-policy
+ """
+ if_path = ['interfaces', iftype, ifname]
+
+ if vif:
+ if_path += ['vif', vif]
+ elif vifs:
+ if_path += ['vif-s', vifs]
+ if vifc:
+ if_path += ['vif-c', vifc]
+
+ if not config.exists(if_path + ['traffic-policy']):
+ return
+
+ config.delete(if_path + ['traffic-policy'])
+
+
if not config.exists(base):
+ # Delete orphaned nodes on interfaces T5941
+ for iftype in config.list_nodes(['interfaces']):
+ for ifname in config.list_nodes(['interfaces', iftype]):
+ delete_orphaned_interface_policy(config, iftype, ifname)
+
+ if config.exists(['interfaces', iftype, ifname, 'vif']):
+ for vif in config.list_nodes(['interfaces', iftype, ifname, 'vif']):
+ delete_orphaned_interface_policy(config, iftype, ifname, vif=vif)
+
+ if config.exists(['interfaces', iftype, ifname, 'vif-s']):
+ for vifs in config.list_nodes(['interfaces', iftype, ifname, 'vif-s']):
+ delete_orphaned_interface_policy(config, iftype, ifname, vifs=vifs)
+
+ if config.exists(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
+ for vifc in config.list_nodes(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
+ delete_orphaned_interface_policy(config, iftype, ifname, vifs=vifs, vifc=vifc)
+
+ try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+ except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ exit(1)
+
# Nothing to do
exit(0)
diff --git a/src/op_mode/image_installer.py b/src/op_mode/image_installer.py
index fad6face7..501e9b804 100755
--- a/src/op_mode/image_installer.py
+++ b/src/op_mode/image_installer.py
@@ -69,8 +69,8 @@ MSG_WARN_ISO_SIGN_INVALID: str = 'Signature is not valid. Do you want to continu
MSG_WARN_ISO_SIGN_UNAVAL: str = 'Signature is not available. Do you want to continue with installation?'
MSG_WARN_ROOT_SIZE_TOOBIG: str = 'The size is too big. Try again.'
MSG_WARN_ROOT_SIZE_TOOSMALL: str = 'The size is too small. Try again'
-MSG_WARN_IMAGE_NAME_WRONG: str = 'The suggested name is unsupported!\n'
-'It must be between 1 and 32 characters long and contains only the next characters: .+-_ a-z A-Z 0-9'
+MSG_WARN_IMAGE_NAME_WRONG: str = 'The suggested name is unsupported!\n'\
+'It must be between 1 and 64 characters long and contains only the next characters: .+-_ a-z A-Z 0-9'
CONST_MIN_DISK_SIZE: int = 2147483648 # 2 GB
CONST_MIN_ROOT_SIZE: int = 1610612736 # 1.5 GB
# a reserved space: 2MB for header, 1 MB for BIOS partition, 256 MB for EFI
@@ -812,7 +812,11 @@ def add_image(image_path: str, vrf: str = None, username: str = '',
f'Adding image would downgrade image tools to v.{cfg_ver}; disallowed')
if not no_prompt:
- image_name: str = ask_input(MSG_INPUT_IMAGE_NAME, version_name)
+ while True:
+ image_name: str = ask_input(MSG_INPUT_IMAGE_NAME, version_name)
+ if image.validate_name(image_name):
+ break
+ print(MSG_WARN_IMAGE_NAME_WRONG)
set_as_default: bool = ask_yes_no(MSG_INPUT_IMAGE_DEFAULT, default=True)
else:
image_name: str = version_name
@@ -867,7 +871,7 @@ def add_image(image_path: str, vrf: str = None, username: str = '',
except Exception as err:
# unmount an ISO and cleanup
cleanup([str(iso_path)])
- exit(f'Whooops: {err}')
+ exit(f'Error: {err}')
def parse_arguments() -> Namespace:
diff --git a/src/op_mode/multicast.py b/src/op_mode/multicast.py
new file mode 100755
index 000000000..0666f8af3
--- /dev/null
+++ b/src/op_mode/multicast.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2024 VyOS maintainers and contributors
+#
+# 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/>.
+
+import json
+import sys
+import typing
+
+from tabulate import tabulate
+from vyos.utils.process import cmd
+
+import vyos.opmode
+
+ArgFamily = typing.Literal['inet', 'inet6']
+
+def _get_raw_data(family, interface=None):
+ tmp = 'ip -4'
+ if family == 'inet6':
+ tmp = 'ip -6'
+ tmp = f'{tmp} -j maddr show'
+ if interface:
+ tmp = f'{tmp} dev {interface}'
+ output = cmd(tmp)
+ data = json.loads(output)
+ if not data:
+ return []
+ return data
+
+def _get_formatted_output(raw_data):
+ data_entries = []
+
+ # sort result by interface name
+ for interface in sorted(raw_data, key=lambda x: x['ifname']):
+ for address in interface['maddr']:
+ tmp = []
+ tmp.append(interface['ifname'])
+ tmp.append(address['family'])
+ tmp.append(address['address'])
+
+ data_entries.append(tmp)
+
+ headers = ["Interface", "Family", "Address"]
+ output = tabulate(data_entries, headers, numalign="left")
+ return output
+
+def show_group(raw: bool, family: ArgFamily, interface: typing.Optional[str]):
+ multicast_data = _get_raw_data(family=family, interface=interface)
+ if raw:
+ return multicast_data
+ else:
+ return _get_formatted_output(multicast_data)
+
+if __name__ == "__main__":
+ try:
+ res = vyos.opmode.run(sys.modules[__name__])
+ if res:
+ print(res)
+ except (ValueError, vyos.opmode.Error) as e:
+ print(e)
+ sys.exit(1)
diff --git a/src/op_mode/show_openvpn.py b/src/op_mode/show_openvpn.py
index e29e594a5..6abafc8b6 100755
--- a/src/op_mode/show_openvpn.py
+++ b/src/op_mode/show_openvpn.py
@@ -63,9 +63,11 @@ def get_vpn_tunnel_address(peer, interface):
# filter out subnet entries
lst = [l for l in lst[1:] if '/' not in l.split(',')[0]]
- tunnel_ip = lst[0].split(',')[0]
+ if lst:
+ tunnel_ip = lst[0].split(',')[0]
+ return tunnel_ip
- return tunnel_ip
+ return 'n/a'
def get_status(mode, interface):
status_file = '/var/run/openvpn/{}.status'.format(interface)