summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Poessinger <christian@poessinger.com>2021-06-27 11:12:05 +0200
committerChristian Poessinger <christian@poessinger.com>2021-06-27 11:14:12 +0200
commit09efa0550dd169e30a851513781b611dd84e9c79 (patch)
tree8420086b1b50ed5f60822222d3b83edf3c38cf55
parentd34cd9572dd6285b92d3e0f8f80dc109a674c205 (diff)
downloadvyos-1x-09efa0550dd169e30a851513781b611dd84e9c79.tar.gz
vyos-1x-09efa0550dd169e30a851513781b611dd84e9c79.zip
op-mode: bond: T2546: implement "show interface bond * slaves" command
Add implementation with XML and Python.
-rw-r--r--op-mode-definitions/show-interfaces-bonding.xml.in12
-rw-r--r--python/vyos/ifconfig/bond.py16
-rwxr-xr-xsrc/op_mode/show-bond.py92
3 files changed, 120 insertions, 0 deletions
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 @@
</properties>
<command>if [ -f "/proc/net/bonding/$4" ]; then cat "/proc/net/bonding/$4"; else echo "Interface $4 does not exist!"; fi</command>
</leafNode>
+ <leafNode name="slaves">
+ <properties>
+ <help>Show specified bonding interface information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/show-bond.py --interface "$4"</command>
+ </leafNode>
<tagNode name="vif">
<properties>
<help>Show specified virtual network interface (vif) information</help>
@@ -62,6 +68,12 @@
</properties>
<command>${vyos_op_scripts_dir}/show_interfaces.py --intf-type=bonding --action=show</command>
</leafNode>
+ <leafNode name="slaves">
+ <properties>
+ <help>Show specified bonding interface information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/show-bond.py --slaves</command>
+ </leafNode>
</children>
</node>
</children>
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 <http://www.gnu.org/licenses/>.
+
+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)