From 09efa0550dd169e30a851513781b611dd84e9c79 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 27 Jun 2021 11:12:05 +0200 Subject: op-mode: bond: T2546: implement "show interface bond * slaves" command Add implementation with XML and Python. --- op-mode-definitions/show-interfaces-bonding.xml.in | 12 +++ python/vyos/ifconfig/bond.py | 16 ++++ src/op_mode/show-bond.py | 92 ++++++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100755 src/op_mode/show-bond.py diff --git a/op-mode-definitions/show-interfaces-bonding.xml.in b/op-mode-definitions/show-interfaces-bonding.xml.in index f6d9b3508..08ce78296 100644 --- a/op-mode-definitions/show-interfaces-bonding.xml.in +++ b/op-mode-definitions/show-interfaces-bonding.xml.in @@ -25,6 +25,12 @@ if [ -f "/proc/net/bonding/$4" ]; then cat "/proc/net/bonding/$4"; else echo "Interface $4 does not exist!"; fi + + + Show specified bonding interface information + + ${vyos_op_scripts_dir}/show-bond.py --interface "$4" + Show specified virtual network interface (vif) information @@ -62,6 +68,12 @@ ${vyos_op_scripts_dir}/show_interfaces.py --intf-type=bonding --action=show + + + Show specified bonding interface information + + ${vyos_op_scripts_dir}/show-bond.py --slaves + diff --git a/python/vyos/ifconfig/bond.py b/python/vyos/ifconfig/bond.py index 233d53688..2b9afe109 100644 --- a/python/vyos/ifconfig/bond.py +++ b/python/vyos/ifconfig/bond.py @@ -86,6 +86,9 @@ class BondIf(Interface): _sysfs_get = {**Interface._sysfs_get, **{ 'bond_arp_ip_target': { 'location': '/sys/class/net/{ifname}/bonding/arp_ip_target', + }, + 'bond_mode': { + 'location': '/sys/class/net/{ifname}/bonding/mode', } }} @@ -317,6 +320,19 @@ class BondIf(Interface): return enslaved_ifs + def get_mode(self): + """ + Return bond operation mode. + + Example: + >>> from vyos.ifconfig import BondIf + >>> BondIf('bond0').get_mode() + '802.3ad' + """ + mode = self.get_interface('bond_mode') + # mode is now "802.3ad 4", we are only interested in "802.3ad" + return mode.split()[0] + def set_primary(self, interface): """ A string (eth0, eth2, etc) specifying which slave is the primary diff --git a/src/op_mode/show-bond.py b/src/op_mode/show-bond.py new file mode 100755 index 000000000..edf7847fc --- /dev/null +++ b/src/op_mode/show-bond.py @@ -0,0 +1,92 @@ +#!/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 . + +import jinja2 + +from argparse import ArgumentParser +from vyos.ifconfig import Section +from vyos.ifconfig import BondIf +from vyos.util import read_file + +from sys import exit + +parser = ArgumentParser() +parser.add_argument("--slaves", action="store_true", help="Show LLDP neighbors on all interfaces") +parser.add_argument("--interface", action="store", help="Show LLDP neighbors on specific interface") + +args = parser.parse_args() + +all_bonds = Section.interfaces('bonding') +# we are not interested in any bond vlan interface +all_bonds = [x for x in all_bonds if '.' not in x] + +TMPL_BRIEF = """Interface Mode State Link Slaves +{% for interface in data %} +{{ "%-12s" | format(interface.ifname) }} {{ "%-22s" | format(interface.mode) }} {{ "%-8s" | format(interface.admin_state) }} {{ "%-6s" | format(interface.oper_state) }} {{ interface.members | join(' ') }} +{% endfor %} +""" + +TMPL_INDIVIDUAL_BOND = """Interface RX: bytes packets TX: bytes packets +{{ "%-16s" | format(data.ifname) }} {{ "%-10s" | format(data.rx_bytes) }} {{ "%-11s" | format(data.rx_packets) }} {{ "%-10s" | format(data.tx_bytes) }} {{ data.tx_packets }} +{% for member in data.members if data.members is defined %} + {{ "%-12s" | format(member.ifname) }} {{ "%-10s" | format(member.rx_bytes) }} {{ "%-11s" | format(member.rx_packets) }} {{ "%-10s" | format(member.tx_bytes) }} {{ member.tx_packets }} +{% endfor %} +""" + +if args.slaves and args.interface: + exit('Can not use both --slaves and --interfaces option at the same time') + parser.print_help() + +elif args.slaves: + data = [] + template = TMPL_BRIEF + for bond in all_bonds: + tmp = BondIf(bond) + cfg_dict = {} + cfg_dict['ifname'] = bond + cfg_dict['mode'] = tmp.get_mode() + cfg_dict['admin_state'] = tmp.get_admin_state() + cfg_dict['oper_state'] = tmp.operational.get_state() + cfg_dict['members'] = tmp.get_slaves() + data.append(cfg_dict) + +elif args.interface: + template = TMPL_INDIVIDUAL_BOND + data = {} + data['ifname'] = args.interface + data['rx_bytes'] = read_file(f'/sys/class/net/{args.interface}/statistics/rx_bytes') + data['rx_packets'] = read_file(f'/sys/class/net/{args.interface}/statistics/rx_packets') + data['tx_bytes'] = read_file(f'/sys/class/net/{args.interface}/statistics/tx_bytes') + data['tx_packets'] = read_file(f'/sys/class/net/{args.interface}/statistics/tx_packets') + + # each bond member interface has its own statistics + data['members'] = [] + for member in BondIf(args.interface).get_slaves(): + tmp = {} + tmp['ifname'] = member + tmp['rx_bytes'] = read_file(f'/sys/class/net/{member}/statistics/rx_bytes') + tmp['rx_packets'] = read_file(f'/sys/class/net/{member}/statistics/rx_packets') + tmp['tx_bytes'] = read_file(f'/sys/class/net/{member}/statistics/tx_bytes') + tmp['tx_packets'] = read_file(f'/sys/class/net/{member}/statistics/tx_packets') + data['members'].append(tmp) + +else: + parser.print_help() + exit(1) + +tmpl = jinja2.Template(template, trim_blocks=True) +config_text = tmpl.render(data=data) +print(config_text) -- cgit v1.2.3