summaryrefslogtreecommitdiff
path: root/python/vyos
diff options
context:
space:
mode:
Diffstat (limited to 'python/vyos')
-rw-r--r--python/vyos/config_mgmt.py32
-rw-r--r--python/vyos/ifconfig/vxlan.py66
-rw-r--r--python/vyos/utils/network.py45
3 files changed, 126 insertions, 17 deletions
diff --git a/python/vyos/config_mgmt.py b/python/vyos/config_mgmt.py
index 0fc72e660..dbf17ade4 100644
--- a/python/vyos/config_mgmt.py
+++ b/python/vyos/config_mgmt.py
@@ -25,12 +25,14 @@ from datetime import datetime
from textwrap import dedent
from pathlib import Path
from tabulate import tabulate
+from shutil import copy
from vyos.config import Config
from vyos.configtree import ConfigTree, ConfigTreeError, show_diff
from vyos.defaults import directories
from vyos.version import get_full_version_data
from vyos.utils.io import ask_yes_no
+from vyos.utils.boot import boot_configuration_complete
from vyos.utils.process import is_systemd_service_active
from vyos.utils.process import rc_cmd
@@ -200,9 +202,9 @@ Proceed ?'''
raise ConfigMgmtError(out)
entry = self._read_tmp_log_entry()
- self._add_log_entry(**entry)
if self._archive_active_config():
+ self._add_log_entry(**entry)
self._update_archive()
msg = 'Reboot timer stopped'
@@ -223,8 +225,6 @@ Proceed ?'''
def rollback(self, rev: int, no_prompt: bool=False) -> Tuple[str,int]:
"""Reboot to config revision 'rev'.
"""
- from shutil import copy
-
msg = ''
if not self._check_revision_number(rev):
@@ -334,10 +334,10 @@ Proceed ?'''
user = self._get_user()
via = 'init'
comment = ''
- self._add_log_entry(user, via, comment)
# add empty init config before boot-config load for revision
# and diff consistency
if self._archive_active_config():
+ self._add_log_entry(user, via, comment)
self._update_archive()
os.umask(mask)
@@ -352,9 +352,8 @@ Proceed ?'''
self._new_log_entry(tmp_file=tmp_log_entry)
return
- self._add_log_entry()
-
if self._archive_active_config():
+ self._add_log_entry()
self._update_archive()
def commit_archive(self):
@@ -475,22 +474,26 @@ Proceed ?'''
conf_file.chmod(0o644)
def _archive_active_config(self) -> bool:
+ save_to_tmp = (boot_configuration_complete() or not
+ os.path.isfile(archive_config_file))
mask = os.umask(0o113)
ext = os.getpid()
- tmp_save = f'/tmp/config.boot.{ext}'
- save_config(tmp_save)
+ cmp_saved = f'/tmp/config.boot.{ext}'
+ if save_to_tmp:
+ save_config(cmp_saved)
+ else:
+ copy(config_file, cmp_saved)
try:
- if cmp(tmp_save, archive_config_file, shallow=False):
- # this will be the case on boot, as well as certain
- # re-initialiation instances after delete/set
- os.unlink(tmp_save)
+ if cmp(cmp_saved, archive_config_file, shallow=False):
+ os.unlink(cmp_saved)
+ os.umask(mask)
return False
except FileNotFoundError:
pass
- rc, out = rc_cmd(f'sudo mv {tmp_save} {archive_config_file}')
+ rc, out = rc_cmd(f'sudo mv {cmp_saved} {archive_config_file}')
os.umask(mask)
if rc != 0:
@@ -522,9 +525,8 @@ Proceed ?'''
return len(l)
def _check_revision_number(self, rev: int) -> bool:
- # exclude init revision:
maxrev = self._get_number_of_revisions()
- if not 0 <= rev < maxrev - 1:
+ if not 0 <= rev < maxrev:
return False
return True
diff --git a/python/vyos/ifconfig/vxlan.py b/python/vyos/ifconfig/vxlan.py
index 6a9911588..1fe5db7cd 100644
--- a/python/vyos/ifconfig/vxlan.py
+++ b/python/vyos/ifconfig/vxlan.py
@@ -1,4 +1,4 @@
-# Copyright 2019-2022 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2023 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
@@ -13,9 +13,15 @@
# 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 json import loads
+
from vyos import ConfigError
+from vyos.configdict import list_diff
from vyos.ifconfig import Interface
+from vyos.utils.assertion import assert_list
from vyos.utils.dict import dict_search
+from vyos.utils.network import get_interface_config
+from vyos.utils.network import get_vxlan_vlan_tunnels
@Interface.register
class VXLANIf(Interface):
@@ -49,6 +55,13 @@ class VXLANIf(Interface):
}
}
+ _command_set = {**Interface._command_set, **{
+ 'vlan_tunnel': {
+ 'validate': lambda v: assert_list(v, ['on', 'off']),
+ 'shellcmd': 'bridge link set dev {ifname} vlan_tunnel {value}',
+ },
+ }}
+
def _create(self):
# This table represents a mapping from VyOS internal config dict to
# arguments used by iproute2. For more information please refer to:
@@ -99,3 +112,54 @@ class VXLANIf(Interface):
cmd = f'bridge fdb append to 00:00:00:00:00:00 dst {remote} ' \
'port {port} dev {ifname}'
self._cmd(cmd.format(**self.config))
+
+ def set_vlan_vni_mapping(self, state):
+ """
+ Controls whether vlan to tunnel mapping is enabled on the port.
+ By default this flag is off.
+ """
+ if not isinstance(state, bool):
+ raise ValueError('Value out of range')
+
+ cur_vlan_ids = []
+ if 'vlan_to_vni_removed' in self.config:
+ cur_vlan_ids = self.config['vlan_to_vni_removed']
+ for vlan in cur_vlan_ids:
+ self._cmd(f'bridge vlan del dev {self.ifname} vid {vlan}')
+
+ # Determine current OS Kernel vlan_tunnel setting - only adjust when needed
+ tmp = get_interface_config(self.ifname)
+ cur_state = 'on' if dict_search(f'linkinfo.info_slave_data.vlan_tunnel', tmp) == True else 'off'
+ new_state = 'on' if state else 'off'
+ if cur_state != new_state:
+ self.set_interface('vlan_tunnel', new_state)
+
+ # Determine current OS Kernel configured VLANs
+ os_configured_vlan_ids = get_vxlan_vlan_tunnels(self.ifname)
+
+ if 'vlan_to_vni' in self.config:
+ add_vlan = list_diff(list(self.config['vlan_to_vni'].keys()), os_configured_vlan_ids)
+
+ for vlan, vlan_config in self.config['vlan_to_vni'].items():
+ # VLAN mapping already exists - skip
+ if vlan not in add_vlan:
+ continue
+
+ vni = vlan_config['vni']
+ # The following commands must be run one after another,
+ # they can not be combined with linux 6.1 and iproute2 6.1
+ self._cmd(f'bridge vlan add dev {self.ifname} vid {vlan}')
+ self._cmd(f'bridge vlan add dev {self.ifname} vid {vlan} tunnel_info id {vni}')
+
+ def update(self, config):
+ """ General helper function which works on a dictionary retrived by
+ get_config_dict(). It's main intention is to consolidate the scattered
+ interface setup code and provide a single point of entry when workin
+ on any interface. """
+
+ # call base class last
+ super().update(config)
+
+ # Enable/Disable VLAN tunnel mapping
+ # This is only possible after the interface was assigned to the bridge
+ self.set_vlan_vni_mapping(dict_search('vlan_to_vni', config) != None)
diff --git a/python/vyos/utils/network.py b/python/vyos/utils/network.py
index bc6899e45..c96ee4eed 100644
--- a/python/vyos/utils/network.py
+++ b/python/vyos/utils/network.py
@@ -429,7 +429,7 @@ def is_subnet_connected(subnet, primary=False):
return False
-def is_afi_configured(interface, afi):
+def is_afi_configured(interface: str, afi):
""" Check if given address family is configured, or in other words - an IP
address is assigned to the interface. """
from netifaces import ifaddresses
@@ -446,3 +446,46 @@ def is_afi_configured(interface, afi):
return False
return afi in addresses
+
+def get_vxlan_vlan_tunnels(interface: str) -> list:
+ """ Return a list of strings with VLAN IDs configured in the Kernel """
+ from json import loads
+ from vyos.utils.process import cmd
+
+ if not interface.startswith('vxlan'):
+ raise ValueError('Only applicable for VXLAN interfaces!')
+
+ # Determine current OS Kernel configured VLANs
+ #
+ # $ bridge -j -p vlan tunnelshow dev vxlan0
+ # [ {
+ # "ifname": "vxlan0",
+ # "tunnels": [ {
+ # "vlan": 10,
+ # "vlanEnd": 11,
+ # "tunid": 10010,
+ # "tunidEnd": 10011
+ # },{
+ # "vlan": 20,
+ # "tunid": 10020
+ # } ]
+ # } ]
+ #
+ os_configured_vlan_ids = []
+ tmp = loads(cmd(f'bridge --json vlan tunnelshow dev {interface}'))
+ if tmp:
+ for tunnel in tmp[0].get('tunnels', {}):
+ vlanStart = tunnel['vlan']
+ if 'vlanEnd' in tunnel:
+ vlanEnd = tunnel['vlanEnd']
+ # Build a real list for user VLAN IDs
+ vlan_list = list(range(vlanStart, vlanEnd +1))
+ # Convert list of integers to list or strings
+ os_configured_vlan_ids.extend(map(str, vlan_list))
+ # Proceed with next tunnel - this one is complete
+ continue
+
+ # Add single tunel id - not part of a range
+ os_configured_vlan_ids.append(str(vlanStart))
+
+ return os_configured_vlan_ids