From a78982625a8a18069bd5a13744734873698fd0f9 Mon Sep 17 00:00:00 2001 From: aapostoliuk Date: Thu, 30 Mar 2023 18:28:56 +0300 Subject: ipsec: T5093: Fixed 'reset vpn ipsec profile' command Fixed 'reset vpn ipsec profile' command using vici library and new op-mode style. Added ability to use 'reset vpn ipsec profile' command with 'remote-host' option. --- src/op_mode/ipsec.py | 62 ++++++++++++++++++++++++++++++++++++++++++++++++ src/op_mode/vpn_ipsec.py | 61 ++++------------------------------------------- 2 files changed, 66 insertions(+), 57 deletions(-) (limited to 'src/op_mode') diff --git a/src/op_mode/ipsec.py b/src/op_mode/ipsec.py index 6acde08ea..7f4fb72e5 100755 --- a/src/op_mode/ipsec.py +++ b/src/op_mode/ipsec.py @@ -13,6 +13,7 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . + import re import sys import typing @@ -487,6 +488,67 @@ def reset_ra(username: typing.Optional[str] = None): vyos.ipsec.terminate_vici_ikeid_list(list_sa_id) +def reset_profile_dst(profile: str, tunnel: str, nbma_dst: str): + if profile and tunnel and nbma_dst: + ike_sa_name = f'dmvpn-{profile}-{tunnel}' + try: + # Get IKE SAs + sa_list = convert_data( + vyos.ipsec.get_vici_sas_by_name(ike_sa_name, None)) + if not sa_list: + raise vyos.opmode.IncorrectValue( + f'SA(s) for profile {profile} tunnel {tunnel} not found, aborting') + sa_nbma_list = list([x for x in sa_list if + ike_sa_name in x and x[ike_sa_name][ + 'remote-host'] == nbma_dst]) + if not sa_nbma_list: + raise vyos.opmode.IncorrectValue( + f'SA(s) for profile {profile} tunnel {tunnel} remote-host {nbma_dst} not found, aborting') + # terminate IKE SAs + vyos.ipsec.terminate_vici_ikeid_list(list( + [x[ike_sa_name]['uniqueid'] for x in sa_nbma_list if + ike_sa_name in x])) + # initiate IKE SAs + for ike in sa_nbma_list: + if ike_sa_name in ike: + vyos.ipsec.vici_initiate(ike_sa_name, 'dmvpn', + ike[ike_sa_name]['local-host'], + ike[ike_sa_name]['remote-host']) + print( + f'Profile {profile} tunnel {tunnel} remote-host {nbma_dst} reset result: success') + except (vyos.ipsec.ViciInitiateError) as err: + raise vyos.opmode.UnconfiguredSubsystem(err) + except (vyos.ipsec.ViciCommandError) as err: + raise vyos.opmode.IncorrectValue(err) + + +def reset_profile_all(profile: str, tunnel: str): + if profile and tunnel: + ike_sa_name = f'dmvpn-{profile}-{tunnel}' + try: + # Get IKE SAs + sa_list: list = convert_data( + vyos.ipsec.get_vici_sas_by_name(ike_sa_name, None)) + if not sa_list: + raise vyos.opmode.IncorrectValue( + f'SA(s) for profile {profile} tunnel {tunnel} not found, aborting') + # terminate IKE SAs + vyos.ipsec.terminate_vici_by_name(ike_sa_name, None) + # initiate IKE SAs + for ike in sa_list: + if ike_sa_name in ike: + vyos.ipsec.vici_initiate(ike_sa_name, 'dmvpn', + ike[ike_sa_name]['local-host'], + ike[ike_sa_name]['remote-host']) + print( + f'Profile {profile} tunnel {tunnel} remote-host {ike[ike_sa_name]["remote-host"]} reset result: success') + print(f'Profile {profile} tunnel {tunnel} reset result: success') + except (vyos.ipsec.ViciInitiateError) as err: + raise vyos.opmode.UnconfiguredSubsystem(err) + except (vyos.ipsec.ViciCommandError) as err: + raise vyos.opmode.IncorrectValue(err) + + def show_sa(raw: bool): sa_data = _get_raw_data_sas() if raw: diff --git a/src/op_mode/vpn_ipsec.py b/src/op_mode/vpn_ipsec.py index 2392cfe92..b81d1693e 100755 --- a/src/op_mode/vpn_ipsec.py +++ b/src/op_mode/vpn_ipsec.py @@ -16,12 +16,12 @@ import re import argparse -from subprocess import TimeoutExpired from vyos.util import call SWANCTL_CONF = '/etc/swanctl/swanctl.conf' + def get_peer_connections(peer, tunnel, return_all = False): search = rf'^[\s]*(peer_{peer}_(tunnel_[\d]+|vti)).*' matches = [] @@ -34,57 +34,6 @@ def get_peer_connections(peer, tunnel, return_all = False): matches.append(result[1]) return matches -def reset_peer(peer, tunnel): - if not peer: - print('Invalid peer, aborting') - return - - conns = get_peer_connections(peer, tunnel, return_all = (not tunnel or tunnel == 'all')) - - if not conns: - print('Tunnel(s) not found, aborting') - return - - result = True - for conn in conns: - try: - call(f'/usr/sbin/ipsec down {conn}{{*}}', timeout = 10) - call(f'/usr/sbin/ipsec up {conn}', timeout = 10) - except TimeoutExpired as e: - print(f'Timed out while resetting {conn}') - result = False - - - print('Peer reset result: ' + ('success' if result else 'failed')) - -def get_profile_connection(profile, tunnel = None): - search = rf'(dmvpn-{profile}-[\w]+)' if tunnel == 'all' else rf'(dmvpn-{profile}-{tunnel})' - with open(SWANCTL_CONF, 'r') as f: - for line in f.readlines(): - result = re.search(search, line) - if result: - return result[1] - return None - -def reset_profile(profile, tunnel): - if not profile: - print('Invalid profile, aborting') - return - - if not tunnel: - print('Invalid tunnel, aborting') - return - - conn = get_profile_connection(profile) - - if not conn: - print('Profile not found, aborting') - return - - call(f'/usr/sbin/ipsec down {conn}') - result = call(f'/usr/sbin/ipsec up {conn}') - - print('Profile reset result: ' + ('success' if result == 0 else 'failed')) def debug_peer(peer, tunnel): peer = peer.replace(':', '-') @@ -119,6 +68,7 @@ def debug_peer(peer, tunnel): for conn in conns: call(f'/usr/sbin/ipsec statusall | grep {conn}') + if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--action', help='Control action', required=True) @@ -127,9 +77,6 @@ if __name__ == '__main__': args = parser.parse_args() - if args.action == 'reset-peer': - reset_peer(args.name, args.tunnel) - elif args.action == "reset-profile": - reset_profile(args.name, args.tunnel) - elif args.action == "vpn-debug": + + if args.action == "vpn-debug": debug_peer(args.name, args.tunnel) -- cgit v1.2.3 From 33b804c2030f8bc47acb3499a155a0e2f1f2db0f Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Thu, 30 Mar 2023 14:12:59 -0500 Subject: interfaces: T5130: remove obsoleted show_interfaces.py --- src/op_mode/show_interfaces.py | 310 ----------------------------------------- 1 file changed, 310 deletions(-) delete mode 100755 src/op_mode/show_interfaces.py (limited to 'src/op_mode') diff --git a/src/op_mode/show_interfaces.py b/src/op_mode/show_interfaces.py deleted file mode 100755 index eac068274..000000000 --- a/src/op_mode/show_interfaces.py +++ /dev/null @@ -1,310 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2017-2021 VyOS maintainers and contributors -# -# 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 . - -import os -import re -import sys -import glob -import argparse - -from vyos.ifconfig import Section -from vyos.ifconfig import Interface -from vyos.ifconfig import VRRP -from vyos.util import cmd, call - - -# interfaces = Sections.reserved() -interfaces = ['eno', 'ens', 'enp', 'enx', 'eth', 'vmnet', 'lo', 'tun', 'wan', 'pppoe'] -glob_ifnames = '/sys/class/net/({})*'.format('|'.join(interfaces)) - - -actions = {} -def register(name): - """ - Decorator to register a function into actions with a name. - `actions[name]' can be used to call the registered functions. - We wrap each function in a SIGPIPE handler as all registered functions - can be subject to a broken pipe if there are a lot of interfaces. - """ - def _register(function): - def handled_function(*args, **kwargs): - try: - function(*args, **kwargs) - except BrokenPipeError: - # Flush output to /dev/null and bail out. - os.dup2(os.open(os.devnull, os.O_WRONLY), sys.stdout.fileno()) - sys.exit(1) - actions[name] = handled_function - return handled_function - return _register - - -def filtered_interfaces(ifnames, iftypes, vif, vrrp): - """ - get all the interfaces from the OS and returns them - ifnames can be used to filter which interfaces should be considered - - ifnames: a list of interfaces names to consider, empty do not filter - return an instance of the interface class - """ - if isinstance(iftypes, list): - for iftype in iftypes: - yield from filtered_interfaces(ifnames, iftype, vif, vrrp) - - for ifname in Section.interfaces(iftypes): - # Bail out early if interface name not part of our search list - if ifnames and ifname not in ifnames: - continue - - # As we are only "reading" from the interface - we must use the - # generic base class which exposes all the data via a common API - interface = Interface(ifname, create=False, debug=False) - - # VLAN interfaces have a '.' in their name by convention - if vif and not '.' in ifname: - continue - - if vrrp: - vrrp_interfaces = VRRP.active_interfaces() - if ifname not in vrrp_interfaces: - continue - - yield interface - - -def split_text(text, used=0): - """ - take a string and attempt to split it to fit with the width of the screen - - text: the string to split - used: number of characted already used in the screen - """ - no_tty = call('tty -s') - - returned = cmd('stty size') if not no_tty else '' - if len(returned) == 2: - rows, columns = [int(_) for _ in returned] - else: - rows, columns = (40, 80) - - desc_len = columns - used - - line = '' - for word in text.split(): - if len(line) + len(word) < desc_len: - line = f'{line} {word}' - continue - if line: - yield line[1:] - else: - line = f'{line} {word}' - - yield line[1:] - - -def get_counter_val(clear, now): - """ - attempt to correct a counter if it wrapped, copied from perl - - clear: previous counter - now: the current counter - """ - # This function has to deal with both 32 and 64 bit counters - if clear == 0: - return now - - # device is using 64 bit values assume they never wrap - value = now - clear - if (now >> 32) != 0: - return value - - # The counter has rolled. If the counter has rolled - # multiple times since the clear value, then this math - # is meaningless. - if (value < 0): - value = (4294967296 - clear) + now - - return value - - -@register('help') -def usage(*args): - print(f"Usage: {sys.argv[0]} [intf=NAME|intf-type=TYPE|vif|vrrp] action=ACTION") - print(f" NAME = " + ' | '.join(Section.interfaces())) - print(f" TYPE = " + ' | '.join(Section.sections())) - print(f" ACTION = " + ' | '.join(actions)) - sys.exit(1) - - -@register('allowed') -def run_allowed(**kwarg): - sys.stdout.write(' '.join(Section.interfaces())) - - -def pppoe(ifname): - out = cmd(f'ps -C pppd -f') - if ifname in out: - return 'C' - elif ifname in [_.split('/')[-1] for _ in glob.glob('/etc/ppp/peers/pppoe*')]: - return 'D' - return '' - - -@register('show') -def run_show_intf(ifnames, iftypes, vif, vrrp): - handled = [] - for interface in filtered_interfaces(ifnames, iftypes, vif, vrrp): - handled.append(interface.ifname) - cache = interface.operational.load_counters() - - out = cmd(f'ip addr show {interface.ifname}') - out = re.sub(f'^\d+:\s+','',out) - if re.search('link/tunnel6', out): - tunnel = cmd(f'ip -6 tun show {interface.ifname}') - # tun0: ip/ipv6 remote ::2 local ::1 encaplimit 4 hoplimit 64 tclass inherit flowlabel inherit (flowinfo 0x00000000) - tunnel = re.sub('.*encap', 'encap', tunnel) - out = re.sub('(\n\s+)(link/tunnel6)', f'\g<1>{tunnel}\g<1>\g<2>', out) - - print(out) - - timestamp = int(cache.get('timestamp', 0)) - if timestamp: - when = interface.operational.strtime(timestamp) - print(f' Last clear: {when}') - - description = interface.get_alias() - if description: - print(f' Description: {description}') - - print() - print(interface.operational.formated_stats()) - - for ifname in ifnames: - if ifname not in handled and ifname.startswith('pppoe'): - state = pppoe(ifname) - if not state: - continue - string = { - 'C': 'Coming up', - 'D': 'Link down', - }[state] - print('{}: {}'.format(ifname, string)) - - -@register('show-brief') -def run_show_intf_brief(ifnames, iftypes, vif, vrrp): - format1 = '%-16s %-33s %-4s %s' - format2 = '%-16s %s' - - print('Codes: S - State, L - Link, u - Up, D - Down, A - Admin Down') - print(format1 % ("Interface", "IP Address", "S/L", "Description")) - print(format1 % ("---------", "----------", "---", "-----------")) - - handled = [] - for interface in filtered_interfaces(ifnames, iftypes, vif, vrrp): - handled.append(interface.ifname) - - oper_state = interface.operational.get_state() - admin_state = interface.get_admin_state() - - intf = [interface.ifname,] - - oper = ['u', ] if oper_state in ('up', 'unknown') else ['D', ] - admin = ['u', ] if admin_state in ('up', 'unknown') else ['A', ] - addrs = [_ for _ in interface.get_addr() if not _.startswith('fe80::')] or ['-', ] - descs = list(split_text(interface.get_alias(),0)) - - while intf or oper or admin or addrs or descs: - i = intf.pop(0) if intf else '' - a = addrs.pop(0) if addrs else '' - d = descs.pop(0) if descs else '' - s = [admin.pop(0)] if admin else [] - l = [oper.pop(0)] if oper else [] - if len(a) < 33: - print(format1 % (i, a, '/'.join(s+l), d)) - else: - print(format2 % (i, a)) - print(format1 % ('', '', '/'.join(s+l), d)) - - for ifname in ifnames: - if ifname not in handled and ifname.startswith('pppoe'): - state = pppoe(ifname) - if not state: - continue - string = { - 'C': 'u/D', - 'D': 'A/D', - }[state] - print(format1 % (ifname, '', string, '')) - - -@register('show-count') -def run_show_counters(ifnames, iftypes, vif, vrrp): - formating = '%-12s %10s %10s %10s %10s' - print(formating % ('Interface', 'Rx Packets', 'Rx Bytes', 'Tx Packets', 'Tx Bytes')) - - for interface in filtered_interfaces(ifnames, iftypes, vif, vrrp): - oper = interface.operational.get_state() - - if oper not in ('up','unknown'): - continue - - stats = interface.operational.get_stats() - cache = interface.operational.load_counters() - print(formating % ( - interface.ifname, - get_counter_val(cache['rx_packets'], stats['rx_packets']), - get_counter_val(cache['rx_bytes'], stats['rx_bytes']), - get_counter_val(cache['tx_packets'], stats['tx_packets']), - get_counter_val(cache['tx_bytes'], stats['tx_bytes']), - )) - - -@register('clear') -def run_clear_intf(ifnames, iftypes, vif, vrrp): - for interface in filtered_interfaces(ifnames, iftypes, vif, vrrp): - print(f'Clearing {interface.ifname}') - interface.operational.clear_counters() - - -@register('reset') -def run_reset_intf(ifnames, iftypes, vif, vrrp): - for interface in filtered_interfaces(ifnames, iftypes, vif, vrrp): - interface.operational.reset_counters() - - -if __name__ == '__main__': - parser = argparse.ArgumentParser(add_help=False, description='Show interface information') - parser.add_argument('--intf', action="store", type=str, default='', help='only show the specified interface(s)') - parser.add_argument('--intf-type', action="store", type=str, default='', help='only show the specified interface type') - parser.add_argument('--action', action="store", type=str, default='show', help='action to perform') - parser.add_argument('--vif', action='store_true', default=False, help="only show vif interfaces") - parser.add_argument('--vrrp', action='store_true', default=False, help="only show vrrp interfaces") - parser.add_argument('--help', action='store_true', default=False, help="show help") - - args = parser.parse_args() - - def missing(*args): - print('Invalid action [{args.action}]') - usage() - - actions.get(args.action, missing)( - [_ for _ in args.intf.split(' ') if _], - [_ for _ in args.intf_type.split(' ') if _], - args.vif, - args.vrrp - ) -- cgit v1.2.3 From 657f5c1a08351c7740ff74cc112321d8f4e2155c Mon Sep 17 00:00:00 2001 From: Viacheslav Hletenko Date: Fri, 31 Mar 2023 13:09:21 +0000 Subject: T5125: Add op-mode for sFlow based on hsflowd Add op-mode for sFlow based on hsflowd "show sflow" Add machine readable format '--raw' and formatted output --- data/templates/sflow/hsflowd.conf.j2 | 1 + op-mode-definitions/sflow.xml.in | 15 +++++ smoketest/scripts/cli/test_system_sflow.py | 1 + src/op_mode/sflow.py | 102 +++++++++++++++++++++++++++++ 4 files changed, 119 insertions(+) create mode 100644 op-mode-definitions/sflow.xml.in create mode 100755 src/op_mode/sflow.py (limited to 'src/op_mode') diff --git a/data/templates/sflow/hsflowd.conf.j2 b/data/templates/sflow/hsflowd.conf.j2 index 94f5939be..5000956bd 100644 --- a/data/templates/sflow/hsflowd.conf.j2 +++ b/data/templates/sflow/hsflowd.conf.j2 @@ -28,4 +28,5 @@ sflow { {% if drop_monitor_limit is vyos_defined %} dropmon { limit={{ drop_monitor_limit }} start=on sw=on hw=off } {% endif %} + dbus { } } diff --git a/op-mode-definitions/sflow.xml.in b/op-mode-definitions/sflow.xml.in new file mode 100644 index 000000000..9f02dacda --- /dev/null +++ b/op-mode-definitions/sflow.xml.in @@ -0,0 +1,15 @@ + + + + + + + + Show sFlow statistics + + + sudo ${vyos_op_scripts_dir}/sflow.py show + + + + diff --git a/smoketest/scripts/cli/test_system_sflow.py b/smoketest/scripts/cli/test_system_sflow.py index fef88b56a..1aec050a4 100755 --- a/smoketest/scripts/cli/test_system_sflow.py +++ b/smoketest/scripts/cli/test_system_sflow.py @@ -91,6 +91,7 @@ class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase): self.assertIn(f'collector {{ ip = {server} udpport = {port} }}', hsflowd) self.assertIn(f'collector {{ ip = {local_server} udpport = {default_port} }}', hsflowd) self.assertIn(f'dropmon {{ limit={mon_limit} start=on sw=on hw=off }}', hsflowd) + self.assertIn('dbus { }', hsflowd) for interface in Section.interfaces('ethernet'): self.assertIn(f'pcap {{ dev={interface} }}', hsflowd) diff --git a/src/op_mode/sflow.py b/src/op_mode/sflow.py new file mode 100755 index 000000000..1ff006274 --- /dev/null +++ b/src/op_mode/sflow.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 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 . + +import dbus +import sys + +from tabulate import tabulate + +from vyos.configquery import ConfigTreeQuery +from vyos.util import cmd + +import vyos.opmode + + +def _get_raw_sflow(): + bus = dbus.SystemBus() + config = ConfigTreeQuery() + + interfaces = config.values('system sflow interface') + servers = config.list_nodes('system sflow server') + + sflow = bus.get_object('net.sflow.hsflowd', '/net/sflow/hsflowd') + sflow_telemetry = dbus.Interface( + sflow, dbus_interface='net.sflow.hsflowd.telemetry') + agent_address = sflow_telemetry.GetAgent() + samples_dropped = int(sflow_telemetry.Get('dropped_samples')) + samples_packet_sent = int(sflow_telemetry.Get('flow_samples')) + samples_counter_sent = int(sflow_telemetry.Get('counter_samples')) + datagrams_sent = int(sflow_telemetry.Get('datagrams')) + rtmetric_samples = int(sflow_telemetry.Get('rtmetric_samples')) + samples_suppressed = int(sflow_telemetry.Get('flow_samples_suppressed')) + counter_samples_suppressed = int( + sflow_telemetry.Get("counter_samples_suppressed")) + version = sflow_telemetry.GetVersion() + + sflow_dict = { + 'agent_address': agent_address, + 'sflow_interfaces': interfaces, + 'sflow_servers': servers, + 'counter_samples_sent': samples_counter_sent, + 'datagrams_sent': datagrams_sent, + 'packet_samples_dropped': samples_dropped, + 'packet_samples_sent': samples_packet_sent, + 'rtmetric_samples': rtmetric_samples, + 'flow_samples_suppressed': samples_suppressed, + 'counter_samples_suppressed': counter_samples_suppressed, + 'hsflowd_version': version + } + return sflow_dict + + +def _get_formatted_sflow(data): + table = [ + ['Agent address', f'{data.get("agent_address")}'], + ['sFlow interfaces', f'{data.get("sflow_interfaces", "n/a")}'], + ['sFlow servers', f'{data.get("sflow_servers", "n/a")}'], + ['Datagrams sent', f'{data.get("datagrams_sent")}'], + ['Packet samples sent', f'{data.get("packet_samples_sent")}'], + ['Packet samples dropped', f'{data.get("packet_samples_dropped")}'], + ['Counter samples sent', f'{data.get("counter_samples_sent")}'], + ['Flow samples suppressed', f'{data.get("flow_samples_suppressed")}'], + ['Counter samples suppressed', f'{data.get("counter_samples_suppressed")}'] + ] + + return tabulate(table) + + +def show(raw: bool): + + config = ConfigTreeQuery() + if not config.exists('system sflow'): + raise vyos.opmode.UnconfiguredSubsystem( + '"system sflow" is not configured!') + + sflow_data = _get_raw_sflow() + if raw: + return sflow_data + else: + return _get_formatted_sflow(sflow_data) + + +if __name__ == '__main__': + try: + res = vyos.opmode.run(sys.modules[__name__]) + if res: + print(res) + except (ValueError, vyos.opmode.Error) as e: + print(e) + sys.exit(1) -- cgit v1.2.3 From 7d6731435410b16a2497dc0bc156fbde77797fb3 Mon Sep 17 00:00:00 2001 From: Viacheslav Hletenko Date: Sat, 1 Apr 2023 18:44:25 +0000 Subject: T5125: Extend op-mode show sflow add new metric Add new metric, the number of packet-drop-events sent --- src/op_mode/sflow.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/op_mode') diff --git a/src/op_mode/sflow.py b/src/op_mode/sflow.py index 1ff006274..ddb8bf44d 100755 --- a/src/op_mode/sflow.py +++ b/src/op_mode/sflow.py @@ -37,6 +37,7 @@ def _get_raw_sflow(): sflow, dbus_interface='net.sflow.hsflowd.telemetry') agent_address = sflow_telemetry.GetAgent() samples_dropped = int(sflow_telemetry.Get('dropped_samples')) + samples_drop_events_sent = int(sflow_telemetry.Get('event_samples')) samples_packet_sent = int(sflow_telemetry.Get('flow_samples')) samples_counter_sent = int(sflow_telemetry.Get('counter_samples')) datagrams_sent = int(sflow_telemetry.Get('datagrams')) @@ -52,6 +53,7 @@ def _get_raw_sflow(): 'sflow_servers': servers, 'counter_samples_sent': samples_counter_sent, 'datagrams_sent': datagrams_sent, + 'samples_drop_events_sent': samples_drop_events_sent, 'packet_samples_dropped': samples_dropped, 'packet_samples_sent': samples_packet_sent, 'rtmetric_samples': rtmetric_samples, @@ -67,10 +69,11 @@ def _get_formatted_sflow(data): ['Agent address', f'{data.get("agent_address")}'], ['sFlow interfaces', f'{data.get("sflow_interfaces", "n/a")}'], ['sFlow servers', f'{data.get("sflow_servers", "n/a")}'], + ['Counter samples sent', f'{data.get("counter_samples_sent")}'], ['Datagrams sent', f'{data.get("datagrams_sent")}'], ['Packet samples sent', f'{data.get("packet_samples_sent")}'], ['Packet samples dropped', f'{data.get("packet_samples_dropped")}'], - ['Counter samples sent', f'{data.get("counter_samples_sent")}'], + ['Samples drop events sent', f'{data.get("samples_drop_events_sent")}'], ['Flow samples suppressed', f'{data.get("flow_samples_suppressed")}'], ['Counter samples suppressed', f'{data.get("counter_samples_suppressed")}'] ] -- cgit v1.2.3 From 1b7534855f92ea307059bd01258746e45d6f79a5 Mon Sep 17 00:00:00 2001 From: Viacheslav Hletenko Date: Mon, 3 Apr 2023 09:44:18 +0000 Subject: T5125: Sflow op-mode add event_samples_suppressed option Add "Packet drops suppressed" option Rename "Samples drop events sent" to "Packet drops sent" --- src/op_mode/sflow.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/op_mode') diff --git a/src/op_mode/sflow.py b/src/op_mode/sflow.py index ddb8bf44d..88f70d6bd 100755 --- a/src/op_mode/sflow.py +++ b/src/op_mode/sflow.py @@ -37,11 +37,12 @@ def _get_raw_sflow(): sflow, dbus_interface='net.sflow.hsflowd.telemetry') agent_address = sflow_telemetry.GetAgent() samples_dropped = int(sflow_telemetry.Get('dropped_samples')) - samples_drop_events_sent = int(sflow_telemetry.Get('event_samples')) + packet_drop_sent = int(sflow_telemetry.Get('event_samples')) samples_packet_sent = int(sflow_telemetry.Get('flow_samples')) samples_counter_sent = int(sflow_telemetry.Get('counter_samples')) datagrams_sent = int(sflow_telemetry.Get('datagrams')) rtmetric_samples = int(sflow_telemetry.Get('rtmetric_samples')) + event_samples_suppressed = int(sflow_telemetry.Get('event_samples_suppressed')) samples_suppressed = int(sflow_telemetry.Get('flow_samples_suppressed')) counter_samples_suppressed = int( sflow_telemetry.Get("counter_samples_suppressed")) @@ -53,10 +54,11 @@ def _get_raw_sflow(): 'sflow_servers': servers, 'counter_samples_sent': samples_counter_sent, 'datagrams_sent': datagrams_sent, - 'samples_drop_events_sent': samples_drop_events_sent, + 'packet_drop_sent': packet_drop_sent, 'packet_samples_dropped': samples_dropped, 'packet_samples_sent': samples_packet_sent, 'rtmetric_samples': rtmetric_samples, + 'event_samples_suppressed': event_samples_suppressed, 'flow_samples_suppressed': samples_suppressed, 'counter_samples_suppressed': counter_samples_suppressed, 'hsflowd_version': version @@ -73,7 +75,8 @@ def _get_formatted_sflow(data): ['Datagrams sent', f'{data.get("datagrams_sent")}'], ['Packet samples sent', f'{data.get("packet_samples_sent")}'], ['Packet samples dropped', f'{data.get("packet_samples_dropped")}'], - ['Samples drop events sent', f'{data.get("samples_drop_events_sent")}'], + ['Packet drops sent', f'{data.get("packet_drop_sent")}'], + ['Packet drops suppressed', f'{data.get("event_samples_suppressed")}'], ['Flow samples suppressed', f'{data.get("flow_samples_suppressed")}'], ['Counter samples suppressed', f'{data.get("counter_samples_suppressed")}'] ] -- cgit v1.2.3 From 3f3621b6874354fbf824171bfe2e3aed6360482f Mon Sep 17 00:00:00 2001 From: Indrajit Raychaudhuri Date: Mon, 3 Apr 2023 18:24:29 -0500 Subject: dns: T5144: Improve dns dynamic status output Improve and fix the output of dynamic dns status to be compatible with new ddclient cache format. Additional details: - The status output is now formatted as a table with per-host dual-stack information in rows. Columns not having actual value present in the output will be kept empty. - The 'Last update' column is now formatted in Local time format instead of UTC. --- src/op_mode/dynamic_dns.py | 72 +++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 39 deletions(-) (limited to 'src/op_mode') diff --git a/src/op_mode/dynamic_dns.py b/src/op_mode/dynamic_dns.py index 263a3b6a5..2cba33cc8 100755 --- a/src/op_mode/dynamic_dns.py +++ b/src/op_mode/dynamic_dns.py @@ -16,69 +16,63 @@ import os import argparse -import jinja2 import sys import time +from tabulate import tabulate from vyos.config import Config from vyos.util import call cache_file = r'/run/ddclient/ddclient.cache' -OUT_TMPL_SRC = """ -{% for entry in hosts %} -ip address : {{ entry.ip }} -host-name : {{ entry.host }} -last update : {{ entry.time }} -update-status: {{ entry.status }} +columns = { + 'host': 'Hostname', + 'ipv4': 'IPv4 address', + 'status-ipv4': 'IPv4 status', + 'ipv6': 'IPv6 address', + 'status-ipv6': 'IPv6 status', + 'mtime': 'Last update', +} + + +def _get_formatted_host_records(host_data): + data_entries = [] + for entry in host_data: + data_entries.append([entry.get(key) for key in columns.keys()]) + + header = columns.values() + output = tabulate(data_entries, header, numalign='left') + return output -{% endfor %} -""" def show_status(): # A ddclient status file must not always exist if not os.path.exists(cache_file): sys.exit(0) - data = { - 'hosts': [] - } + data = [] with open(cache_file, 'r') as f: for line in f: if line.startswith('#'): continue - outp = { - 'host': '', - 'ip': '', - 'time': '' - } - - if 'host=' in line: - host = line.split('host=')[1] - if host: - outp['host'] = host.split(',')[0] - - if 'ip=' in line: - ip = line.split('ip=')[1] - if ip: - outp['ip'] = ip.split(',')[0] - - if 'mtime=' in line: - mtime = line.split('mtime=')[1] - if mtime: - outp['time'] = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(int(mtime.split(',')[0], base=10))) + props = {} + # ddclient cache rows have properties in 'key=value' format separated by comma + # we pick up the ones we are interested in + for kvraw in line.split(' ')[0].split(','): + k, v = kvraw.split('=') + if k in columns.keys(): + props[k] = v - if 'status=' in line: - status = line.split('status=')[1] - if status: - outp['status'] = status.split(',')[0] + # Convert mtime to human readable format + if 'mtime' in props: + props['mtime'] = time.strftime( + "%Y-%m-%d %H:%M:%S", time.localtime(int(props['mtime'], base=10))) - data['hosts'].append(outp) + data.append(props) - tmpl = jinja2.Template(OUT_TMPL_SRC) - print(tmpl.render(data)) + print(_get_formatted_host_records(data)) def update_ddns(): -- cgit v1.2.3 From f14de93cdb1d1e366603d10d8d51994d9eb4bb8b Mon Sep 17 00:00:00 2001 From: mkorobeinikov <92354771+mkorobeinikov@users.noreply.github.com> Date: Sun, 2 Apr 2023 07:29:14 +0300 Subject: T5137: refactoring the tech-support command Refactoring the tech-support command from .sh to .py --- op-mode-definitions/show-techsupport_report.xml.in | 17 + src/op_mode/show_techsupport_report.py | 438 +++++++++++++++++++++ 2 files changed, 455 insertions(+) create mode 100644 op-mode-definitions/show-techsupport_report.xml.in create mode 100644 src/op_mode/show_techsupport_report.py (limited to 'src/op_mode') diff --git a/op-mode-definitions/show-techsupport_report.xml.in b/op-mode-definitions/show-techsupport_report.xml.in new file mode 100644 index 000000000..6f38105f8 --- /dev/null +++ b/op-mode-definitions/show-techsupport_report.xml.in @@ -0,0 +1,17 @@ + + + + + + + + + Show consolidated tech-support report (contains private information) + + ${vyos_op_scripts_dir}/show_techsupport_report.py + + + + + + diff --git a/src/op_mode/show_techsupport_report.py b/src/op_mode/show_techsupport_report.py new file mode 100644 index 000000000..13ed9a3c1 --- /dev/null +++ b/src/op_mode/show_techsupport_report.py @@ -0,0 +1,438 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 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 vyos.util import call +import os + + +def header(cmd): + print(16 * '-' + '\n' + cmd + '\n' + 16 * '-') + return + + +# get intefaces info +interfaces_list = os.popen('ls /sys/class/net/ | grep eth').read().split() +bridges_list = os.popen('ls /sys/class/net/ | grep br').read().split() + +###################### THE PART OF CONFIGURATION ###################### + +cmd_list_conf = [ + "VyOS Version and Package Changes%/opt/vyatta/bin/vyatta-op-cmd-wrapper show version all", + "Configuration File%cat /opt/vyatta/etc/config/config.boot", + "Running configuration%/opt/vyatta/bin/vyatta-op-cmd-wrapper show configuration", + "Package Repository Configuration File%cat /etc/apt/sources.list", + "User Startup Scripts%cat /etc/rc.local", + "Quagga Configuration%vtysh -c 'show run'" +] + + +def CONFIGURATION(cmd): + for command_line in cmd: + line = command_line.split('%') + head = line[0] + command = line[1] + header(head) + call(command) + return + + +###################### THE PART OF INTERFACES ###################### + +cmd_list_int = [ + "Interfaces%/opt/vyatta/bin/vyatta-op-cmd-wrapper show interfaces", + "Ethernet", + "Interface statistics%ip -s link show", + "Physical Interface statistics for%ethtool -S", + "Physical Interface Details for %/opt/vyatta/bin/vyatta-op-cmd-wrapper show interfaces ethernet%ethtool -k $eth", + "ARP Table (Total entries)%/opt/vyatta/bin/vyatta-op-cmd-wrapper show arp", + "Number of incomplete entries in ARP table%show arp | grep incomplete | wc -l", + "Bridges" +] + + +def INTERFACES(cmd): + for command_line in cmd: + line = command_line.split('%') + head = line[0] + if command_line.startswith("Ethernet"): + header(command_line) + elif command_line.startswith("Physical Interface statistics"): + for command_interface in interfaces_list: + header(f'{head} {command_interface}') + call(f'{line[1]} {command_interface}') + elif command_line.startswith("Physical Interface Details"): + for command_interface in interfaces_list: + header(f'{head} {command_interface}') + call(f'{line[1]} {command_interface} physical') + call(f'{line[2]} {command_interface}') + elif command_line.startswith("Bridges"): + header(command_line) + for command_interface in bridges_list: + header(f'Information for {command_interface}') + call(f'/sbin/brctl showstp {command_interface}') + call(f'/sbin/brctl showmacs {command_interface}') + else: + command = line[1] + header(head) + call(command) + return + + +###################### THE PART OF ROUTING ###################### + +cmd_list_route = [ + "show ip route bgp", + "show ip route cache", + "show ip route connected", + "show ip route forward", + "show ip route isis", + "show ip route kernel", + "show ip route ospf", + "show ip route rip", + "show ip route static", + "show ip route summary", + "show ip route supernets-only", + "show ip route table", + "show ip route tag", + "show ip route vrf", + "show ipv6 route bgp", + "show ipv6 route cache", + "show ipv6 route connected", + "show ipv6 route forward", + "show ipv6 route isis", + "show ipv6 route kernel", + "show ipv6 route ospf", + "show ipv6 route rip", + "show ipv6 route static", + "show ipv6 route summary", + "show ipv6 route supernets-only", + "show ipv6 route table", + "show ipv6 route tag", + "show ipv6 route vrf", +] + + +def ROUTING(cmd): + for command_line in cmd: + head = command_line + command = command_line + header(head) + call(f'/opt/vyatta/bin/vyatta-op-cmd-wrapper {command}') + return + + +###################### THE PART OF IPTABLES ###################### + +cmd_list_iptables = [ + "Filter Chain Details%sudo /sbin/iptables -L -vn", + "Nat Chain Details%sudo /sbin/iptables -t nat -L -vn", + "Mangle Chain Details%sudo /sbin/iptables -t mangle -L -vn", + "Raw Chain Details%sudo /sbin/iptables -t raw -L -vn", + "Save Iptables Rule-Set%sudo iptables-save -c" +] + + +def IPTABLES(cmd): + for command_line in cmd: + line = command_line.split('%') + head = line[0] + command = line[1] + header(head) + call(command) + return + + +###################### THE PART OF SYSTEM ###################### + +cmd_list_system = [ + "Show System Image Version%show system image version", + "Show System Image Storage%show system image storage", + "Current Time%date", + "Installed Packages%dpkg -l", + "Loaded Modules%cat /proc/modules", + "CPU", + "Installed CPU/s%lscpu", + "Cumulative CPU Time Used by Running Processes%top -n1 -b -S", + "Hardware Interrupt Counters%cat /proc/interrupts", + "Load Average%cat /proc/loadavg" +] + + +def SYSTEM(cmd): + for command_line in cmd: + line = command_line.split('%') + head = line[0] + if command_line.startswith("CPU"): + header(command_line) + elif line[1].startswith("show"): + header(head) + command = line[1] + call(f'/opt/vyatta/bin/vyatta-op-cmd-wrapper {command}') + else: + header(head) + command = line[1] + call(command) + return + + +###################### THE PART OF PROCESSES ###################### + +cmd_list_processes = [ + "Running Processes%ps -ef", + "Memory", + "Installed Memory%cat /proc/meminfo", + " Memory Usage%free", + "Storage", + "Devices%cat /proc/devices", + "Partitions%cat /proc/partitions", + "Partitioning for disks%fdisk -l /dev/" +] + + +def PROCESSES(cmd): + for command_line in cmd: + line = command_line.split('%') + head = line[0] + if command_line.startswith("Memory"): + header(command_line) + elif command_line.startswith("Storage"): + header(command_line) + elif command_line.startswith("Partitioning for disks"): + header(head) + disks = set() + with open('/proc/partitions') as partitions_file: + for line in partitions_file: + fields = line.strip().split() + if len(fields) == 4 and fields[3].isalpha() and fields[3] != 'name': + disks.add(fields[3]) + for disk in disks: + call(f'fdisk -l /dev/{disk}') + else: + header(head) + command = line[1] + call(command) + return + + +###################### THE PART OF CORE SECTION ###################### + +cmd_list_core = [ + "Mounts%cat /proc/mounts", + "Diskstats%cat /proc/diskstats", + "Hard Drive Usage%df -h -x squashfs", + # "General System", + "Boot Messages%cat /var/log/dmesg", + "Recent Kernel messages (dmesg)%dmesg", + "PCI Info%sudo lspci -vvx", + "PCI Vendor and Device Codes%sudo lspci -nn", + # "System Info%${vyatta_bindir}/vyatta-show-dmi", + "GRUB Command line%cat /proc/cmdline", + "Open Ports%sudo lsof -P -n -i", + "System Startup Files%ls -l /etc/rc?.d", + "Login History%last -ix", + "Recent Log Messages%tail -n 250 /var/log/messages", + "NTP%/opt/vyatta/bin/vyatta-op-cmd-wrapper show ntp", +] + + +def CORE(cmd): + for command_line in cmd: + line = command_line.split('%') + command = line[1] + header(line[0]) + call(command) + return + + +###################### THE PART OF VyOS INFORMATION ###################### + +cmd_list_vyos = [ + "BGP", + "header BGP Summary", + "show ip bgp summary", + "header BGP Neighbors", + "show ip bgp neighbors", + "header BGP Debugging Information", + "show monitoring protocols bgp", + "CLUSTERING", + "Cluster Status", + "show cluster status", + "DHCP Server", + "DHCP Leases", + "show dhcp server leases", + "DHCP Statistics", + "show dhcp server statistics", + "DHCP Client", + "DHCP Client Leases", + "show dhcp client leases", + "DHCPV6 Server", + "DHCPV6 Server Status", + "show dhcpv6 server status", + "DHCPV6 Server Leases", + "show dhcpv6 server leases", + "DHCPV6 Relay", + "DHCPV6 Relay Status", + "show dhcpv6 relay-agent status", + "DHCPV6 Client", + "DHCPV6 Client Leases", + "show dhcpv6 client leases", + "DNS", + "DNS Dynamic Status", + "show dns dynamic status", + "DNS Forwarding Statistics", + "show dns forwarding statistics", + "DNS Forwarding Nameservers", + "show dns forwarding nameservers", + "FIREWALL", + "Firewall Group", + "show firewall group", + "Firewall Summary", + "show firewall summary", + "Firewall Statistics", + "show firewall statistics", + "IPSec", + "IPSec Status", + "show vpn ipsec status", + "IPSec sa", + "show vpn ipsec sa", + "IPSec sa Detail", + "show vpn ipsec sa detail", + "IPSec sa Statistics", + "show vpn ipsec sa statistics", + "/etc/ipsec.conf", + "cat /etc/ipsec.conf", + "/etc/ipsec.secrets", + "cat /etc/ipsec.secrets", + "NAT", + "NAT Rules", + "show nat rules", + "NAT Statistics", + "show nat statistics", + "NAT Translations Detail", + "show nat translations detail", + "FlowAccounting", + "show flow-accounting", + "OPENVPN", + "OpenVPN Interfaces", + "show interfaces openvpn detail", + "OpenVPN Server Status", + "show openvpn status server", + "OSPF", + "OSPF Neighbor", + "show ip ospf neighbor", + "OSPF Route", + "show ip ospf route", + "OSPF Debugging Information", + "show monitoring protocols ospf", + "OSPFV3", + "OSPFV3 Debugging Information", + "show monitoring protocols ospfv3", + "Policy", + "IP Route Maps", + "show ip protocol", + "Route-Map", + "show route-map", + # header IP Access Lists + # show ip access-lists + "IP Community List", + "show ip community-list", + "Traffic Policy", + "Current Traffic Policies", + "show queueing", + "RIP", + "IP RIP", + "show ip rip", + "RIP Status", + "show ip rip status", + "RIP Debugging Information", + "show monitoring protocols rip", + "RIPNG", + "RIPNG Debugging Information", + "show monitoring protocols ripng", + "VPN-L2TP", + "VPN ike secrets", + "show vpn ike secrets", + "VPN rsa-keys", + "show vpn ike rsa-keys", + "VPN ike sa", + "show vpn ike sa", + "VPN ike Status", + "show vpn ike status", + "VPN Remote-Access", + "show vpn remote-access", + "VPN Debug Detail", + "show vpn debug detail", + "VPN-PPTP", + "VPN Remote-Access", + "show vpn remote-access", + "VRRP", + # XXX: not checking if configured, we'd have to walk all VIFs + "show vrrp detail", + "WAN LOAD BALANCING", + "Wan Load Balance", + "show wan-load-balance", + "Wan Load Balance Status", + "show wan-load-balance status", + "Wan Load Balance Connection", + "show wan-load-balance connection", + "WEBPROXY/URL-FILTERING", + "WebProxy Blacklist Categories", + "show webproxy blacklist categories", + "WebProxy Blacklist Domains", + "show webproxy blacklist domains", + "WebProxy Blacklist URLs", + "show webproxy blacklist urls", + "WebProxy Blacklist Log", + "show webproxy blacklist log summary", +] + + +def VyOS(cmd): + for command_line in cmd: + if command_line.startswith("show"): + call(f'/opt/vyatta/bin/vyatta-op-cmd-wrapper {command_line}') + elif command_line.startswith("cat"): + call(command_line) + else: + header(command_line) + return + + +###################### execute all the commands ###################### + +header('CONFIGURATION') +CONFIGURATION(cmd_list_conf) + +header('INTERFACES') +INTERFACES(cmd_list_int) + +header('ROUTING') +ROUTING(cmd_list_route) + +header('IPTABLES') +IPTABLES(cmd_list_iptables) + +header('SYSTEM') +SYSTEM(cmd_list_system) + +header('PROCESSES') +PROCESSES(cmd_list_processes) + +header('CORE') +CORE(cmd_list_core) + +header('VyOS Information') +VyOS(cmd_list_vyos) -- cgit v1.2.3 From d5eafd464047ee293c68c2fe6e1ba4e6e4d60585 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Fri, 7 Apr 2023 13:20:53 -0500 Subject: openvpn: T5149: do not raise error in case of disabled interface --- src/op_mode/openvpn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/op_mode') diff --git a/src/op_mode/openvpn.py b/src/op_mode/openvpn.py index 37fdbcbeb..5a1a4914d 100755 --- a/src/op_mode/openvpn.py +++ b/src/op_mode/openvpn.py @@ -63,7 +63,7 @@ def _get_interface_status(mode: str, interface: str) -> dict: } if not os.path.exists(status_file): - raise vyos.opmode.DataUnavailable('No information for interface {interface}') + return data with open(status_file, 'r') as f: lines = f.readlines() -- cgit v1.2.3 From 4fac9c122887c91af4e6d51b4e2eb01977196852 Mon Sep 17 00:00:00 2001 From: Viacheslav Hletenko Date: Fri, 7 Apr 2023 12:28:46 +0000 Subject: T4770: Ability to get OpenVPN iface state and description for raw --- src/op_mode/openvpn.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'src/op_mode') diff --git a/src/op_mode/openvpn.py b/src/op_mode/openvpn.py index 37fdbcbeb..6ef80919b 100755 --- a/src/op_mode/openvpn.py +++ b/src/op_mode/openvpn.py @@ -16,6 +16,7 @@ # # +import json import os import sys import typing @@ -25,6 +26,7 @@ import vyos.opmode from vyos.util import bytes_to_human from vyos.util import commit_in_progress from vyos.util import call +from vyos.util import rc_cmd from vyos.config import Config ArgMode = typing.Literal['client', 'server', 'site_to_site'] @@ -142,6 +144,25 @@ def _get_interface_status(mode: str, interface: str) -> dict: return data + +def _get_interface_state(iface): + rc, out = rc_cmd(f'ip --json link show dev {iface}') + try: + data = json.loads(out) + except: + return 'DOWN' + return data[0].get('operstate', 'DOWN') + + +def _get_interface_description(iface): + rc, out = rc_cmd(f'ip --json link show dev {iface}') + try: + data = json.loads(out) + except: + return '' + return data[0].get('ifalias', '') + + def _get_raw_data(mode: str) -> list: data: list = [] conf = Config() @@ -154,6 +175,8 @@ def _get_raw_data(mode: str) -> list: conf_dict[x]['mode'].replace('-', '_') == mode] for intf in interfaces: d = _get_interface_status(mode, intf) + d['state'] = _get_interface_state(intf) + d['description'] = _get_interface_description(intf) d['local_host'] = conf_dict[intf].get('local-host', '') d['local_port'] = conf_dict[intf].get('local-port', '') if conf.exists(f'interfaces openvpn {intf} server client'): -- cgit v1.2.3 From 0531ea22050d66e6da3adc87cad57ea527cea71b Mon Sep 17 00:00:00 2001 From: Viacheslav Hletenko Date: Mon, 17 Apr 2023 18:00:05 +0000 Subject: T5137: Refactoring show tech-support report Split script to small functions for flexible output reports. Improve header for commands. Each funciton easily can be modified or extended. Remove splitting command/output via percent symbol. Remove old commands and directiories like /etc/rc.local, iptables, brctl, etc. Remove ethtool operation for subinterfaces. Extend ethtool debug output. Add correct nftables command. --- src/op_mode/show_techsupport_report.py | 703 +++++++++++++-------------------- 1 file changed, 284 insertions(+), 419 deletions(-) (limited to 'src/op_mode') diff --git a/src/op_mode/show_techsupport_report.py b/src/op_mode/show_techsupport_report.py index 13ed9a3c1..782004144 100644 --- a/src/op_mode/show_techsupport_report.py +++ b/src/op_mode/show_techsupport_report.py @@ -14,425 +14,290 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from vyos.util import call import os +from typing import List +from vyos.util import rc_cmd +from vyos.ifconfig import Section +from vyos.ifconfig import Interface -def header(cmd): - print(16 * '-' + '\n' + cmd + '\n' + 16 * '-') - return - - -# get intefaces info -interfaces_list = os.popen('ls /sys/class/net/ | grep eth').read().split() -bridges_list = os.popen('ls /sys/class/net/ | grep br').read().split() - -###################### THE PART OF CONFIGURATION ###################### - -cmd_list_conf = [ - "VyOS Version and Package Changes%/opt/vyatta/bin/vyatta-op-cmd-wrapper show version all", - "Configuration File%cat /opt/vyatta/etc/config/config.boot", - "Running configuration%/opt/vyatta/bin/vyatta-op-cmd-wrapper show configuration", - "Package Repository Configuration File%cat /etc/apt/sources.list", - "User Startup Scripts%cat /etc/rc.local", - "Quagga Configuration%vtysh -c 'show run'" -] - - -def CONFIGURATION(cmd): - for command_line in cmd: - line = command_line.split('%') - head = line[0] - command = line[1] - header(head) - call(command) - return - - -###################### THE PART OF INTERFACES ###################### - -cmd_list_int = [ - "Interfaces%/opt/vyatta/bin/vyatta-op-cmd-wrapper show interfaces", - "Ethernet", - "Interface statistics%ip -s link show", - "Physical Interface statistics for%ethtool -S", - "Physical Interface Details for %/opt/vyatta/bin/vyatta-op-cmd-wrapper show interfaces ethernet%ethtool -k $eth", - "ARP Table (Total entries)%/opt/vyatta/bin/vyatta-op-cmd-wrapper show arp", - "Number of incomplete entries in ARP table%show arp | grep incomplete | wc -l", - "Bridges" -] - - -def INTERFACES(cmd): - for command_line in cmd: - line = command_line.split('%') - head = line[0] - if command_line.startswith("Ethernet"): - header(command_line) - elif command_line.startswith("Physical Interface statistics"): - for command_interface in interfaces_list: - header(f'{head} {command_interface}') - call(f'{line[1]} {command_interface}') - elif command_line.startswith("Physical Interface Details"): - for command_interface in interfaces_list: - header(f'{head} {command_interface}') - call(f'{line[1]} {command_interface} physical') - call(f'{line[2]} {command_interface}') - elif command_line.startswith("Bridges"): - header(command_line) - for command_interface in bridges_list: - header(f'Information for {command_interface}') - call(f'/sbin/brctl showstp {command_interface}') - call(f'/sbin/brctl showmacs {command_interface}') - else: - command = line[1] - header(head) - call(command) - return - - -###################### THE PART OF ROUTING ###################### - -cmd_list_route = [ - "show ip route bgp", - "show ip route cache", - "show ip route connected", - "show ip route forward", - "show ip route isis", - "show ip route kernel", - "show ip route ospf", - "show ip route rip", - "show ip route static", - "show ip route summary", - "show ip route supernets-only", - "show ip route table", - "show ip route tag", - "show ip route vrf", - "show ipv6 route bgp", - "show ipv6 route cache", - "show ipv6 route connected", - "show ipv6 route forward", - "show ipv6 route isis", - "show ipv6 route kernel", - "show ipv6 route ospf", - "show ipv6 route rip", - "show ipv6 route static", - "show ipv6 route summary", - "show ipv6 route supernets-only", - "show ipv6 route table", - "show ipv6 route tag", - "show ipv6 route vrf", -] - - -def ROUTING(cmd): - for command_line in cmd: - head = command_line - command = command_line - header(head) - call(f'/opt/vyatta/bin/vyatta-op-cmd-wrapper {command}') - return - - -###################### THE PART OF IPTABLES ###################### - -cmd_list_iptables = [ - "Filter Chain Details%sudo /sbin/iptables -L -vn", - "Nat Chain Details%sudo /sbin/iptables -t nat -L -vn", - "Mangle Chain Details%sudo /sbin/iptables -t mangle -L -vn", - "Raw Chain Details%sudo /sbin/iptables -t raw -L -vn", - "Save Iptables Rule-Set%sudo iptables-save -c" -] - - -def IPTABLES(cmd): - for command_line in cmd: - line = command_line.split('%') - head = line[0] - command = line[1] - header(head) - call(command) - return - - -###################### THE PART OF SYSTEM ###################### - -cmd_list_system = [ - "Show System Image Version%show system image version", - "Show System Image Storage%show system image storage", - "Current Time%date", - "Installed Packages%dpkg -l", - "Loaded Modules%cat /proc/modules", - "CPU", - "Installed CPU/s%lscpu", - "Cumulative CPU Time Used by Running Processes%top -n1 -b -S", - "Hardware Interrupt Counters%cat /proc/interrupts", - "Load Average%cat /proc/loadavg" -] - - -def SYSTEM(cmd): - for command_line in cmd: - line = command_line.split('%') - head = line[0] - if command_line.startswith("CPU"): - header(command_line) - elif line[1].startswith("show"): - header(head) - command = line[1] - call(f'/opt/vyatta/bin/vyatta-op-cmd-wrapper {command}') - else: - header(head) - command = line[1] - call(command) - return - - -###################### THE PART OF PROCESSES ###################### - -cmd_list_processes = [ - "Running Processes%ps -ef", - "Memory", - "Installed Memory%cat /proc/meminfo", - " Memory Usage%free", - "Storage", - "Devices%cat /proc/devices", - "Partitions%cat /proc/partitions", - "Partitioning for disks%fdisk -l /dev/" -] - - -def PROCESSES(cmd): - for command_line in cmd: - line = command_line.split('%') - head = line[0] - if command_line.startswith("Memory"): - header(command_line) - elif command_line.startswith("Storage"): - header(command_line) - elif command_line.startswith("Partitioning for disks"): - header(head) - disks = set() - with open('/proc/partitions') as partitions_file: - for line in partitions_file: - fields = line.strip().split() - if len(fields) == 4 and fields[3].isalpha() and fields[3] != 'name': - disks.add(fields[3]) - for disk in disks: - call(f'fdisk -l /dev/{disk}') - else: - header(head) - command = line[1] - call(command) - return - - -###################### THE PART OF CORE SECTION ###################### - -cmd_list_core = [ - "Mounts%cat /proc/mounts", - "Diskstats%cat /proc/diskstats", - "Hard Drive Usage%df -h -x squashfs", - # "General System", - "Boot Messages%cat /var/log/dmesg", - "Recent Kernel messages (dmesg)%dmesg", - "PCI Info%sudo lspci -vvx", - "PCI Vendor and Device Codes%sudo lspci -nn", - # "System Info%${vyatta_bindir}/vyatta-show-dmi", - "GRUB Command line%cat /proc/cmdline", - "Open Ports%sudo lsof -P -n -i", - "System Startup Files%ls -l /etc/rc?.d", - "Login History%last -ix", - "Recent Log Messages%tail -n 250 /var/log/messages", - "NTP%/opt/vyatta/bin/vyatta-op-cmd-wrapper show ntp", -] - - -def CORE(cmd): - for command_line in cmd: - line = command_line.split('%') - command = line[1] - header(line[0]) - call(command) - return - - -###################### THE PART OF VyOS INFORMATION ###################### - -cmd_list_vyos = [ - "BGP", - "header BGP Summary", - "show ip bgp summary", - "header BGP Neighbors", - "show ip bgp neighbors", - "header BGP Debugging Information", - "show monitoring protocols bgp", - "CLUSTERING", - "Cluster Status", - "show cluster status", - "DHCP Server", - "DHCP Leases", - "show dhcp server leases", - "DHCP Statistics", - "show dhcp server statistics", - "DHCP Client", - "DHCP Client Leases", - "show dhcp client leases", - "DHCPV6 Server", - "DHCPV6 Server Status", - "show dhcpv6 server status", - "DHCPV6 Server Leases", - "show dhcpv6 server leases", - "DHCPV6 Relay", - "DHCPV6 Relay Status", - "show dhcpv6 relay-agent status", - "DHCPV6 Client", - "DHCPV6 Client Leases", - "show dhcpv6 client leases", - "DNS", - "DNS Dynamic Status", - "show dns dynamic status", - "DNS Forwarding Statistics", - "show dns forwarding statistics", - "DNS Forwarding Nameservers", - "show dns forwarding nameservers", - "FIREWALL", - "Firewall Group", - "show firewall group", - "Firewall Summary", - "show firewall summary", - "Firewall Statistics", - "show firewall statistics", - "IPSec", - "IPSec Status", - "show vpn ipsec status", - "IPSec sa", - "show vpn ipsec sa", - "IPSec sa Detail", - "show vpn ipsec sa detail", - "IPSec sa Statistics", - "show vpn ipsec sa statistics", - "/etc/ipsec.conf", - "cat /etc/ipsec.conf", - "/etc/ipsec.secrets", - "cat /etc/ipsec.secrets", - "NAT", - "NAT Rules", - "show nat rules", - "NAT Statistics", - "show nat statistics", - "NAT Translations Detail", - "show nat translations detail", - "FlowAccounting", - "show flow-accounting", - "OPENVPN", - "OpenVPN Interfaces", - "show interfaces openvpn detail", - "OpenVPN Server Status", - "show openvpn status server", - "OSPF", - "OSPF Neighbor", - "show ip ospf neighbor", - "OSPF Route", - "show ip ospf route", - "OSPF Debugging Information", - "show monitoring protocols ospf", - "OSPFV3", - "OSPFV3 Debugging Information", - "show monitoring protocols ospfv3", - "Policy", - "IP Route Maps", - "show ip protocol", - "Route-Map", - "show route-map", - # header IP Access Lists - # show ip access-lists - "IP Community List", - "show ip community-list", - "Traffic Policy", - "Current Traffic Policies", - "show queueing", - "RIP", - "IP RIP", - "show ip rip", - "RIP Status", - "show ip rip status", - "RIP Debugging Information", - "show monitoring protocols rip", - "RIPNG", - "RIPNG Debugging Information", - "show monitoring protocols ripng", - "VPN-L2TP", - "VPN ike secrets", - "show vpn ike secrets", - "VPN rsa-keys", - "show vpn ike rsa-keys", - "VPN ike sa", - "show vpn ike sa", - "VPN ike Status", - "show vpn ike status", - "VPN Remote-Access", - "show vpn remote-access", - "VPN Debug Detail", - "show vpn debug detail", - "VPN-PPTP", - "VPN Remote-Access", - "show vpn remote-access", - "VRRP", - # XXX: not checking if configured, we'd have to walk all VIFs - "show vrrp detail", - "WAN LOAD BALANCING", - "Wan Load Balance", - "show wan-load-balance", - "Wan Load Balance Status", - "show wan-load-balance status", - "Wan Load Balance Connection", - "show wan-load-balance connection", - "WEBPROXY/URL-FILTERING", - "WebProxy Blacklist Categories", - "show webproxy blacklist categories", - "WebProxy Blacklist Domains", - "show webproxy blacklist domains", - "WebProxy Blacklist URLs", - "show webproxy blacklist urls", - "WebProxy Blacklist Log", - "show webproxy blacklist log summary", -] - - -def VyOS(cmd): - for command_line in cmd: - if command_line.startswith("show"): - call(f'/opt/vyatta/bin/vyatta-op-cmd-wrapper {command_line}') - elif command_line.startswith("cat"): - call(command_line) - else: - header(command_line) - return - - -###################### execute all the commands ###################### - -header('CONFIGURATION') -CONFIGURATION(cmd_list_conf) - -header('INTERFACES') -INTERFACES(cmd_list_int) - -header('ROUTING') -ROUTING(cmd_list_route) - -header('IPTABLES') -IPTABLES(cmd_list_iptables) - -header('SYSTEM') -SYSTEM(cmd_list_system) - -header('PROCESSES') -PROCESSES(cmd_list_processes) - -header('CORE') -CORE(cmd_list_core) - -header('VyOS Information') -VyOS(cmd_list_vyos) + +def print_header(command: str) -> None: + """Prints a command with headers '-'. + + Example: + + % print_header('Example command') + + --------------- + Example command + --------------- + """ + header_length = len(command) * '-' + print(f"\n{header_length}\n{command}\n{header_length}") + + +def execute_command(command: str, header_text: str) -> None: + """Executes a command and prints the output with a header. + + Example: + % execute_command('uptime', "Uptime of the system") + + -------------------- + Uptime of the system + -------------------- + 20:21:57 up 9:04, 5 users, load average: 0.00, 0.00, 0.0 + + """ + print_header(header_text) + try: + rc, output = rc_cmd(command) + print(output) + except Exception as e: + print(f"Error executing command: {command}") + print(f"Error message: {e}") + + +def op(cmd: str) -> str: + """Returns a command with the VyOS operational mode wrapper.""" + return f'/opt/vyatta/bin/vyatta-op-cmd-wrapper {cmd}' + + +def get_ethernet_interfaces() -> List[Interface]: + """Returns a list of Ethernet interfaces.""" + return Section.interfaces('ethernet') + + +def show_version() -> None: + """Prints the VyOS version and package changes.""" + execute_command(op('show version'), 'VyOS Version and Package Changes') + + +def show_config_file() -> None: + """Prints the contents of a configuration file with a header.""" + execute_command('cat /opt/vyatta/etc/config/config.boot', 'Configuration file') + + +def show_running_config() -> None: + """Prints the running configuration.""" + execute_command(op('show configuration'), 'Running configuration') + + +def show_package_repository_config() -> None: + """Prints the package repository configuration file.""" + execute_command('cat /etc/apt/sources.list', 'Package Repository Configuration File') + execute_command('ls -l /etc/apt/sources.list.d/', 'Repositories') + + +def show_user_startup_scripts() -> None: + """Prints the user startup scripts.""" + execute_command('cat /config/scripts/vyos-postconfig-bootup.script', 'User Startup Scripts') + + +def show_frr_config() -> None: + """Prints the FRR configuration.""" + execute_command('vtysh -c "show run"', 'FRR configuration') + + +def show_interfaces() -> None: + """Prints the interfaces.""" + execute_command(op('show interfaces'), 'Interfaces') + + +def show_interface_statistics() -> None: + """Prints the interface statistics.""" + execute_command('ip -s link show', 'Interface statistics') + + +def show_physical_interface_statistics() -> None: + """Prints the physical interface statistics.""" + execute_command('/usr/bin/true', 'Physical Interface statistics') + for iface in get_ethernet_interfaces(): + # Exclude vlans + if '.' in iface: + continue + execute_command(f'ethtool --driver {iface}', f'ethtool --driver {iface}') + execute_command(f'ethtool --statistics {iface}', f'ethtool --statistics {iface}') + execute_command(f'ethtool --show-ring {iface}', f'ethtool --show-ring {iface}') + execute_command(f'ethtool --show-coalesce {iface}', f'ethtool --show-coalesce {iface}') + execute_command(f'ethtool --pause {iface}', f'ethtool --pause {iface}') + execute_command(f'ethtool --show-features {iface}', f'ethtool --show-features {iface}') + execute_command(f'ethtool --phy-statistics {iface}', f'ethtool --phy-statistics {iface}') + execute_command('netstat --interfaces', 'netstat --interfaces') + execute_command('netstat --listening', 'netstat --listening') + execute_command('cat /proc/net/dev', 'cat /proc/net/dev') + + +def show_bridge() -> None: + """Show bridge interfaces.""" + execute_command(op('show bridge'), 'Show bridge') + + +def show_arp() -> None: + """Prints ARP entries.""" + execute_command(op('show arp'), 'ARP Table (Total entries)') + execute_command(op('show ipv6 neighbors'), 'show ipv6 neighbors') + + +def show_route() -> None: + """Prints routing information.""" + + cmd_list_route = [ + "show ip route bgp | head -108", + "show ip route cache", + "show ip route connected", + "show ip route forward", + "show ip route isis | head -108", + "show ip route kernel", + "show ip route ospf | head -108", + "show ip route rip", + "show ip route static", + "show ip route summary", + "show ip route supernets-only", + "show ip route table all", + "show ip route vrf all", + "show ipv6 route bgp | head 108", + "show ipv6 route cache", + "show ipv6 route connected", + "show ipv6 route forward", + "show ipv6 route isis", + "show ipv6 route kernel", + "show ipv6 route ospf", + "show ipv6 route rip", + "show ipv6 route static", + "show ipv6 route summary", + "show ipv6 route table all", + "show ipv6 route vrf all", + ] + for command in cmd_list_route: + execute_command(op(command), command) + + +def show_firewall() -> None: + """Prints firweall information.""" + execute_command('sudo nft list ruleset', 'nft list ruleset') + + +def show_system() -> None: + """Prints system parameters.""" + execute_command(op('show system image version'), 'Show System Image Version') + execute_command(op('show system image storage'), 'Show System Image Storage') + + +def show_date() -> None: + """Print the current date.""" + execute_command('date', 'Current Time') + + +def show_installed_packages() -> None: + """Prints installed packages.""" + execute_command('dpkg --list', 'Installed Packages') + + +def show_loaded_modules() -> None: + """Prints loaded modules /proc/modules""" + execute_command('cat /proc/modules', 'Loaded Modules') + + +def show_cpu_statistics() -> None: + """Prints CPU statistics.""" + execute_command('/usr/bin/true', 'CPU') + execute_command('lscpu', 'Installed CPU\'s') + execute_command('top --iterations 1 --batch-mode --accum-time-toggle', 'Cumulative CPU Time Used by Running Processes') + execute_command('cat /proc/loadavg', 'Load Average') + + +def show_system_interrupts() -> None: + """Prints system interrupts.""" + execute_command('cat /proc/interrupts', 'Hardware Interrupt Counters') + + +def show_soft_irqs() -> None: + """Prints soft IRQ's.""" + execute_command('cat /proc/softirqs', 'Soft IRQ\'s') + + +def show_softnet_statistics() -> None: + """Prints softnet statistics.""" + execute_command('cat /proc/net/softnet_stat', 'cat /proc/net/softnet_stat') + + +def show_running_processes() -> None: + """Prints current running processes""" + execute_command('ps -ef', 'Running Processes') + + +def show_memory_usage() -> None: + """Prints memory usage""" + execute_command('/usr/bin/true', 'Memory') + execute_command('cat /proc/meminfo', 'Installed Memory') + execute_command('free', 'Memory Usage') + + +def list_disks(): + disks = set() + with open('/proc/partitions') as partitions_file: + for line in partitions_file: + fields = line.strip().split() + if len(fields) == 4 and fields[3].isalpha() and fields[3] != 'name': + disks.add(fields[3]) + return disks + + +def show_storage() -> None: + """Prints storage information.""" + execute_command('cat /proc/devices', 'Devices') + execute_command('cat /proc/partitions', 'Partitions') + + for disk in list_disks(): + execute_command(f'fdisk --list /dev/{disk}', f'Partitioning for disk {disk}') + + +def main(): + # Configuration data + show_version() + show_config_file() + show_running_config() + show_package_repository_config() + show_user_startup_scripts() + show_frr_config() + + # Interfaces + show_interfaces() + show_interface_statistics() + show_physical_interface_statistics() + show_bridge() + show_arp() + + # Routing + show_route() + + # Firewall + show_firewall() + + # System + show_system() + show_date() + show_installed_packages() + show_loaded_modules() + + # CPU + show_cpu_statistics() + show_system_interrupts() + show_soft_irqs() + show_softnet_statistics() + + # Memory + show_memory_usage() + + # Storage + show_storage() + + # Processes + show_running_processes() + + # TODO: Get information from clouds + + +if __name__ == "__main__": + main() -- cgit v1.2.3 From 77858da1e5640aef1a542e6d6aa07715858ef750 Mon Sep 17 00:00:00 2001 From: sarthurdev <965089+sarthurdev@users.noreply.github.com> Date: Tue, 18 Apr 2023 11:40:42 +0200 Subject: pki: T3642: Fix show command if no CA certs are present --- src/op_mode/pki.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/op_mode') diff --git a/src/op_mode/pki.py b/src/op_mode/pki.py index 1e78c3a03..b054690b0 100755 --- a/src/op_mode/pki.py +++ b/src/op_mode/pki.py @@ -87,6 +87,9 @@ def get_config_certificate(name=None): def get_certificate_ca(cert, ca_certs): # Find CA certificate for given certificate + if not ca_certs: + return None + for ca_name, ca_dict in ca_certs.items(): if 'certificate' not in ca_dict: continue -- cgit v1.2.3 From e9fe5a877aa4d34cfde6e577e34146c9c025ccfa Mon Sep 17 00:00:00 2001 From: srividya0208 Date: Fri, 14 Apr 2023 07:11:11 -0400 Subject: op-mode: T5159: dhcpv6 incorrect warning message The operational command "show dhcpv6 server leases" shows a warning message e ven if dhcpv6 setting are configured and ipv6 address got assigned to clients. --- src/op_mode/dhcp.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/op_mode') diff --git a/src/op_mode/dhcp.py b/src/op_mode/dhcp.py index 41da14065..fe7f252ba 100755 --- a/src/op_mode/dhcp.py +++ b/src/op_mode/dhcp.py @@ -264,8 +264,10 @@ def show_pool_statistics(raw: bool, family: ArgFamily, pool: typing.Optional[str def show_server_leases(raw: bool, family: ArgFamily, pool: typing.Optional[str], sorted: typing.Optional[str], state: typing.Optional[ArgState]): # if dhcp server is down, inactive leases may still be shown as active, so warn the user. - if not is_systemd_service_running('isc-dhcp-server.service'): - Warning('DHCP server is configured but not started. Data may be stale.') + v = '6' if family == 'inet6' else '' + service_name = 'DHCPv6' if family == 'inet6' else 'DHCP' + if not is_systemd_service_running(f'isc-dhcp-server{v}.service'): + Warning(f'{service_name} server is configured but not started. Data may be stale.') v = 'v6' if family == 'inet6' else '' if pool and pool not in _get_dhcp_pools(family=family): -- cgit v1.2.3