summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorRobert <rgingras@mieweb.com>2025-02-10 10:51:12 -0500
committerGitHub <noreply@github.com>2025-02-10 10:51:12 -0500
commitf400eec4f40a814baba82f8a6d5755bdfd69f120 (patch)
tree729d57c54de53bc7d2f97d436496dd636d5b4045 /python
parenta24d2f87fdde466625d9b6173657f07cf4401f30 (diff)
parent41e9b8b362faedc0c20316255d59d7f90e848a4c (diff)
downloadvyos-1x-f400eec4f40a814baba82f8a6d5755bdfd69f120.tar.gz
vyos-1x-f400eec4f40a814baba82f8a6d5755bdfd69f120.zip
Merge branch 'current' into T7095_vrf-fix
Diffstat (limited to 'python')
-rw-r--r--python/vyos/configdict.py12
-rw-r--r--python/vyos/configsession.py4
-rw-r--r--python/vyos/defaults.py7
-rw-r--r--python/vyos/ifconfig/interface.py69
-rw-r--r--python/vyos/ifconfig/wireguard.py5
-rw-r--r--python/vyos/utils/cpu.py15
-rw-r--r--python/vyos/utils/misc.py2
-rw-r--r--python/vyos/utils/process.py2
-rwxr-xr-xpython/vyos/xml_ref/generate_op_cache.py95
9 files changed, 127 insertions, 84 deletions
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index 5a353b110..78b98a3eb 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -491,10 +491,8 @@ def get_interface_dict(config, base, ifname='', recursive_defaults=True, with_pk
# Check if any DHCP options changed which require a client restat
dhcp = is_node_changed(config, base + [ifname, 'dhcp-options'])
if dhcp: dict.update({'dhcp_options_changed' : {}})
-
- # Changine interface VRF assignemnts require a DHCP restart, too
- dhcp = is_node_changed(config, base + [ifname, 'vrf'])
- if dhcp: dict.update({'dhcp_options_changed' : {}})
+ dhcpv6 = is_node_changed(config, base + [ifname, 'dhcpv6-options'])
+ if dhcpv6: dict.update({'dhcpv6_options_changed' : {}})
# Some interfaces come with a source_interface which must also not be part
# of any other bond or bridge interface as it is exclusivly assigned as the
@@ -543,6 +541,8 @@ def get_interface_dict(config, base, ifname='', recursive_defaults=True, with_pk
# Check if any DHCP options changed which require a client restat
dhcp = is_node_changed(config, base + [ifname, 'vif', vif, 'dhcp-options'])
if dhcp: dict['vif'][vif].update({'dhcp_options_changed' : {}})
+ dhcpv6 = is_node_changed(config, base + [ifname, 'vif', vif, 'dhcpv6-options'])
+ if dhcpv6: dict['vif'][vif].update({'dhcpv6_options_changed' : {}})
for vif_s, vif_s_config in dict.get('vif_s', {}).items():
# Add subinterface name to dictionary
@@ -569,6 +569,8 @@ def get_interface_dict(config, base, ifname='', recursive_defaults=True, with_pk
# Check if any DHCP options changed which require a client restat
dhcp = is_node_changed(config, base + [ifname, 'vif-s', vif_s, 'dhcp-options'])
if dhcp: dict['vif_s'][vif_s].update({'dhcp_options_changed' : {}})
+ dhcpv6 = is_node_changed(config, base + [ifname, 'vif-s', vif_s, 'dhcpv6-options'])
+ if dhcpv6: dict['vif_s'][vif_s].update({'dhcpv6_options_changed' : {}})
for vif_c, vif_c_config in vif_s_config.get('vif_c', {}).items():
# Add subinterface name to dictionary
@@ -597,6 +599,8 @@ def get_interface_dict(config, base, ifname='', recursive_defaults=True, with_pk
# Check if any DHCP options changed which require a client restat
dhcp = is_node_changed(config, base + [ifname, 'vif-s', vif_s, 'vif-c', vif_c, 'dhcp-options'])
if dhcp: dict['vif_s'][vif_s]['vif_c'][vif_c].update({'dhcp_options_changed' : {}})
+ dhcpv6 = is_node_changed(config, base + [ifname, 'vif-s', vif_s, 'vif-c', vif_c, 'dhcpv6-options'])
+ if dhcpv6: dict['vif_s'][vif_s]['vif_c'][vif_c].update({'dhcpv6_options_changed' : {}})
# Check vif, vif-s/vif-c VLAN interfaces for removal
dict = get_removed_vlans(config, base + [ifname], dict)
diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py
index dd3ad1e3d..90b96b88c 100644
--- a/python/vyos/configsession.py
+++ b/python/vyos/configsession.py
@@ -23,8 +23,8 @@ from vyos.utils.process import is_systemd_service_running
from vyos.utils.dict import dict_to_paths
CLI_SHELL_API = '/bin/cli-shell-api'
-SET = '/usr/libexec/vyos/vyconf/vy_set'
-DELETE = '/usr/libexec/vyos/vyconf/vy_delete'
+SET = '/opt/vyatta/sbin/my_set'
+DELETE = '/opt/vyatta/sbin/my_delete'
COMMENT = '/opt/vyatta/sbin/my_comment'
COMMIT = '/opt/vyatta/sbin/my_commit'
DISCARD = '/opt/vyatta/sbin/my_discard'
diff --git a/python/vyos/defaults.py b/python/vyos/defaults.py
index 9757a34df..89e51707b 100644
--- a/python/vyos/defaults.py
+++ b/python/vyos/defaults.py
@@ -1,4 +1,4 @@
-# Copyright 2018-2024 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2018-2025 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
@@ -40,6 +40,11 @@ directories = {
'ca_certificates' : '/usr/local/share/ca-certificates/vyos'
}
+systemd_services = {
+ 'rsyslog' : 'rsyslog.service',
+ 'snmpd' : 'snmpd.service',
+}
+
config_status = '/tmp/vyos-config-status'
api_config_state = '/run/http-api-state'
frr_debug_enable = '/tmp/vyos.frr.debug'
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index cb73e2597..979b62578 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -595,12 +595,16 @@ class Interface(Control):
"""
Add/Remove interface from given VRF instance.
+ Keyword arguments:
+ vrf: VRF instance name or empty string (default VRF)
+
+ Return True if VRF was changed, False otherwise
+
Example:
>>> from vyos.ifconfig import Interface
>>> Interface('eth0').set_vrf('foo')
>>> Interface('eth0').set_vrf()
"""
-
# Don't allow for netns yet
if 'netns' in self.config:
return False
@@ -611,21 +615,33 @@ class Interface(Control):
# Get current VRF table ID
old_vrf_tableid = get_vrf_tableid(self.ifname)
- self.set_interface('vrf', vrf)
+ # Always stop the DHCP client process to clean up routes within the VRF
+ # where the process was originally started. There is no need to add a
+ # condition to only call the method if "address dhcp" was defined, as
+ # this is handled inside set_dhcp(v6) by only stopping if the daemon is
+ # running. DHCP client process restart will be handled later on once the
+ # interface is moved to the new VRF.
+ self.set_dhcp(False)
+ self.set_dhcpv6(False)
+
+ # Move interface in/out of VRF
+ self.set_interface('vrf', vrf)
if vrf:
# Get routing table ID number for VRF
vrf_table_id = get_vrf_tableid(vrf)
# Add map element with interface and zone ID
- if vrf_table_id:
+ if vrf_table_id and old_vrf_tableid != vrf_table_id:
# delete old table ID from nftables if it has changed, e.g. interface moved to a different VRF
- if old_vrf_tableid and old_vrf_tableid != int(vrf_table_id):
- self._del_interface_from_ct_iface_map()
+ self._del_interface_from_ct_iface_map()
self._add_interface_to_ct_iface_map(vrf_table_id)
+ return True
else:
- self._del_interface_from_ct_iface_map()
+ if old_vrf_tableid != get_vrf_tableid(self.ifname):
+ self._del_interface_from_ct_iface_map()
+ return True
- return True
+ return False
def set_arp_cache_tmo(self, tmo):
"""
@@ -1181,7 +1197,7 @@ class Interface(Control):
"""
return self.get_addr_v4() + self.get_addr_v6()
- def add_addr(self, addr):
+ def add_addr(self, addr: str, vrf_changed: bool=False) -> bool:
"""
Add IP(v6) address to interface. Address is only added if it is not
already assigned to that interface. Address format must be validated
@@ -1214,15 +1230,14 @@ class Interface(Control):
# add to interface
if addr == 'dhcp':
- self.set_dhcp(True)
+ self.set_dhcp(True, vrf_changed=vrf_changed)
elif addr == 'dhcpv6':
- self.set_dhcpv6(True)
+ self.set_dhcpv6(True, vrf_changed=vrf_changed)
elif not is_intf_addr_assigned(self.ifname, addr, netns=netns):
netns_cmd = f'ip netns exec {netns}' if netns else ''
tmp = f'{netns_cmd} ip addr add {addr} dev {self.ifname}'
# Add broadcast address for IPv4
if is_ipv4(addr): tmp += ' brd +'
-
self._cmd(tmp)
else:
return False
@@ -1232,7 +1247,7 @@ class Interface(Control):
return True
- def del_addr(self, addr):
+ def del_addr(self, addr: str) -> bool:
"""
Delete IP(v6) address from interface. Address is only deleted if it is
assigned to that interface. Address format must be exactly the same as
@@ -1356,7 +1371,7 @@ class Interface(Control):
cmd = f'bridge vlan add dev {ifname} vid {native_vlan_id} pvid untagged master'
self._cmd(cmd)
- def set_dhcp(self, enable):
+ def set_dhcp(self, enable: bool, vrf_changed: bool=False):
"""
Enable/Disable DHCP client on a given interface.
"""
@@ -1396,7 +1411,9 @@ class Interface(Control):
# the old lease is released a new one is acquired (T4203). We will
# only restart DHCP client if it's option changed, or if it's not
# running, but it should be running (e.g. on system startup)
- if 'dhcp_options_changed' in self.config or not is_systemd_service_active(systemd_service):
+ if (vrf_changed or
+ ('dhcp_options_changed' in self.config) or
+ (not is_systemd_service_active(systemd_service))):
return self._cmd(f'systemctl restart {systemd_service}')
else:
if is_systemd_service_active(systemd_service):
@@ -1423,7 +1440,7 @@ class Interface(Control):
return None
- def set_dhcpv6(self, enable):
+ def set_dhcpv6(self, enable: bool, vrf_changed: bool=False):
"""
Enable/Disable DHCPv6 client on a given interface.
"""
@@ -1452,7 +1469,10 @@ class Interface(Control):
# We must ignore any return codes. This is required to enable
# DHCPv6-PD for interfaces which are yet not up and running.
- return self._popen(f'systemctl restart {systemd_service}')
+ if (vrf_changed or
+ ('dhcpv6_options_changed' in self.config) or
+ (not is_systemd_service_active(systemd_service))):
+ return self._popen(f'systemctl restart {systemd_service}')
else:
if is_systemd_service_active(systemd_service):
self._cmd(f'systemctl stop {systemd_service}')
@@ -1669,30 +1689,31 @@ class Interface(Control):
else:
self.del_addr(addr)
- # start DHCPv6 client when only PD was configured
- if dhcpv6pd:
- self.set_dhcpv6(True)
-
# XXX: Bind interface to given VRF or unbind it if vrf is not set. Unbinding
# will call 'ip link set dev eth0 nomaster' which will also drop the
# interface out of any bridge or bond - thus this is checked before.
+ vrf_changed = False
if 'is_bond_member' in config:
bond_if = next(iter(config['is_bond_member']))
tmp = get_interface_config(config['ifname'])
if 'master' in tmp and tmp['master'] != bond_if:
- self.set_vrf('')
+ vrf_changed = self.set_vrf('')
elif 'is_bridge_member' in config:
bridge_if = next(iter(config['is_bridge_member']))
tmp = get_interface_config(config['ifname'])
if 'master' in tmp and tmp['master'] != bridge_if:
- self.set_vrf('')
+ vrf_changed = self.set_vrf('')
else:
- self.set_vrf(config.get('vrf', ''))
+ vrf_changed = self.set_vrf(config.get('vrf', ''))
+
+ # start DHCPv6 client when only PD was configured
+ if dhcpv6pd:
+ self.set_dhcpv6(True, vrf_changed=vrf_changed)
# Add this section after vrf T4331
for addr in new_addr:
- self.add_addr(addr)
+ self.add_addr(addr, vrf_changed=vrf_changed)
# Configure MSS value for IPv4 TCP connections
tmp = dict_search('ip.adjust_mss', config)
diff --git a/python/vyos/ifconfig/wireguard.py b/python/vyos/ifconfig/wireguard.py
index 341fd32ff..fed7a5f84 100644
--- a/python/vyos/ifconfig/wireguard.py
+++ b/python/vyos/ifconfig/wireguard.py
@@ -14,14 +14,9 @@
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
import os
-import time
-from datetime import timedelta
from tempfile import NamedTemporaryFile
-from hurry.filesize import size
-from hurry.filesize import alternative
-
from vyos.configquery import ConfigTreeQuery
from vyos.ifconfig import Interface
from vyos.ifconfig import Operational
diff --git a/python/vyos/utils/cpu.py b/python/vyos/utils/cpu.py
index 3bea5ac12..8ace77d15 100644
--- a/python/vyos/utils/cpu.py
+++ b/python/vyos/utils/cpu.py
@@ -99,3 +99,18 @@ def get_core_count():
core_count += 1
return core_count
+
+
+def get_available_cpus():
+ """ List of cpus with ids that are available in the system
+ Uses 'lscpu' command
+
+ Returns: list[dict[str, str | int | bool]]: cpus details
+ """
+ import json
+
+ from vyos.utils.process import cmd
+
+ out = json.loads(cmd('lscpu --extended -b --json'))
+
+ return out['cpus']
diff --git a/python/vyos/utils/misc.py b/python/vyos/utils/misc.py
index ac8011b8d..d82655914 100644
--- a/python/vyos/utils/misc.py
+++ b/python/vyos/utils/misc.py
@@ -52,7 +52,7 @@ def install_into_config(conf, config_paths, override_prompt=True):
continue
try:
- cmd(f'/usr/libexec/vyos/vyconf/vy_set {path}')
+ cmd(f'/opt/vyatta/sbin/my_set {path}')
count += 1
except:
failed.append(path)
diff --git a/python/vyos/utils/process.py b/python/vyos/utils/process.py
index faed8f1b0..21335e6b3 100644
--- a/python/vyos/utils/process.py
+++ b/python/vyos/utils/process.py
@@ -83,13 +83,13 @@ def popen(command, flag='', shell=None, input=None, timeout=None, env=None,
use_shell = True
# Must be run as root to execute command in VRF or network namespace
+ wrapper = get_wrapper(vrf, netns)
if vrf or netns:
if os.getuid() != 0:
raise OSError(
'Permission denied: cannot execute commands in VRF and netns contexts as an unprivileged user'
)
- wrapper = get_wrapper(vrf, netns)
if use_shell:
command = f'{shlex.join(wrapper)} {command}'
else:
diff --git a/python/vyos/xml_ref/generate_op_cache.py b/python/vyos/xml_ref/generate_op_cache.py
index cd2ac890e..95779d066 100755
--- a/python/vyos/xml_ref/generate_op_cache.py
+++ b/python/vyos/xml_ref/generate_op_cache.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2024 VyOS maintainers and contributors
+# Copyright (C) 2024-2025 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
@@ -33,9 +33,9 @@ _here = dirname(__file__)
sys.path.append(join(_here, '..'))
from defaults import directories
-from op_definition import NodeData
from op_definition import PathData
+
xml_op_cache_json = 'xml_op_cache.json'
xml_op_tmp = join('/tmp', xml_op_cache_json)
op_ref_cache = abspath(join(_here, 'op_cache.py'))
@@ -74,7 +74,7 @@ def translate_op_script(s: str) -> str:
return s
-def insert_node(n: Element, l: list[PathData], path = None) -> None:
+def insert_node(n: Element, l: list[PathData], path=None) -> None:
# pylint: disable=too-many-locals,too-many-branches
prop: OptElement = n.find('properties')
children: OptElement = n.find('children')
@@ -95,65 +95,67 @@ def insert_node(n: Element, l: list[PathData], path = None) -> None:
if command_text is not None:
command_text = translate_command(command_text, path)
- comp_help = None
+ comp_help = {}
if prop is not None:
- che = prop.findall("completionHelp")
+ che = prop.findall('completionHelp')
+
for c in che:
- lists = c.findall("list")
- paths = c.findall("path")
- scripts = c.findall("script")
-
- comp_help = {}
- list_l = []
- for i in lists:
- list_l.append(i.text)
- path_l = []
- for i in paths:
- path_str = re.sub(r'\s+', '/', i.text)
- path_l.append(path_str)
- script_l = []
- for i in scripts:
- script_str = translate_op_script(i.text)
- script_l.append(script_str)
-
- comp_help['list'] = list_l
- comp_help['fs_path'] = path_l
- comp_help['script'] = script_l
-
- for d in l:
- if name in list(d):
- break
- else:
- d = {}
- l.append(d)
-
- inner_l = d.setdefault(name, [])
-
- inner_d: PathData = {'node_data': NodeData(node_type=node_type,
- help_text=help_text,
- comp_help=comp_help,
- command=command_text,
- path=path)}
- inner_l.append(inner_d)
+ comp_list_els = c.findall('list')
+ comp_path_els = c.findall('path')
+ comp_script_els = c.findall('script')
+
+ comp_lists = []
+ for i in comp_list_els:
+ comp_lists.append(i.text)
+
+ comp_paths = []
+ for i in comp_path_els:
+ comp_paths.append(i.text)
+
+ comp_scripts = []
+ for i in comp_script_els:
+ comp_script_str = translate_op_script(i.text)
+ comp_scripts.append(comp_script_str)
+
+ if comp_lists:
+ comp_help['list'] = comp_lists
+ if comp_paths:
+ comp_help['path'] = comp_paths
+ if comp_scripts:
+ comp_help['script'] = comp_scripts
+
+ cur_node_dict = {}
+ cur_node_dict['name'] = name
+ cur_node_dict['type'] = node_type
+ cur_node_dict['comp_help'] = comp_help
+ cur_node_dict['help'] = help_text
+ cur_node_dict['command'] = command_text
+ cur_node_dict['path'] = path
+ cur_node_dict['children'] = []
+ l.append(cur_node_dict)
if children is not None:
- inner_nodes = children.iterfind("*")
+ inner_nodes = children.iterfind('*')
for inner_n in inner_nodes:
inner_path = path[:]
- insert_node(inner_n, inner_l, inner_path)
+ insert_node(inner_n, cur_node_dict['children'], inner_path)
def parse_file(file_path, l):
tree = ET.parse(file_path)
root = tree.getroot()
- for n in root.iterfind("*"):
+ for n in root.iterfind('*'):
insert_node(n, l)
def main():
parser = ArgumentParser(description='generate dict from xml defintions')
- parser.add_argument('--xml-dir', type=str, required=True,
- help='transcluded xml op-mode-definition file')
+ parser.add_argument(
+ '--xml-dir',
+ type=str,
+ required=True,
+ help='transcluded xml op-mode-definition file',
+ )
args = vars(parser.parse_args())
@@ -170,5 +172,6 @@ def main():
with open(op_ref_cache, 'w') as f:
f.write(f'op_reference = {str(l)}')
+
if __name__ == '__main__':
main()