From f19c92f255011149eeb7626a2e158456abe4c9b8 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 9 Oct 2021 08:38:06 +0200 Subject: tunnel: T3894: fix design when building synthetic MAC addresses It seems not all systems have eth0 - get a list of all available Ethernet interfaces on the system (without VLAN subinterfaces) and then take the first one. --- python/vyos/ifconfig/interface.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'python') diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index e6dbd861b..c73ffb634 100755 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -461,15 +461,19 @@ class Interface(Control): # Get processor ID number cpu_id = self._cmd('sudo dmidecode -t 4 | grep ID | head -n1 | sed "s/.*ID://;s/ //g"') - # Get system eth0 base MAC address - every system has eth0 - eth0_mac = Interface('eth0').get_mac() + + # XXX: T3894 - it seems not all systems have eth0 - get a list of all + # available Ethernet interfaces on the system (without VLAN subinterfaces) + # and then take the first one. + all_eth_ifs = [x for x in Section.interfaces('ethernet') if '.' not in x] + first_mac = Interface(all_eth_ifs[0]).get_mac() sha = sha256() # Calculate SHA256 sum based on the CPU ID number, eth0 mac address and # this interface identifier - this is as predictable as an interface # MAC address and thus can be used in the same way sha.update(cpu_id.encode()) - sha.update(eth0_mac.encode()) + sha.update(first_mac.encode()) sha.update(self.ifname.encode()) # take the most significant 48 bits from the SHA256 string tmp = sha.hexdigest()[:12] -- cgit v1.2.3 From 46e331cdf44b7880f6e2f5fefaa1f536829f6ebc Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 11 Oct 2021 16:47:08 +0200 Subject: vyos.configdict: T2653: do not merge in defaults when interface is deleted It makes less to zero sense to blend in the default values of an interface when it is about to be deleted from the system anyways - this makes the entire dict just cleaner and easier to debug. --- python/vyos/configdict.py | 38 +++++++++++++++++++++++------------- src/conf_mode/interfaces-wireless.py | 5 +++-- 2 files changed, 27 insertions(+), 16 deletions(-) (limited to 'python') diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index 5c6836e97..8308f0e9b 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -368,9 +368,11 @@ def get_interface_dict(config, base, ifname=''): del default_values['dhcpv6_options'] # We have gathered the dict representation of the CLI, but there are - # default options which we need to update into the dictionary - # retrived. - dict = dict_merge(default_values, dict) + # default options which we need to update into the dictionary retrived. + # But we should only add them when interface is not deleted - as this might + # confuse parsers + if 'deleted' not in dict: + dict = dict_merge(default_values, dict) # XXX: T2665: blend in proper DHCPv6-PD default values dict = T2665_set_dhcpv6pd_defaults(dict) @@ -423,9 +425,12 @@ def get_interface_dict(config, base, ifname=''): if not 'dhcpv6_options' in vif_config: del default_vif_values['dhcpv6_options'] - dict['vif'][vif] = dict_merge(default_vif_values, vif_config) - # XXX: T2665: blend in proper DHCPv6-PD default values - dict['vif'][vif] = T2665_set_dhcpv6pd_defaults(dict['vif'][vif]) + # Only add defaults if interface is not about to be deleted - this is + # to keep a cleaner config dict. + if 'deleted' not in dict: + dict['vif'][vif] = dict_merge(default_vif_values, vif_config) + # XXX: T2665: blend in proper DHCPv6-PD default values + dict['vif'][vif] = T2665_set_dhcpv6pd_defaults(dict['vif'][vif]) # Check if we are a member of a bridge device bridge = is_member(config, f'{ifname}.{vif}', 'bridge') @@ -441,10 +446,12 @@ def get_interface_dict(config, base, ifname=''): if not 'dhcpv6_options' in vif_s_config: del default_vif_s_values['dhcpv6_options'] - dict['vif_s'][vif_s] = dict_merge(default_vif_s_values, vif_s_config) - # XXX: T2665: blend in proper DHCPv6-PD default values - dict['vif_s'][vif_s] = T2665_set_dhcpv6pd_defaults( - dict['vif_s'][vif_s]) + # Only add defaults if interface is not about to be deleted - this is + # to keep a cleaner config dict. + if 'deleted' not in dict: + dict['vif_s'][vif_s] = dict_merge(default_vif_s_values, vif_s_config) + # XXX: T2665: blend in proper DHCPv6-PD default values + dict['vif_s'][vif_s] = T2665_set_dhcpv6pd_defaults(dict['vif_s'][vif_s]) # Check if we are a member of a bridge device bridge = is_member(config, f'{ifname}.{vif_s}', 'bridge') @@ -458,11 +465,14 @@ def get_interface_dict(config, base, ifname=''): if not 'dhcpv6_options' in vif_c_config: del default_vif_c_values['dhcpv6_options'] - dict['vif_s'][vif_s]['vif_c'][vif_c] = dict_merge( + # Only add defaults if interface is not about to be deleted - this is + # to keep a cleaner config dict. + if 'deleted' not in dict: + dict['vif_s'][vif_s]['vif_c'][vif_c] = dict_merge( default_vif_c_values, vif_c_config) - # XXX: T2665: blend in proper DHCPv6-PD default values - dict['vif_s'][vif_s]['vif_c'][vif_c] = T2665_set_dhcpv6pd_defaults( - dict['vif_s'][vif_s]['vif_c'][vif_c]) + # XXX: T2665: blend in proper DHCPv6-PD default values + dict['vif_s'][vif_s]['vif_c'][vif_c] = T2665_set_dhcpv6pd_defaults( + dict['vif_s'][vif_s]['vif_c'][vif_c]) # Check if we are a member of a bridge device bridge = is_member(config, f'{ifname}.{vif_s}.{vif_c}', 'bridge') diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py index 7b3de6e8a..af35b5f03 100755 --- a/src/conf_mode/interfaces-wireless.py +++ b/src/conf_mode/interfaces-wireless.py @@ -82,11 +82,12 @@ def get_config(config=None): tmp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True) if not (dict_search('security.wpa.passphrase', tmp) or dict_search('security.wpa.radius', tmp)): - del wifi['security']['wpa'] + if 'deleted' not in wifi: + del wifi['security']['wpa'] # defaults include RADIUS server specifics per TAG node which need to be # added to individual RADIUS servers instead - so we can simply delete them - if dict_search('security.wpa.radius.server.port', wifi): + if dict_search('security.wpa.radius.server.port', wifi) != None: del wifi['security']['wpa']['radius']['server']['port'] if not len(wifi['security']['wpa']['radius']['server']): del wifi['security']['wpa']['radius'] -- cgit v1.2.3 From 103fca9f77c28e392146f791e0241e119feeadc9 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 21 Oct 2021 19:38:38 +0200 Subject: tunnel: T3925: dhcp-interface was of no use - use source-interface instead (cherry picked from commit c1015d8ce0013719eb898b60b14ffec192b8141c) --- interface-definitions/interfaces-tunnel.xml.in | 1 - python/vyos/configverify.py | 7 ++-- smoketest/configs/tunnel-broker | 2 +- smoketest/scripts/cli/test_interfaces_tunnel.py | 20 ----------- src/migration-scripts/interfaces/21-to-22 | 46 +++++++++++++++++++++++++ 5 files changed, 49 insertions(+), 27 deletions(-) create mode 100755 src/migration-scripts/interfaces/21-to-22 (limited to 'python') diff --git a/interface-definitions/interfaces-tunnel.xml.in b/interface-definitions/interfaces-tunnel.xml.in index 7450ef2af..cca732f82 100644 --- a/interface-definitions/interfaces-tunnel.xml.in +++ b/interface-definitions/interfaces-tunnel.xml.in @@ -54,7 +54,6 @@ - #include Encapsulation of this tunnel interface diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py index 8aca76568..365a28feb 100644 --- a/python/vyos/configverify.py +++ b/python/vyos/configverify.py @@ -110,15 +110,12 @@ def verify_tunnel(config): raise ConfigError('Must configure the tunnel encapsulation for '\ '{ifname}!'.format(**config)) - if 'source_address' not in config and 'dhcp_interface' not in config: - raise ConfigError('source-address is mandatory for tunnel') + if 'source_address' not in config and 'source_interface' not in config: + raise ConfigError('source-address or source-interface required for tunnel!') if 'remote' not in config and config['encapsulation'] != 'gre': raise ConfigError('remote ip address is mandatory for tunnel') - if {'source_address', 'dhcp_interface'} <= set(config): - raise ConfigError('Can not use both source-address and dhcp-interface') - if config['encapsulation'] in ['ipip6', 'ip6ip6', 'ip6gre', 'ip6gretap', 'ip6erspan']: error_ipv6 = 'Encapsulation mode requires IPv6' if 'source_address' in config and not is_ipv6(config['source_address']): diff --git a/smoketest/configs/tunnel-broker b/smoketest/configs/tunnel-broker index d4a5c2dfc..03ac0db41 100644 --- a/smoketest/configs/tunnel-broker +++ b/smoketest/configs/tunnel-broker @@ -56,7 +56,7 @@ interfaces { tunnel tun100 { address 172.16.0.1/30 encapsulation gre-bridge - local-ip 192.0.2.0 + dhcp-interface eth0 remote-ip 192.0.2.100 } tunnel tun200 { diff --git a/smoketest/scripts/cli/test_interfaces_tunnel.py b/smoketest/scripts/cli/test_interfaces_tunnel.py index 841527d21..fc2e254d6 100755 --- a/smoketest/scripts/cli/test_interfaces_tunnel.py +++ b/smoketest/scripts/cli/test_interfaces_tunnel.py @@ -156,26 +156,6 @@ class TunnelInterfaceTest(BasicInterfaceTest.TestCase): self.cli_delete(self._base_path + [interface]) self.cli_commit() - def test_tunnel_verify_local_dhcp(self): - # We can not use source-address and dhcp-interface at the same time - - interface = f'tun1020' - local_if_addr = f'10.0.0.1/24' - - self.cli_set(self._base_path + [interface, 'address', local_if_addr]) - self.cli_set(self._base_path + [interface, 'encapsulation', 'gre']) - self.cli_set(self._base_path + [interface, 'source-address', self.local_v4]) - self.cli_set(self._base_path + [interface, 'remote', remote_ip4]) - self.cli_set(self._base_path + [interface, 'dhcp-interface', 'eth0']) - - # source-address and dhcp-interface can not be used at the same time - with self.assertRaises(ConfigSessionError): - self.cli_commit() - self.cli_delete(self._base_path + [interface, 'dhcp-interface']) - - # Check if commit is ok - self.cli_commit() - def test_tunnel_parameters_gre(self): interface = f'tun1030' gre_key = '10' diff --git a/src/migration-scripts/interfaces/21-to-22 b/src/migration-scripts/interfaces/21-to-22 new file mode 100755 index 000000000..098102102 --- /dev/null +++ b/src/migration-scripts/interfaces/21-to-22 @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 . + +from sys import argv +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + 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) +base = ['interfaces', 'tunnel'] + +if not config.exists(base): + exit(0) + +for interface in config.list_nodes(base): + path = base + [interface, 'dhcp-interface'] + if config.exists(path): + tmp = config.return_value(path) + config.delete(path) + config.set(base + [interface, 'source-interface'], value=tmp) + +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) -- cgit v1.2.3 From 241a19943b7321aa1f2e2ece86b5ad68997390fe Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Sun, 24 Oct 2021 11:17:49 +0700 Subject: T3937: rewrite the "show system memory" script in Python --- op-mode-definitions/show-system.xml.in | 2 +- python/vyos/util.py | 34 ++++++++++++++++++ src/op_mode/show_ram.py | 64 ++++++++++++++++++++++++++++++++++ src/op_mode/show_ram.sh | 33 ------------------ 4 files changed, 99 insertions(+), 34 deletions(-) create mode 100755 src/op_mode/show_ram.py delete mode 100755 src/op_mode/show_ram.sh (limited to 'python') diff --git a/op-mode-definitions/show-system.xml.in b/op-mode-definitions/show-system.xml.in index 18a28868d..b32aee0c2 100644 --- a/op-mode-definitions/show-system.xml.in +++ b/op-mode-definitions/show-system.xml.in @@ -104,7 +104,7 @@ Show system memory usage - ${vyos_op_scripts_dir}/show_ram.sh + ${vyos_op_scripts_dir}/show_ram.py diff --git a/python/vyos/util.py b/python/vyos/util.py index 849b27d3b..2c4051a7a 100644 --- a/python/vyos/util.py +++ b/python/vyos/util.py @@ -489,6 +489,40 @@ def seconds_to_human(s, separator=""): return result +def bytes_to_human(bytes, initial_exponent=0): + """ Converts a value in bytes to a human-readable size string like 640 KB + + The initial_exponent parameter is the exponent of 2, + e.g. 10 (1024) for kilobytes, 20 (1024 * 1024) for megabytes. + """ + + from math import log2 + + bytes = bytes * (2**initial_exponent) + + # log2 is a float, while range checking requires an int + exponent = int(log2(bytes)) + + if exponent < 10: + value = bytes + suffix = "B" + elif exponent in range(10, 20): + value = bytes / 1024 + suffix = "KB" + elif exponent in range(20, 30): + value = bytes / 1024**2 + suffix = "MB" + elif exponent in range(30, 40): + value = bytes / 1024**3 + suffix = "GB" + else: + value = bytes / 1024**4 + suffix = "TB" + # Add a new case when the first machine with petabyte RAM + # hits the market. + + size_string = "{0:.2f} {1}".format(value, suffix) + return size_string def get_cfg_group_id(): from grp import getgrnam diff --git a/src/op_mode/show_ram.py b/src/op_mode/show_ram.py new file mode 100755 index 000000000..5818ec132 --- /dev/null +++ b/src/op_mode/show_ram.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 . +# + +def get_system_memory(): + from re import search as re_search + + def find_value(keyword, mem_data): + regex = keyword + ':\s+(\d+)' + res = re_search(regex, mem_data).group(1) + return int(res) + + with open("/proc/meminfo", "r") as f: + mem_data = f.read() + + total = find_value('MemTotal', mem_data) + available = find_value('MemAvailable', mem_data) + buffers = find_value('Buffers', mem_data) + cached = find_value('Cached', mem_data) + + used = total - available + + res = { + "total": total, + "free": available, + "used": used, + "buffers": buffers, + "cached": cached + } + + return res + +def get_system_memory_human(): + from vyos.util import bytes_to_human + + mem = get_system_memory() + + for key in mem: + # The Linux kernel exposes memory values in kilobytes, + # so we need to normalize them + mem[key] = bytes_to_human(mem[key], initial_exponent=10) + + return mem + +if __name__ == '__main__': + mem = get_system_memory_human() + + print("Total: {}".format(mem["total"])) + print("Free: {}".format(mem["free"])) + print("Used: {}".format(mem["used"])) + diff --git a/src/op_mode/show_ram.sh b/src/op_mode/show_ram.sh deleted file mode 100755 index b013e16f8..000000000 --- a/src/op_mode/show_ram.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -# -# Module: vyos-show-ram.sh -# Displays memory usage information in minimalistic format -# -# Copyright (C) 2019 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 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 . - -MB_DIVISOR=1024 - -TOTAL=$(cat /proc/meminfo | grep -E "^MemTotal:" | awk -F ' ' '{print $2}') -FREE=$(cat /proc/meminfo | grep -E "^MemFree:" | awk -F ' ' '{print $2}') -BUFFERS=$(cat /proc/meminfo | grep -E "^Buffers:" | awk -F ' ' '{print $2}') -CACHED=$(cat /proc/meminfo | grep -E "^Cached:" | awk -F ' ' '{print $2}') - -DISPLAY_FREE=$(( ($FREE + $BUFFERS + $CACHED) / $MB_DIVISOR )) -DISPLAY_TOTAL=$(( $TOTAL / $MB_DIVISOR )) -DISPLAY_USED=$(( $DISPLAY_TOTAL - $DISPLAY_FREE )) - -echo "Total: $DISPLAY_TOTAL" -echo "Free: $DISPLAY_FREE" -echo "Used: $DISPLAY_USED" -- cgit v1.2.3 From 8cf5a4f023c5459cad4c84e93f73a9ddd69be81a Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 24 Oct 2021 21:27:28 +0200 Subject: vyos.ethtool: T3935: relax __init__() when driver name is not detected In addition to commit 0b414bcd ("vyos.ethtool: T3874: do not throw exception if adapter has issues with autoneg") we should also not care too strict when locating the driver name. This might cause false positives. --- python/vyos/ethtool.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'python') diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index eb5b0a456..e45b0f041 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -56,9 +56,6 @@ class Ethtool: link = os.readlink(sysfs_file) self._driver_name = os.path.basename(link) - if not self._driver_name: - raise ValueError(f'Could not determine driver for interface {ifname}!') - # Build a dictinary of supported link-speed and dupley settings. out, err = popen(f'ethtool {ifname}') reading = False -- cgit v1.2.3