From e0aa8aa20b835f17a74e0cf2a0dc2ca6e7823a3c Mon Sep 17 00:00:00 2001 From: Thomas Mangin Date: Tue, 14 Apr 2020 09:46:46 +0100 Subject: ifconfig: T2223: group all operational commands All operational command are moved within an Operational class and an inherited on for wireguard. --- python/vyos/ifconfig/operational.py | 179 ++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 python/vyos/ifconfig/operational.py (limited to 'python/vyos/ifconfig/operational.py') diff --git a/python/vyos/ifconfig/operational.py b/python/vyos/ifconfig/operational.py new file mode 100644 index 000000000..5a292f90b --- /dev/null +++ b/python/vyos/ifconfig/operational.py @@ -0,0 +1,179 @@ +# Copyright 2019 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 +from time import time +from datetime import datetime +from functools import reduce + +from tabulate import tabulate + +from vyos.ifconfig import Control + + +class Operational(Control): + """ + A class able to load Interface statistics + """ + + cache_magic = 'XYZZYX' + + _stat_names = { + 'rx': ['bytes', 'packets', 'errors', 'dropped', 'overrun', 'mcast'], + 'tx': ['bytes', 'packets', 'errors', 'dropped', 'carrier', 'collisions'], + } + + _stats_dir = { + 'rx': ['rx_bytes', 'rx_packets', 'rx_errors', 'rx_dropped', 'rx_over_errors', 'multicast'], + 'tx': ['tx_bytes', 'tx_packets', 'tx_errors', 'tx_dropped', 'tx_carrier_errors', 'collisions'], + } + + # a list made of the content of _stats_dir['rx'] + _stats_dir['tx'] + _stats_all = reduce(lambda x, y: x+y, _stats_dir.values()) + + # this is not an interface but will be able to be controlled like one + _sysfs_get = { + 'oper_state':{ + 'location': '/sys/class/net/{ifname}/operstate', + }, + } + + + @classmethod + def cachefile (cls, ifname): + # the file where we are saving the counters + return f'/var/run/vyatta/{ifname}.stats' + + + def __init__(self, ifname): + """ + Operational provide access to the counters of an interface + It behave like an interface when it comes to access sysfs + + interface is an instance of the interface for which we want + to look at (a subclass of Interface, such as EthernetIf) + """ + + # add a self.config to minic Interface behaviour and make + # coding similar. Perhaps part of class Interface could be + # moved into a shared base class. + self.config = { + 'ifname': ifname, + 'create': False, + 'debug': False, + } + super().__init__(**self.config) + self.ifname = ifname + + # adds all the counters of an interface + for stat in self._stats_all: + self._sysfs_get[stat] = { + 'location': '/sys/class/net/{ifname}/statistics/'+stat, + } + + def get_state(self): + """ + Get interface operational state + + Example: + >>> from vyos.ifconfig import Interface + >>> Interface('eth0').operational.get_sate() + 'up' + """ + # https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-net + # "unknown", "notpresent", "down", "lowerlayerdown", "testing", "dormant", "up" + return self.get_interface('oper_state') + + @classmethod + def strtime (cls, epoc): + """ + represent an epoc/unix date in the format used by operation commands + """ + return datetime.fromtimestamp(epoc).strftime("%a %b %d %R:%S %Z %Y") + + def save_counters(self, stats): + """ + record the provided stats to a file keeping vyatta compatibility + """ + + with open(self.cachefile(self.ifname), 'w') as f: + f.write(self.cache_magic) + f.write('\n') + f.write(str(int(time()))) + f.write('\n') + for k,v in stats.items(): + if v: + f.write(f'{k},{v}\n') + + def load_counters(self): + """ + load the stats from a file keeping vyatta compatibility + return a dict() with the value for each interface counter for the cache + """ + ifname = self.config['ifname'] + + stats = {} + no_stats = {} + for name in self._stats_all: + stats[name] = 0 + no_stats[name] = 0 + + try: + with open(self.cachefile(self.ifname),'r') as f: + magic = f.readline().strip() + if magic != self.cache_magic: + print(f'bad magic {ifname}') + return no_stats + stats['timestamp'] = f.readline().strip() + for line in f: + k, v = line.split(',') + stats[k] = int(v) + return stats + except IOError: + return stats + + def clear_counters(self, counters=None): + clear = self._stats_all if counters is None else [] + stats = self.load_counters() + for counter, value in stats.items(): + stats[counter] = 0 if counter in clear else value + self.save_counters(stats) + + def reset_counters(self): + os.remove(self.cachefile(self.ifname)) + + def get_stats(self): + """ return a dict() with the value for each interface counter """ + stats = {} + for counter in self._stats_all: + stats[counter] = int(self.get_interface(counter)) + return stats + + def formated_stats(self, indent=4): + tabs = [] + stats = self.get_stats() + for rtx in self._stats_dir: + tabs.append([f'{rtx.upper()}:', ] + [_ for _ in self._stat_names[rtx]]) + tabs.append(['', ] + [stats[_] for _ in self._stats_dir[rtx]]) + + s = tabulate( + tabs, + stralign="right", + numalign="right", + tablefmt="plain" + ) + + p = ' '*indent + return f'{p}' + s.replace('\n', f'\n{p}') -- cgit v1.2.3 From 7d04bfbcc74e062b80b337753e7018a6af81e70c Mon Sep 17 00:00:00 2001 From: Thomas Mangin Date: Tue, 14 Apr 2020 17:10:20 +0100 Subject: op_mode: T2223: two cosmetic change and bug fix --- python/vyos/ifconfig/operational.py | 2 +- src/op_mode/show_interfaces.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'python/vyos/ifconfig/operational.py') diff --git a/python/vyos/ifconfig/operational.py b/python/vyos/ifconfig/operational.py index 5a292f90b..d585c1873 100644 --- a/python/vyos/ifconfig/operational.py +++ b/python/vyos/ifconfig/operational.py @@ -142,7 +142,7 @@ class Operational(Control): stats[k] = int(v) return stats except IOError: - return stats + return no_stats def clear_counters(self, counters=None): clear = self._stats_all if counters is None else [] diff --git a/src/op_mode/show_interfaces.py b/src/op_mode/show_interfaces.py index c5d4c8ac6..8b6690b7d 100755 --- a/src/op_mode/show_interfaces.py +++ b/src/op_mode/show_interfaces.py @@ -20,7 +20,6 @@ import re import sys import datetime import argparse -from subprocess import Popen, PIPE, STDOUT import netifaces from vyos.ifconfig import Section @@ -87,7 +86,7 @@ def split_text(text, used=0): text: the string to split used: number of characted already used in the screen """ - returned = Popen('stty size', stdout=PIPE, stderr=STDOUT, shell=True).communicate()[0].strip().split() + returned = cmd('stty size') if len(returned) == 2: rows, columns = [int(_) for _ in returned] else: @@ -240,7 +239,9 @@ def run_clear_intf(intf, iftypes, vif, vrrp): @register('reset') def run_reset_intf(intf, iftypes, vif, vrrp): - os.remove() + for interface in filtered_interfaces(ifnames, iftypes, vif, vrrp): + interface = Interface(ifname, create=False, debug=False) + interface.operational.reset_counters() if __name__ == '__main__': -- cgit v1.2.3