summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--data/templates/sflow/hsflowd.conf.j21
-rw-r--r--op-mode-definitions/include/isis-common.xml.i12
-rw-r--r--op-mode-definitions/sflow.xml.in15
-rwxr-xr-xsmoketest/scripts/cli/test_load_balancing_wan.py4
-rwxr-xr-xsmoketest/scripts/cli/test_system_sflow.py1
-rwxr-xr-xsrc/op_mode/sflow.py102
-rwxr-xr-xsrc/op_mode/show_interfaces.py310
8 files changed, 127 insertions, 319 deletions
diff --git a/Makefile b/Makefile
index a4bfac17d..4f49f0d27 100644
--- a/Makefile
+++ b/Makefile
@@ -65,7 +65,6 @@ op_mode_definitions: $(op_xml_obj)
rm -f $(OP_TMPL_DIR)/generate/node.def
rm -f $(OP_TMPL_DIR)/monitor/node.def
rm -f $(OP_TMPL_DIR)/set/node.def
- rm -f $(OP_TMPL_DIR)/show/interfaces/node.def
rm -f $(OP_TMPL_DIR)/show/node.def
rm -f $(OP_TMPL_DIR)/show/system/node.def
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/include/isis-common.xml.i b/op-mode-definitions/include/isis-common.xml.i
index 95a171515..4a2f1e503 100644
--- a/op-mode-definitions/include/isis-common.xml.i
+++ b/op-mode-definitions/include/isis-common.xml.i
@@ -122,6 +122,12 @@
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
</leafNode>
+ <leafNode name="prefix-sid">
+ <properties>
+ <help>Show Prefix-SID information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </leafNode>
</children>
<command>vtysh -c "show isis route"</command>
</node>
@@ -136,12 +142,6 @@
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
</leafNode>
- <leafNode name="prefix-sids">
- <properties>
- <help>Show prefix segment IDs</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- </leafNode>
</children>
</node>
<leafNode name="spf-delay-ietf">
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- sflow op mode commands -->
+<interfaceDefinition>
+ <node name="show">
+ <children>
+ <node name="sflow">
+ <properties>
+ <help>Show sFlow statistics</help>
+ </properties>
+ <!-- requires sudo, do not remove it -->
+ <command>sudo ${vyos_op_scripts_dir}/sflow.py show</command>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/smoketest/scripts/cli/test_load_balancing_wan.py b/smoketest/scripts/cli/test_load_balancing_wan.py
index 0e1806f66..8df3471f7 100755
--- a/smoketest/scripts/cli/test_load_balancing_wan.py
+++ b/smoketest/scripts/cli/test_load_balancing_wan.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-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
@@ -177,7 +177,7 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase):
nat_vyos_pre_snat_hook = """table ip nat {
chain VYOS_PRE_SNAT_HOOK {
type nat hook postrouting priority srcnat - 1; policy accept;
- return
+ counter jump WANLOADBALANCE
}
}"""
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 <http://www.gnu.org/licenses/>.
+
+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)
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 <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/>.
-
-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
- )