diff options
Diffstat (limited to 'python/vyos')
-rw-r--r-- | python/vyos/configsession.py | 4 | ||||
-rw-r--r-- | python/vyos/cpu.py | 102 | ||||
-rw-r--r-- | python/vyos/firewall.py | 71 | ||||
-rw-r--r-- | python/vyos/frr.py | 2 | ||||
-rw-r--r-- | python/vyos/migrator.py | 5 | ||||
-rw-r--r-- | python/vyos/pki.py | 2 | ||||
-rw-r--r-- | python/vyos/util.py | 17 |
7 files changed, 194 insertions, 9 deletions
diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py index f28ad09c5..3a60f6d92 100644 --- a/python/vyos/configsession.py +++ b/python/vyos/configsession.py @@ -33,6 +33,7 @@ INSTALL_IMAGE = ['/opt/vyatta/sbin/install-image', '--url'] REMOVE_IMAGE = ['/opt/vyatta/bin/vyatta-boot-image.pl', '--del'] GENERATE = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'generate'] SHOW = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'show'] +RESET = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'reset'] # Default "commit via" string APP = "vyos-http-api" @@ -200,3 +201,6 @@ class ConfigSession(object): out = self.__run_command(SHOW + path) return out + def reset(self, path): + out = self.__run_command(RESET + path) + return out diff --git a/python/vyos/cpu.py b/python/vyos/cpu.py new file mode 100644 index 000000000..a0ef864be --- /dev/null +++ b/python/vyos/cpu.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# Copyright 2022 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/>. + +""" +Retrieves (or at least attempts to retrieve) the total number of real CPU cores +installed in a Linux system. + +The issue of core count is complicated by existence of SMT, e.g. Intel's Hyper Threading. +GNU nproc returns the number of LOGICAL cores, +which is 2x of the real cores if SMT is enabled. + +The idea is to find all physical CPUs and add up their core counts. +It has special cases for x86_64 and MAY work correctly on other architectures, +but nothing is certain. +""" + +import re + + +def _read_cpuinfo(): + with open('/proc/cpuinfo', 'r') as f: + return f.readlines() + +def _split_line(l): + l = l.strip() + parts = re.split(r'\s*:\s*', l) + return (parts[0], ":".join(parts[1:])) + +def _find_cpus(cpuinfo_lines): + # Make a dict because it's more convenient to work with later, + # when we need to find physicall distinct CPUs there. + cpus = {} + + cpu_number = 0 + + for l in cpuinfo_lines: + key, value = _split_line(l) + if key == 'processor': + cpu_number = value + cpus[cpu_number] = {} + else: + cpus[cpu_number][key] = value + + return cpus + +def _find_physical_cpus(): + cpus = _find_cpus(_read_cpuinfo()) + + phys_cpus = {} + + for num in cpus: + if 'physical id' in cpus[num]: + # On at least some architectures, CPUs in different sockets + # have different 'physical id' field, e.g. on x86_64. + phys_id = cpus[num]['physical id'] + if phys_id not in phys_cpus: + phys_cpus[phys_id] = cpus[num] + else: + # On other architectures, e.g. on ARM, there's no such field. + # We just assume they are different CPUs, + # whether single core ones or cores of physical CPUs. + phys_cpus[num] = cpu[num] + + return phys_cpus + +def get_cpus(): + """ Returns a list of /proc/cpuinfo entries that belong to different CPUs. + """ + cpus_dict = _find_physical_cpus() + return list(cpus_dict.values()) + +def get_core_count(): + """ Returns the total number of physical CPU cores + (even if Hyper-Threading or another SMT is enabled and has inflated + the number of cores in /proc/cpuinfo) + """ + physical_cpus = _find_physical_cpus() + + core_count = 0 + + for num in physical_cpus: + # Some architectures, e.g. x86_64, include a field for core count. + # Since we found unique physical CPU entries, we can sum their core counts. + if 'cpu cores' in physical_cpus[num]: + core_count += int(physical_cpus[num]['cpu cores']) + else: + core_count += 1 + + return core_count diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py index dbe5efcb6..31fe8b5e3 100644 --- a/python/vyos/firewall.py +++ b/python/vyos/firewall.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-2022 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 @@ -16,9 +16,70 @@ import re +from vyos.util import call from vyos.util import cmd from vyos.util import dict_search_args + +# Functions for firewall group domain-groups +def get_ips_domains_dict(list_domains): + """ + Get list of IPv4 addresses by list of domains + Ex: get_ips_domains_dict(['ex1.com', 'ex2.com']) + {'ex1.com': ['192.0.2.1'], 'ex2.com': ['192.0.2.2', '192.0.2.3']} + """ + from socket import gethostbyname_ex + from socket import gaierror + + ip_dict = {} + for domain in list_domains: + try: + _, _, ips = gethostbyname_ex(domain) + ip_dict[domain] = ips + except gaierror: + pass + + return ip_dict + +def nft_init_set(group_name, table="filter", family="ip"): + """ + table ip filter { + set GROUP_NAME + type ipv4_addr + flags interval + } + """ + return call(f'nft add set ip {table} {group_name} {{ type ipv4_addr\\; flags interval\\; }}') + + +def nft_add_set_elements(group_name, elements, table="filter", family="ip"): + """ + table ip filter { + set GROUP_NAME { + type ipv4_addr + flags interval + elements = { 192.0.2.1, 192.0.2.2 } + } + """ + elements = ", ".join(elements) + return call(f'nft add element {family} {table} {group_name} {{ {elements} }} ') + +def nft_flush_set(group_name, table="filter", family="ip"): + """ + Flush elements of nft set + """ + return call(f'nft flush set {family} {table} {group_name}') + +def nft_update_set_elements(group_name, elements, table="filter", family="ip"): + """ + Update elements of nft set + """ + flush_set = nft_flush_set(group_name, table="filter", family="ip") + nft_add_set = nft_add_set_elements(group_name, elements, table="filter", family="ip") + return flush_set, nft_add_set + +# END firewall group domain-group (sets) + def find_nftables_rule(table, chain, rule_matches=[]): # Find rule in table/chain that matches all criteria and return the handle results = cmd(f'sudo nft -a list chain {table} {chain}').split("\n") @@ -118,6 +179,14 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name): operator = '!=' group_name = group_name[1:] output.append(f'{ip_name} {prefix}addr {operator} $A{def_suffix}_{group_name}') + # Generate firewall group domain-group + elif 'domain_group' in group: + group_name = group['domain_group'] + operator = '' + if group_name[0] == '!': + operator = '!=' + group_name = group_name[1:] + output.append(f'{ip_name} {prefix}addr {operator} @{group_name}') elif 'network_group' in group: group_name = group['network_group'] operator = '' diff --git a/python/vyos/frr.py b/python/vyos/frr.py index cbba19ab7..0ffd5cba9 100644 --- a/python/vyos/frr.py +++ b/python/vyos/frr.py @@ -85,7 +85,7 @@ LOG.addHandler(ch2) _frr_daemons = ['zebra', 'bgpd', 'fabricd', 'isisd', 'ospf6d', 'ospfd', 'pbrd', 'pimd', 'ripd', 'ripngd', 'sharpd', 'staticd', 'vrrpd', 'ldpd', - 'bfdd'] + 'bfdd', 'eigrpd'] path_vtysh = '/usr/bin/vtysh' path_frr_reload = '/usr/lib/frr/frr-reload.py' diff --git a/python/vyos/migrator.py b/python/vyos/migrator.py index a2e0daabd..c6e3435ca 100644 --- a/python/vyos/migrator.py +++ b/python/vyos/migrator.py @@ -105,6 +105,11 @@ class Migrator(object): sys_keys = list(sys_versions.keys()) sys_keys.sort() + # XXX 'bgp' needs to follow 'quagga': + if 'bgp' in sys_keys and 'quagga' in sys_keys: + sys_keys.insert(sys_keys.index('quagga'), + sys_keys.pop(sys_keys.index('bgp'))) + rev_versions = {} for key in sys_keys: diff --git a/python/vyos/pki.py b/python/vyos/pki.py index 0b916eaae..fd91fc9bf 100644 --- a/python/vyos/pki.py +++ b/python/vyos/pki.py @@ -247,7 +247,7 @@ def load_private_key(raw_data, passphrase=None, wrap_tags=True): if wrap_tags: raw_data = wrap_private_key(raw_data, passphrase) - if passphrase: + if passphrase is not None: passphrase = bytes(passphrase, 'utf-8') try: diff --git a/python/vyos/util.py b/python/vyos/util.py index de55e108b..0d62fbfe9 100644 --- a/python/vyos/util.py +++ b/python/vyos/util.py @@ -757,21 +757,26 @@ def dict_search_args(dict_object, *path): dict_object = dict_object[item] return dict_object -def dict_search_recursive(dict_object, key): +def dict_search_recursive(dict_object, key, path=[]): """ Traverse a dictionary recurisvely and return the value of the key we are looking for. Thankfully copied from https://stackoverflow.com/a/19871956 + + Modified to yield optional path to found keys """ if isinstance(dict_object, list): for i in dict_object: - for x in dict_search_recursive(i, key): - yield x + new_path = path + [i] + for x in dict_search_recursive(i, key, new_path): + yield x elif isinstance(dict_object, dict): if key in dict_object: - yield dict_object[key] - for j in dict_object.values(): - for x in dict_search_recursive(j, key): + new_path = path + [key] + yield dict_object[key], new_path + for k, j in dict_object.items(): + new_path = path + [k] + for x in dict_search_recursive(j, key, new_path): yield x def get_bridge_fdb(interface): |