diff options
Diffstat (limited to 'python/vyos')
-rw-r--r-- | python/vyos/accel_ppp.py | 3 | ||||
-rw-r--r-- | python/vyos/config_mgmt.py | 43 | ||||
-rw-r--r-- | python/vyos/configtree.py | 32 | ||||
-rw-r--r-- | python/vyos/configverify.py | 28 | ||||
-rw-r--r-- | python/vyos/defaults.py | 39 | ||||
-rw-r--r-- | python/vyos/ifconfig/interface.py | 6 | ||||
-rw-r--r-- | python/vyos/ifconfig/loopback.py | 2 | ||||
-rw-r--r-- | python/vyos/ifconfig/tunnel.py | 14 | ||||
-rw-r--r-- | python/vyos/ipsec.py | 141 | ||||
-rw-r--r-- | python/vyos/template.py | 20 |
10 files changed, 264 insertions, 64 deletions
diff --git a/python/vyos/accel_ppp.py b/python/vyos/accel_ppp.py index bfc8ee5a9..0af311e57 100644 --- a/python/vyos/accel_ppp.py +++ b/python/vyos/accel_ppp.py @@ -38,6 +38,9 @@ def get_server_statistics(accel_statistics, pattern, sep=':') -> dict: if key in ['starting', 'active', 'finishing']: stat_dict['sessions'][key] = value.strip() continue + if key == 'cpu': + stat_dict['cpu_load_percentage'] = int(re.sub(r'%', '', value.strip())) + continue stat_dict[key] = value.strip() return stat_dict diff --git a/python/vyos/config_mgmt.py b/python/vyos/config_mgmt.py index 22a49ff50..fade3081c 100644 --- a/python/vyos/config_mgmt.py +++ b/python/vyos/config_mgmt.py @@ -24,7 +24,7 @@ from datetime import datetime from tabulate import tabulate from vyos.config import Config -from vyos.configtree import ConfigTree +from vyos.configtree import ConfigTree, ConfigTreeError, show_diff from vyos.defaults import directories from vyos.util import is_systemd_service_active, ask_yes_no, rc_cmd @@ -93,15 +93,7 @@ class ConfigMgmt: # a call to compare without args is edit_level aware edit_level = os.getenv('VYATTA_EDIT_LEVEL', '') - edit_path = [l for l in edit_level.split('/') if l] - if edit_path: - eff_conf = config.show_config(edit_path, effective=True) - self.edit_level_active_config = ConfigTree(eff_conf) - conf = config.show_config(edit_path) - self.edit_level_working_config = ConfigTree(conf) - else: - self.edit_level_active_config = None - self.edit_level_working_config = None + self.edit_path = [l for l in edit_level.split('/') if l] self.active_config = config._running_config self.working_config = config._session_config @@ -241,14 +233,8 @@ Proceed ?''' revision n vs. revision m; working version vs. active version; or working version vs. saved version. """ - from difflib import unified_diff - - ct1 = self.edit_level_active_config - if ct1 is None: - ct1 = self.active_config - ct2 = self.edit_level_working_config - if ct2 is None: - ct2 = self.working_config + ct1 = self.active_config + ct2 = self.working_config msg = 'No changes between working and active configurations.\n' if saved: ct1 = self._get_saved_config_tree() @@ -268,19 +254,16 @@ Proceed ?''' ct1 = self._get_config_tree_revision(rev2) msg = f'No changes between revisions {rev2} and {rev1} configurations.\n' - if commands: - lines1 = ct1.to_commands().splitlines(keepends=True) - lines2 = ct2.to_commands().splitlines(keepends=True) - else: - lines1 = ct1.to_string().splitlines(keepends=True) - lines2 = ct2.to_string().splitlines(keepends=True) - out = '' - comp = unified_diff(lines1, lines2) - for line in comp: - if re.match(r'(\-\-)|(\+\+)|(@@)', line): - continue - out += line + path = [] if commands else self.edit_path + try: + if commands: + out = show_diff(ct1, ct2, path=path, commands=True) + else: + out = show_diff(ct1, ct2, path=path) + except ConfigTreeError as e: + return e, 1 + if out: msg = out diff --git a/python/vyos/configtree.py b/python/vyos/configtree.py index f2358ee4f..c0b3ebd78 100644 --- a/python/vyos/configtree.py +++ b/python/vyos/configtree.py @@ -16,7 +16,7 @@ import os import re import json -from ctypes import cdll, c_char_p, c_void_p, c_int +from ctypes import cdll, c_char_p, c_void_p, c_int, c_bool LIBPATH = '/usr/lib/libvyosconfig.so.0' @@ -322,6 +322,36 @@ class ConfigTree(object): subt = ConfigTree(address=res) return subt +def show_diff(left, right, path=[], commands=False, libpath=LIBPATH): + if left is None: + left = ConfigTree(config_string='\n') + if right is None: + right = ConfigTree(config_string='\n') + if not (isinstance(left, ConfigTree) and isinstance(right, ConfigTree)): + raise TypeError("Arguments must be instances of ConfigTree") + if path: + if (not left.exists(path)) and (not right.exists(path)): + raise ConfigTreeError(f"Path {path} doesn't exist") + + check_path(path) + path_str = " ".join(map(str, path)).encode() + + __lib = cdll.LoadLibrary(libpath) + __show_diff = __lib.show_diff + __show_diff.argtypes = [c_bool, c_char_p, c_void_p, c_void_p] + __show_diff.restype = c_char_p + __get_error = __lib.get_error + __get_error.argtypes = [] + __get_error.restype = c_char_p + + res = __show_diff(commands, path_str, left._get_config(), right._get_config()) + res = res.decode() + if res == "#1@": + msg = __get_error().decode() + raise ConfigTreeError(msg) + + return res + class DiffTree: def __init__(self, left, right, path=[], libpath=LIBPATH): if left is None: diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py index 8e0ce701e..8fddd91d0 100644 --- a/python/vyos/configverify.py +++ b/python/vyos/configverify.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2020-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 @@ -23,6 +23,7 @@ from vyos import ConfigError from vyos.util import dict_search +from vyos.util import dict_search_recursive def verify_mtu(config): """ @@ -35,8 +36,14 @@ def verify_mtu(config): mtu = int(config['mtu']) tmp = Interface(config['ifname']) - min_mtu = tmp.get_min_mtu() - max_mtu = tmp.get_max_mtu() + # Not all interfaces support min/max MTU + # https://vyos.dev/T5011 + try: + min_mtu = tmp.get_min_mtu() + max_mtu = tmp.get_max_mtu() + except: # Fallback to defaults + min_mtu = 68 + max_mtu = 9000 if mtu < min_mtu: raise ConfigError(f'Interface MTU too low, ' \ @@ -232,7 +239,7 @@ def verify_authentication(config): """ if 'authentication' not in config: return - if not {'user', 'password'} <= set(config['authentication']): + if not {'username', 'password'} <= set(config['authentication']): raise ConfigError('Authentication requires both username and ' \ 'password to be set!') @@ -414,7 +421,18 @@ def verify_accel_ppp_base_service(config, local_users=True): if 'key' not in radius_config: raise ConfigError(f'Missing RADIUS secret key for server "{server}"') - if 'gateway_address' not in config: + # Check global gateway or gateway in named pool + gateway = False + if 'gateway_address' in config: + gateway = True + else: + if 'client_ip_pool' in config: + if dict_search_recursive(config, 'gateway_address', ['client_ip_pool', 'name']): + for _, v in config['client_ip_pool']['name'].items(): + if 'gateway_address' in v: + gateway = True + break + if not gateway: raise ConfigError('Server requires gateway-address to be configured!') if 'name_server_ipv4' in config: diff --git a/python/vyos/defaults.py b/python/vyos/defaults.py index 7de458960..db0def8ed 100644 --- a/python/vyos/defaults.py +++ b/python/vyos/defaults.py @@ -1,4 +1,4 @@ -# Copyright 2018 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2018-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 @@ -15,19 +15,22 @@ import os +base_dir = '/usr/libexec/vyos/' + directories = { - "data": "/usr/share/vyos/", - "conf_mode": "/usr/libexec/vyos/conf_mode", - "op_mode": "/usr/libexec/vyos/op_mode", - "config": "/opt/vyatta/etc/config", - "current": "/opt/vyatta/etc/config-migrate/current", - "migrate": "/opt/vyatta/etc/config-migrate/migrate", - "log": "/var/log/vyatta", - "templates": "/usr/share/vyos/templates/", - "certbot": "/config/auth/letsencrypt", - "api_schema": "/usr/libexec/vyos/services/api/graphql/graphql/schema/", - "api_templates": "/usr/libexec/vyos/services/api/graphql/session/templates/", - "vyos_udev_dir": "/run/udev/vyos" + 'base' : base_dir, + 'data' : '/usr/share/vyos/', + 'conf_mode' : f'{base_dir}/conf_mode', + 'op_mode' : f'{base_dir}/op_mode', + 'config' : '/opt/vyatta/etc/config', + 'current' : '/opt/vyatta/etc/config-migrate/current', + 'migrate' : '/opt/vyatta/etc/config-migrate/migrate', + 'log' : '/var/log/vyatta', + 'templates' : '/usr/share/vyos/templates/', + 'certbot' : '/config/auth/letsencrypt', + 'api_schema': f'{base_dir}/services/api/graphql/graphql/schema/', + 'api_templates': f'{base_dir}/services/api/graphql/session/templates/', + 'vyos_udev_dir' : '/run/udev/vyos' } config_status = '/tmp/vyos-config-status' @@ -50,12 +53,12 @@ api_data = { 'socket' : False, 'strict' : False, 'debug' : False, - 'api_keys' : [ {"id": "testapp", "key": "qwerty"} ] + 'api_keys' : [ {'id' : 'testapp', 'key' : 'qwerty'} ] } vyos_cert_data = { - "conf": "/etc/nginx/snippets/vyos-cert.conf", - "crt": "/etc/ssl/certs/vyos-selfsigned.crt", - "key": "/etc/ssl/private/vyos-selfsign", - "lifetime": "365", + 'conf' : '/etc/nginx/snippets/vyos-cert.conf', + 'crt' : '/etc/ssl/certs/vyos-selfsigned.crt', + 'key' : '/etc/ssl/private/vyos-selfsign', + 'lifetime' : '365', } diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index c50ead89f..fc33430eb 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -751,8 +751,8 @@ class Interface(Control): elif all_rp_filter == 2: global_setting = 'loose' from vyos.base import Warning - Warning(f'Global source-validation is set to "{global_setting} '\ - f'this overrides per interface setting!') + Warning(f'Global source-validation is set to "{global_setting}", this '\ + f'overrides per interface setting on "{self.ifname}"!') tmp = self.get_interface('rp_filter') if int(tmp) == value: @@ -1365,7 +1365,7 @@ class Interface(Control): if not isinstance(state, bool): raise ValueError("Value out of range") - # https://phabricator.vyos.net/T3448 - there is (yet) no RPI support for XDP + # https://vyos.dev/T3448 - there is (yet) no RPI support for XDP if not os.path.exists('/usr/sbin/xdp_loader'): return diff --git a/python/vyos/ifconfig/loopback.py b/python/vyos/ifconfig/loopback.py index b3babfadc..e1d041839 100644 --- a/python/vyos/ifconfig/loopback.py +++ b/python/vyos/ifconfig/loopback.py @@ -46,7 +46,7 @@ class LoopbackIf(Interface): if addr in self._persistent_addresses: # Do not allow deletion of the default loopback addresses as # this will cause weird system behavior like snmp/ssh no longer - # operating as expected, see https://phabricator.vyos.net/T2034. + # operating as expected, see https://vyos.dev/T2034. continue self.del_addr(addr) diff --git a/python/vyos/ifconfig/tunnel.py b/python/vyos/ifconfig/tunnel.py index 5258a2cb1..b7bf7d982 100644 --- a/python/vyos/ifconfig/tunnel.py +++ b/python/vyos/ifconfig/tunnel.py @@ -83,11 +83,6 @@ class TunnelIf(Interface): 'convert': enable_to_on, 'shellcmd': 'ip link set dev {ifname} multicast {value}', }, - 'allmulticast': { - 'validate': lambda v: assert_list(v, ['enable', 'disable']), - 'convert': enable_to_on, - 'shellcmd': 'ip link set dev {ifname} allmulticast {value}', - }, } } @@ -162,6 +157,10 @@ class TunnelIf(Interface): """ Get a synthetic MAC address. """ return self.get_mac_synthetic() + def set_multicast(self, enable): + """ Change the MULTICAST flag on the device """ + return self.set_interface('multicast', enable) + 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 @@ -170,5 +169,10 @@ class TunnelIf(Interface): # Adjust iproute2 tunnel parameters if necessary self._change_options() + # IP Multicast + tmp = dict_search('enable_multicast', config) + value = 'enable' if (tmp != None) else 'disable' + self.set_multicast(value) + # call base class first super().update(config) diff --git a/python/vyos/ipsec.py b/python/vyos/ipsec.py new file mode 100644 index 000000000..cb7c39ff6 --- /dev/null +++ b/python/vyos/ipsec.py @@ -0,0 +1,141 @@ +# Copyright 2020-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 +# 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/>. + +#Package to communicate with Strongswan VICI + +class ViciInitiateError(Exception): + """ + VICI can't initiate a session. + """ + pass +class ViciCommandError(Exception): + """ + VICI can't execute a command by any reason. + """ + pass + +def get_vici_sas(): + from vici import Session as vici_session + + try: + session = vici_session() + except Exception: + raise ViciInitiateError("IPsec not initialized") + sas = list(session.list_sas()) + return sas + + +def get_vici_connections(): + from vici import Session as vici_session + + try: + session = vici_session() + except Exception: + raise ViciInitiateError("IPsec not initialized") + connections = list(session.list_conns()) + return connections + + +def get_vici_sas_by_name(ike_name: str, tunnel: str) -> list: + """ + Find sas by IKE_SA name and/or CHILD_SA name + and return list of OrdinaryDicts with SASs info + If tunnel is not None return value is list of OrdenaryDicts contained only + CHILD_SAs wich names equal tunnel value. + :param ike_name: IKE SA name + :type ike_name: str + :param tunnel: CHILD SA name + :type tunnel: str + :return: list of Ordinary Dicts with SASs + :rtype: list + """ + from vici import Session as vici_session + + try: + session = vici_session() + except Exception: + raise ViciInitiateError("IPsec not initialized") + vici_dict = {} + if ike_name: + vici_dict['ike'] = ike_name + if tunnel: + vici_dict['child'] = tunnel + try: + sas = list(session.list_sas(vici_dict)) + return sas + except Exception: + raise ViciCommandError(f'Failed to get SAs') + + +def terminate_vici_ikeid_list(ike_id_list: list) -> None: + """ + Terminate IKE SAs by their id that contained in the list + :param ike_id_list: list of IKE SA id + :type ike_id_list: list + """ + from vici import Session as vici_session + + try: + session = vici_session() + except Exception: + raise ViciInitiateError("IPsec not initialized") + try: + for ikeid in ike_id_list: + session_generator = session.terminate( + {'ike-id': ikeid, 'timeout': '-1'}) + # a dummy `for` loop is required because of requirements + # from vici. Without a full iteration on the output, the + # command to vici may not be executed completely + for _ in session_generator: + pass + except Exception: + raise ViciCommandError( + f'Failed to terminate SA for IKE ids {ike_id_list}') + + +def terminate_vici_by_name(ike_name: str, child_name: str) -> None: + """ + Terminate IKE SAs by name if CHILD SA name is None. + Terminate CHILD SAs by name if CHILD SA name is specified + :param ike_name: IKE SA name + :type ike_name: str + :param child_name: CHILD SA name + :type child_name: str + """ + from vici import Session as vici_session + + try: + session = vici_session() + except Exception: + raise ViciInitiateError("IPsec not initialized") + try: + vici_dict: dict= {} + if ike_name: + vici_dict['ike'] = ike_name + if child_name: + vici_dict['child'] = child_name + session_generator = session.terminate(vici_dict) + # a dummy `for` loop is required because of requirements + # from vici. Without a full iteration on the output, the + # command to vici may not be executed completely + for _ in session_generator: + pass + except Exception: + if child_name: + raise ViciCommandError( + f'Failed to terminate SA for IPSEC {child_name}') + else: + raise ViciCommandError( + f'Failed to terminate SA for IKE {ike_name}') diff --git a/python/vyos/template.py b/python/vyos/template.py index 15240f815..6367f51e5 100644 --- a/python/vyos/template.py +++ b/python/vyos/template.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 @@ -158,6 +158,24 @@ def force_to_list(value): else: return [value] +@register_filter('seconds_to_human') +def seconds_to_human(seconds, separator=""): + """ Convert seconds to human-readable values like 1d6h15m23s """ + from vyos.util import seconds_to_human + return seconds_to_human(seconds, separator=separator) + +@register_filter('bytes_to_human') +def bytes_to_human(bytes, initial_exponent=0, precision=2): + """ Convert bytes to human-readable values like 1.44M """ + from vyos.util import bytes_to_human + return bytes_to_human(bytes, initial_exponent=initial_exponent, precision=precision) + +@register_filter('human_to_bytes') +def human_to_bytes(value): + """ Convert a data amount with a unit suffix to bytes, like 2K to 2048 """ + from vyos.util import human_to_bytes + return human_to_bytes(value) + @register_filter('ip_from_cidr') def ip_from_cidr(prefix): """ Take an IPv4/IPv6 CIDR host and strip cidr mask. |