summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/pull-request-labels.yml2
-rw-r--r--data/templates/firewall/nftables-vrf-zones.j217
-rw-r--r--data/templates/frr/bfdd.frr.j26
-rw-r--r--data/templates/grub/grub_vyos_version.j29
-rw-r--r--data/vyos-firewall-init.conf19
-rw-r--r--git0
-rw-r--r--interface-definitions/include/bfd/common.xml.i12
-rw-r--r--interface-definitions/include/firewall/common-rule-inet.xml.i19
-rw-r--r--interface-definitions/include/firewall/ipv4-custom-name.xml.i1
-rw-r--r--interface-definitions/include/firewall/ipv4-hook-forward.xml.i1
-rw-r--r--interface-definitions/include/firewall/ipv4-hook-input.xml.i1
-rw-r--r--interface-definitions/include/firewall/ipv6-custom-name.xml.i1
-rw-r--r--interface-definitions/include/firewall/ipv6-hook-forward.xml.i1
-rw-r--r--interface-definitions/include/firewall/ipv6-hook-input.xml.i1
-rw-r--r--interface-definitions/include/firewall/match-ipsec.xml.i21
-rw-r--r--interface-definitions/include/haproxy/rule-backend.xml.i2
-rw-r--r--interface-definitions/include/version/bgp-version.xml.i2
-rw-r--r--interface-definitions/system_option.xml.in13
-rw-r--r--op-mode-definitions/rpki.xml.in29
-rw-r--r--python/vyos/remote.py2
-rw-r--r--python/vyos/system/compat.py15
-rw-r--r--python/vyos/system/grub.py61
-rw-r--r--python/vyos/system/grub_util.py30
-rw-r--r--python/vyos/system/image.py13
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bfd.py10
-rwxr-xr-xsrc/conf_mode/protocols_bfd.py3
-rwxr-xr-xsrc/conf_mode/protocols_bgp.py10
-rwxr-xr-xsrc/conf_mode/system_option.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/show_openvpn.py6
35 files changed, 453 insertions, 91 deletions
diff --git a/.github/workflows/pull-request-labels.yml b/.github/workflows/pull-request-labels.yml
index 778daae30..3398af5b0 100644
--- a/.github/workflows/pull-request-labels.yml
+++ b/.github/workflows/pull-request-labels.yml
@@ -17,4 +17,4 @@ jobs:
contents: read
pull-requests: write
steps:
- - uses: actions/labeler@v5.0.0-alpha.1
+ - uses: actions/labeler@v5.0.0
diff --git a/data/templates/firewall/nftables-vrf-zones.j2 b/data/templates/firewall/nftables-vrf-zones.j2
deleted file mode 100644
index 3bce7312d..000000000
--- a/data/templates/firewall/nftables-vrf-zones.j2
+++ /dev/null
@@ -1,17 +0,0 @@
-table inet vrf_zones {
- # Map of interfaces and connections tracking zones
- map ct_iface_map {
- typeof iifname : ct zone
- }
- # Assign unique zones for each VRF
- # Chain for inbound traffic
- chain vrf_zones_ct_in {
- type filter hook prerouting priority raw; policy accept;
- counter ct original zone set iifname map @ct_iface_map
- }
- # Chain for locally-generated traffic
- chain vrf_zones_ct_out {
- type filter hook output priority raw; policy accept;
- counter ct original zone set oifname map @ct_iface_map
- }
-}
diff --git a/data/templates/frr/bfdd.frr.j2 b/data/templates/frr/bfdd.frr.j2
index c4adeb402..f3303e401 100644
--- a/data/templates/frr/bfdd.frr.j2
+++ b/data/templates/frr/bfdd.frr.j2
@@ -13,6 +13,9 @@ bfd
{% if profile_config.echo_mode is vyos_defined %}
echo-mode
{% endif %}
+{% if profile_config.minimum_ttl is vyos_defined %}
+ minimum-ttl {{ profile_config.minimum_ttl }}
+{% endif %}
{% if profile_config.passive is vyos_defined %}
passive-mode
{% endif %}
@@ -38,6 +41,9 @@ bfd
{% if peer_config.echo_mode is vyos_defined %}
echo-mode
{% endif %}
+{% if peer_config.minimum_ttl is vyos_defined %}
+ minimum-ttl {{ peer_config.minimum_ttl }}
+{% endif %}
{% if peer_config.passive is vyos_defined %}
passive-mode
{% endif %}
diff --git a/data/templates/grub/grub_vyos_version.j2 b/data/templates/grub/grub_vyos_version.j2
index 62688e68b..de85f1419 100644
--- a/data/templates/grub/grub_vyos_version.j2
+++ b/data/templates/grub/grub_vyos_version.j2
@@ -1,5 +1,10 @@
-{% set boot_opts_default = "boot=live rootdelay=5 noautologin net.ifnames=0 biosdevname=0 vyos-union=/boot/" + version_name %}
-{% if boot_opts != '' %}
+{% if boot_opts_config is vyos_defined %}
+{% if boot_opts_config %}
+{% set boot_opts_rendered = boot_opts_default + " " + boot_opts_config %}
+{% else %}
+{% set boot_opts_rendered = boot_opts_default %}
+{% endif %}
+{% elif boot_opts != '' %}
{% set boot_opts_rendered = boot_opts %}
{% else %}
{% set boot_opts_rendered = boot_opts_default %}
diff --git a/data/vyos-firewall-init.conf b/data/vyos-firewall-init.conf
index cd7d5011f..5a4e03015 100644
--- a/data/vyos-firewall-init.conf
+++ b/data/vyos-firewall-init.conf
@@ -54,3 +54,22 @@ table ip6 raw {
type filter hook prerouting priority -300; policy accept;
}
}
+
+# Required by VRF
+table inet vrf_zones {
+ # Map of interfaces and connections tracking zones
+ map ct_iface_map {
+ typeof iifname : ct zone
+ }
+ # Assign unique zones for each VRF
+ # Chain for inbound traffic
+ chain vrf_zones_ct_in {
+ type filter hook prerouting priority raw; policy accept;
+ counter ct original zone set iifname map @ct_iface_map
+ }
+ # Chain for locally-generated traffic
+ chain vrf_zones_ct_out {
+ type filter hook output priority raw; policy accept;
+ counter ct original zone set oifname map @ct_iface_map
+ }
+}
diff --git a/git b/git
deleted file mode 100644
index e69de29bb..000000000
--- a/git
+++ /dev/null
diff --git a/interface-definitions/include/bfd/common.xml.i b/interface-definitions/include/bfd/common.xml.i
index 126ab9b9a..8e6999d28 100644
--- a/interface-definitions/include/bfd/common.xml.i
+++ b/interface-definitions/include/bfd/common.xml.i
@@ -63,6 +63,18 @@
</leafNode>
</children>
</node>
+<leafNode name="minimum-ttl">
+ <properties>
+ <help>Expect packets with at least this TTL</help>
+ <valueHelp>
+ <format>u32:1-254</format>
+ <description>Minimum TTL expected</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-254"/>
+ </constraint>
+ </properties>
+</leafNode>
<leafNode name="passive">
<properties>
<help>Do not attempt to start sessions</help>
diff --git a/interface-definitions/include/firewall/common-rule-inet.xml.i b/interface-definitions/include/firewall/common-rule-inet.xml.i
index 6f56ecc85..85189d975 100644
--- a/interface-definitions/include/firewall/common-rule-inet.xml.i
+++ b/interface-definitions/include/firewall/common-rule-inet.xml.i
@@ -32,25 +32,6 @@
</leafNode>
</children>
</node>
-<node name="ipsec">
- <properties>
- <help>Inbound IPsec packets</help>
- </properties>
- <children>
- <leafNode name="match-ipsec">
- <properties>
- <help>Inbound IPsec packets</help>
- <valueless/>
- </properties>
- </leafNode>
- <leafNode name="match-none">
- <properties>
- <help>Inbound non-IPsec packets</help>
- <valueless/>
- </properties>
- </leafNode>
- </children>
-</node>
<node name="limit">
<properties>
<help>Rate limit using a token bucket filter</help>
diff --git a/interface-definitions/include/firewall/ipv4-custom-name.xml.i b/interface-definitions/include/firewall/ipv4-custom-name.xml.i
index 8199d15fe..8046b2d6c 100644
--- a/interface-definitions/include/firewall/ipv4-custom-name.xml.i
+++ b/interface-definitions/include/firewall/ipv4-custom-name.xml.i
@@ -33,6 +33,7 @@
<children>
#include <include/firewall/common-rule-ipv4.xml.i>
#include <include/firewall/inbound-interface.xml.i>
+ #include <include/firewall/match-ipsec.xml.i>
#include <include/firewall/offload-target.xml.i>
#include <include/firewall/outbound-interface.xml.i>
</children>
diff --git a/interface-definitions/include/firewall/ipv4-hook-forward.xml.i b/interface-definitions/include/firewall/ipv4-hook-forward.xml.i
index de2c70482..b0e240a03 100644
--- a/interface-definitions/include/firewall/ipv4-hook-forward.xml.i
+++ b/interface-definitions/include/firewall/ipv4-hook-forward.xml.i
@@ -28,6 +28,7 @@
#include <include/firewall/action-forward.xml.i>
#include <include/firewall/common-rule-ipv4.xml.i>
#include <include/firewall/inbound-interface.xml.i>
+ #include <include/firewall/match-ipsec.xml.i>
#include <include/firewall/offload-target.xml.i>
#include <include/firewall/outbound-interface.xml.i>
</children>
diff --git a/interface-definitions/include/firewall/ipv4-hook-input.xml.i b/interface-definitions/include/firewall/ipv4-hook-input.xml.i
index 5d32657ea..cefb1ffa7 100644
--- a/interface-definitions/include/firewall/ipv4-hook-input.xml.i
+++ b/interface-definitions/include/firewall/ipv4-hook-input.xml.i
@@ -27,6 +27,7 @@
<children>
#include <include/firewall/common-rule-ipv4.xml.i>
#include <include/firewall/inbound-interface.xml.i>
+ #include <include/firewall/match-ipsec.xml.i>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/include/firewall/ipv6-custom-name.xml.i b/interface-definitions/include/firewall/ipv6-custom-name.xml.i
index 5748b3927..fb8740c38 100644
--- a/interface-definitions/include/firewall/ipv6-custom-name.xml.i
+++ b/interface-definitions/include/firewall/ipv6-custom-name.xml.i
@@ -33,6 +33,7 @@
<children>
#include <include/firewall/common-rule-ipv6.xml.i>
#include <include/firewall/inbound-interface.xml.i>
+ #include <include/firewall/match-ipsec.xml.i>
#include <include/firewall/offload-target.xml.i>
#include <include/firewall/outbound-interface.xml.i>
</children>
diff --git a/interface-definitions/include/firewall/ipv6-hook-forward.xml.i b/interface-definitions/include/firewall/ipv6-hook-forward.xml.i
index b53f09f59..7efc2614e 100644
--- a/interface-definitions/include/firewall/ipv6-hook-forward.xml.i
+++ b/interface-definitions/include/firewall/ipv6-hook-forward.xml.i
@@ -28,6 +28,7 @@
#include <include/firewall/action-forward.xml.i>
#include <include/firewall/common-rule-ipv6.xml.i>
#include <include/firewall/inbound-interface.xml.i>
+ #include <include/firewall/match-ipsec.xml.i>
#include <include/firewall/offload-target.xml.i>
#include <include/firewall/outbound-interface.xml.i>
</children>
diff --git a/interface-definitions/include/firewall/ipv6-hook-input.xml.i b/interface-definitions/include/firewall/ipv6-hook-input.xml.i
index 493611fb1..e1f41e64c 100644
--- a/interface-definitions/include/firewall/ipv6-hook-input.xml.i
+++ b/interface-definitions/include/firewall/ipv6-hook-input.xml.i
@@ -27,6 +27,7 @@
<children>
#include <include/firewall/common-rule-ipv6.xml.i>
#include <include/firewall/inbound-interface.xml.i>
+ #include <include/firewall/match-ipsec.xml.i>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/include/firewall/match-ipsec.xml.i b/interface-definitions/include/firewall/match-ipsec.xml.i
new file mode 100644
index 000000000..82c2b324d
--- /dev/null
+++ b/interface-definitions/include/firewall/match-ipsec.xml.i
@@ -0,0 +1,21 @@
+<!-- include start from firewall/match-ipsec.xml.i -->
+<node name="ipsec">
+ <properties>
+ <help>Inbound IPsec packets</help>
+ </properties>
+ <children>
+ <leafNode name="match-ipsec">
+ <properties>
+ <help>Inbound IPsec packets</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="match-none">
+ <properties>
+ <help>Inbound non-IPsec packets</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/haproxy/rule-backend.xml.i b/interface-definitions/include/haproxy/rule-backend.xml.i
index a6832d693..b2be4fde4 100644
--- a/interface-definitions/include/haproxy/rule-backend.xml.i
+++ b/interface-definitions/include/haproxy/rule-backend.xml.i
@@ -118,7 +118,7 @@
<description>Exactly URL</description>
</valueHelp>
<constraint>
- <regex>^\/[\w\-.\/]+$</regex>
+ <regex>^\/[\w\-.\/]*$</regex>
</constraint>
<constraintErrorMessage>Incorrect URL format</constraintErrorMessage>
<multi/>
diff --git a/interface-definitions/include/version/bgp-version.xml.i b/interface-definitions/include/version/bgp-version.xml.i
index 1386ea9bc..6bed7189f 100644
--- a/interface-definitions/include/version/bgp-version.xml.i
+++ b/interface-definitions/include/version/bgp-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/bgp-version.xml.i -->
-<syntaxVersion component='bgp' version='4'></syntaxVersion>
+<syntaxVersion component='bgp' version='5'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/system_option.xml.in b/interface-definitions/system_option.xml.in
index adb45bdcc..602d7d100 100644
--- a/interface-definitions/system_option.xml.in
+++ b/interface-definitions/system_option.xml.in
@@ -32,6 +32,19 @@
<constraintErrorMessage>Must be ignore, reboot, or poweroff</constraintErrorMessage>
</properties>
</leafNode>
+ <node name="kernel">
+ <properties>
+ <help>Kernel boot parameters</help>
+ </properties>
+ <children>
+ <leafNode name="disable-mitigations">
+ <properties>
+ <help>Disable all optional CPU mitigations</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
<leafNode name="keyboard-layout">
<properties>
<help>System keyboard layout, type ISO2</help>
diff --git a/op-mode-definitions/rpki.xml.in b/op-mode-definitions/rpki.xml.in
index 72d378b88..9e0f83e20 100644
--- a/op-mode-definitions/rpki.xml.in
+++ b/op-mode-definitions/rpki.xml.in
@@ -7,6 +7,15 @@
<help>Show RPKI (Resource Public Key Infrastructure) information</help>
</properties>
<children>
+ <tagNode name="as-number">
+ <properties>
+ <help>Lookup by ASN in prefix table</help>
+ <completionHelp>
+ <list>&lt;ASNUM&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </tagNode>
<leafNode name="cache-connection">
<properties>
<help>Show RPKI cache connections</help>
@@ -19,6 +28,26 @@
</properties>
<command>vtysh -c "show rpki cache-server"</command>
</leafNode>
+ <tagNode name="prefix">
+ <properties>
+ <help>Lookup IP prefix and optionally ASN in prefix table</help>
+ <completionHelp>
+ <list>&lt;x.x.x.x/x&gt; &lt;h:h:h:h:h:h:h:h/x&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ <tagNode name="as-number">
+ <properties>
+ <help>AS Number</help>
+ <completionHelp>
+ <list>&lt;ASNUM&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $(echo $@ | sed -e "s/as-number //g")</command>
+ </tagNode>
+ </children>
+ </tagNode>
<leafNode name="prefix-table">
<properties>
<help>Show RPKI-validated prefixes</help>
diff --git a/python/vyos/remote.py b/python/vyos/remote.py
index b1efcd10b..830770d11 100644
--- a/python/vyos/remote.py
+++ b/python/vyos/remote.py
@@ -148,7 +148,7 @@ class FtpC:
# Almost all FTP servers support the `SIZE' command.
size = conn.size(self.path)
if self.check_space:
- check_storage(path, size)
+ check_storage(location, size)
# No progressbar if we can't determine the size or if the file is too small.
if self.progressbar and size and size > CHUNK_SIZE:
with Progressbar(CHUNK_SIZE / size) as p:
diff --git a/python/vyos/system/compat.py b/python/vyos/system/compat.py
index 436da14e8..37b834ad6 100644
--- a/python/vyos/system/compat.py
+++ b/python/vyos/system/compat.py
@@ -1,4 +1,4 @@
-# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2023-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
@@ -170,9 +170,12 @@ def prune_vyos_versions(root_dir: str = '') -> None:
if not root_dir:
root_dir = disk.find_persistence()
- for version in grub.version_list():
+ version_files = Path(f'{root_dir}/{grub.GRUB_DIR_VYOS_VERS}').glob('*.cfg')
+
+ for file in version_files:
+ version = Path(file).stem
if not Path(f'{root_dir}/boot/{version}').is_dir():
- grub.version_del(version)
+ grub.version_del(version, root_dir)
def update_cfg_ver(root_dir:str = '') -> int:
@@ -246,13 +249,17 @@ def update_version_list(root_dir: str = '') -> list[dict]:
menu_entries = list(filter(lambda x: x.get('version') != ver,
menu_entries))
+ # reset boot_opts in case of config update
+ for entry in menu_entries:
+ entry['boot_opts'] = grub.get_boot_opts(entry['version'])
+
add = list(set(current_versions) - set(menu_versions))
for ver in add:
last = menu_entries[0].get('version')
new = deepcopy(list(filter(lambda x: x.get('version') == last,
menu_entries)))
for e in new:
- boot_opts = e.get('boot_opts').replace(last, ver)
+ boot_opts = grub.get_boot_opts(ver)
e.update({'version': ver, 'boot_opts': boot_opts})
menu_entries = new + menu_entries
diff --git a/python/vyos/system/grub.py b/python/vyos/system/grub.py
index 781962dd0..2e8b20972 100644
--- a/python/vyos/system/grub.py
+++ b/python/vyos/system/grub.py
@@ -1,4 +1,4 @@
-# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2023-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
@@ -45,10 +45,14 @@ TMPL_GRUB_MODULES: str = 'grub/grub_modules.j2'
TMPL_GRUB_OPTS: str = 'grub/grub_options.j2'
TMPL_GRUB_COMMON: str = 'grub/grub_common.j2'
+# default boot options
+BOOT_OPTS_STEM: str = 'boot=live rootdelay=5 noautologin net.ifnames=0 biosdevname=0 vyos-union=/boot/'
+
# prepare regexes
REGEX_GRUB_VARS: str = r'^set (?P<variable_name>.+)=[\'"]?(?P<variable_value>.*)(?<![\'"])[\'"]?$'
REGEX_GRUB_MODULES: str = r'^insmod (?P<module_name>.+)$'
REGEX_KERNEL_CMDLINE: str = r'^BOOT_IMAGE=/(?P<boot_type>boot|live)/((?P<image_version>.+)/)?vmlinuz.*$'
+REGEX_GRUB_BOOT_OPTS: str = r'^\s*set boot_opts="(?P<boot_opts>[^$]+)"$'
def install(drive_path: str, boot_dir: str, efi_dir: str, id: str = 'VyOS') -> None:
@@ -95,7 +99,8 @@ def gen_version_uuid(version_name: str) -> str:
def version_add(version_name: str,
root_dir: str = '',
- boot_opts: str = '') -> None:
+ boot_opts: str = '',
+ boot_opts_config = None) -> None:
"""Add a new VyOS version to GRUB loader configuration
Args:
@@ -112,7 +117,9 @@ def version_add(version_name: str,
version_config, TMPL_VYOS_VERSION, {
'version_name': version_name,
'version_uuid': gen_version_uuid(version_name),
- 'boot_opts': boot_opts
+ 'boot_opts_default': BOOT_OPTS_STEM + version_name,
+ 'boot_opts': boot_opts,
+ 'boot_opts_config': boot_opts_config
})
@@ -294,12 +301,43 @@ def vars_write(grub_cfg: str, grub_vars: dict[str, str]) -> None:
"""
render(grub_cfg, TMPL_GRUB_VARS, {'vars': grub_vars})
+def get_boot_opts(version_name: str, root_dir: str = '') -> str:
+ """Read boot_opts setting from version file; return default setting on
+ any failure.
+
+ Args:
+ version_name (str): version name
+ root_dir (str, optional): an optional path to the root directory.
+ Defaults to empty.
+ """
+ if not root_dir:
+ root_dir = disk.find_persistence()
+
+ boot_opts_default: str = BOOT_OPTS_STEM + version_name
+ boot_opts: str = ''
+ regex_filter = re_compile(REGEX_GRUB_BOOT_OPTS)
+ version_config: str = f'{root_dir}/{GRUB_DIR_VYOS_VERS}/{version_name}.cfg'
+ try:
+ config_text: list[str] = Path(version_config).read_text().splitlines()
+ except FileNotFoundError:
+ return boot_opts_default
+ for line in config_text:
+ search_result = regex_filter.fullmatch(line)
+ if search_result:
+ search_dict = search_result.groupdict()
+ boot_opts = search_dict.get('boot_opts', '')
+ break
+
+ if not boot_opts:
+ boot_opts = boot_opts_default
+
+ return boot_opts
def set_default(version_name: str, root_dir: str = '') -> None:
"""Set version as default boot entry
Args:
- version_name (str): versio name
+ version_name (str): version name
root_dir (str, optional): an optional path to the root directory.
Defaults to empty.
"""
@@ -369,3 +407,18 @@ def set_console_speed(console_speed: str, root_dir: str = '') -> None:
vars_current: dict[str, str] = vars_read(vars_file)
vars_current['console_speed'] = str(console_speed)
vars_write(vars_file, vars_current)
+
+def set_kernel_cmdline_options(cmdline_options: str, version_name: str,
+ root_dir: str = '') -> None:
+ """Write additional cmdline options to GRUB configuration
+
+ Args:
+ cmdline_options (str): cmdline options to add to default boot line
+ version_name (str): image version name
+ root_dir (str, optional): an optional path to the root directory.
+ """
+ if not root_dir:
+ root_dir = disk.find_persistence()
+
+ version_add(version_name=version_name, root_dir=root_dir,
+ boot_opts_config=cmdline_options)
diff --git a/python/vyos/system/grub_util.py b/python/vyos/system/grub_util.py
index 9e79d41d4..4a3d8795e 100644
--- a/python/vyos/system/grub_util.py
+++ b/python/vyos/system/grub_util.py
@@ -13,7 +13,7 @@
# 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 vyos.system import disk, grub, compat
+from vyos.system import disk, grub, image, compat
@compat.grub_cfg_update
def set_console_speed(console_speed: str, root_dir: str = '') -> None:
@@ -29,6 +29,7 @@ def set_console_speed(console_speed: str, root_dir: str = '') -> None:
grub.set_console_speed(console_speed, root_dir)
+@image.if_not_live_boot
def update_console_speed(console_speed: str, root_dir: str = '') -> None:
"""Update console_speed if different from current value"""
@@ -40,3 +41,30 @@ def update_console_speed(console_speed: str, root_dir: str = '') -> None:
console_speed_current = vars_current.get('console_speed', None)
if console_speed != console_speed_current:
set_console_speed(console_speed, root_dir)
+
+@compat.grub_cfg_update
+def set_kernel_cmdline_options(cmdline_options: str, version: str = '',
+ root_dir: str = '') -> None:
+ """Write Kernel CLI cmdline options to GRUB configuration"""
+ if not root_dir:
+ root_dir = disk.find_persistence()
+
+ if not version:
+ version = image.get_running_image()
+
+ grub.set_kernel_cmdline_options(cmdline_options, version, root_dir)
+
+@image.if_not_live_boot
+def update_kernel_cmdline_options(cmdline_options: str,
+ root_dir: str = '') -> None:
+ """Update Kernel custom cmdline options"""
+ if not root_dir:
+ root_dir = disk.find_persistence()
+
+ version = image.get_running_image()
+
+ boot_opts_current = grub.get_boot_opts(version, root_dir)
+ boot_opts_proposed = grub.BOOT_OPTS_STEM + f'{version} {cmdline_options}'
+
+ if boot_opts_proposed != boot_opts_current:
+ set_kernel_cmdline_options(cmdline_options, version, root_dir)
diff --git a/python/vyos/system/image.py b/python/vyos/system/image.py
index 514275654..5460e6a36 100644
--- a/python/vyos/system/image.py
+++ b/python/vyos/system/image.py
@@ -1,4 +1,4 @@
-# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2023-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
@@ -15,6 +15,7 @@
from pathlib import Path
from re import compile as re_compile
+from functools import wraps
from tempfile import TemporaryDirectory
from typing import TypedDict
@@ -262,6 +263,16 @@ def is_live_boot() -> bool:
return True
return False
+def if_not_live_boot(func):
+ """Decorator to call function only if not live boot"""
+ @wraps(func)
+ def wrapper(*args, **kwargs):
+ if not is_live_boot():
+ ret = func(*args, **kwargs)
+ return ret
+ return None
+ return wrapper
+
def is_running_as_container() -> bool:
if Path('/.dockerenv').exists():
return True
diff --git a/smoketest/scripts/cli/test_protocols_bfd.py b/smoketest/scripts/cli/test_protocols_bfd.py
index f209eae3a..716d0a806 100755
--- a/smoketest/scripts/cli/test_protocols_bfd.py
+++ b/smoketest/scripts/cli/test_protocols_bfd.py
@@ -32,6 +32,7 @@ peers = {
'multihop' : '',
'source_addr': '192.0.2.254',
'profile' : 'foo-bar-baz',
+ 'minimum_ttl': '20',
},
'192.0.2.20' : {
'echo_mode' : '',
@@ -63,6 +64,7 @@ profiles = {
'intv_rx' : '222',
'intv_tx' : '333',
'shutdown' : '',
+ 'minimum_ttl': '40',
},
'foo-bar-baz' : {
'intv_mult' : '4',
@@ -109,6 +111,8 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['peer', peer, 'interval', 'receive', peer_config["intv_rx"]])
if 'intv_tx' in peer_config:
self.cli_set(base_path + ['peer', peer, 'interval', 'transmit', peer_config["intv_tx"]])
+ if 'minimum_ttl' in peer_config:
+ self.cli_set(base_path + ['peer', peer, 'minimum-ttl', peer_config["minimum_ttl"]])
if 'multihop' in peer_config:
self.cli_set(base_path + ['peer', peer, 'multihop'])
if 'passive' in peer_config:
@@ -152,6 +156,8 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'receive-interval {peer_config["intv_rx"]}', peerconfig)
if 'intv_tx' in peer_config:
self.assertIn(f'transmit-interval {peer_config["intv_tx"]}', peerconfig)
+ if 'minimum_ttl' in peer_config:
+ self.assertIn(f'minimum-ttl {peer_config["minimum_ttl"]}', peerconfig)
if 'passive' in peer_config:
self.assertIn(f'passive-mode', peerconfig)
if 'shutdown' in peer_config:
@@ -173,6 +179,8 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['profile', profile, 'interval', 'receive', profile_config["intv_rx"]])
if 'intv_tx' in profile_config:
self.cli_set(base_path + ['profile', profile, 'interval', 'transmit', profile_config["intv_tx"]])
+ if 'minimum_ttl' in profile_config:
+ self.cli_set(base_path + ['profile', profile, 'minimum-ttl', profile_config["minimum_ttl"]])
if 'passive' in profile_config:
self.cli_set(base_path + ['profile', profile, 'passive'])
if 'shutdown' in profile_config:
@@ -210,6 +218,8 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' receive-interval {profile_config["intv_rx"]}', config)
if 'intv_tx' in profile_config:
self.assertIn(f' transmit-interval {profile_config["intv_tx"]}', config)
+ if 'minimum_ttl' in profile_config:
+ self.assertIn(f' minimum-ttl {profile_config["minimum_ttl"]}', config)
if 'passive' in profile_config:
self.assertIn(f' passive-mode', config)
if 'shutdown' in profile_config:
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/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/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/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)