summaryrefslogtreecommitdiff
path: root/python/vyos/ifconfig/operational.py
diff options
context:
space:
mode:
authorThomas Mangin <thomas.mangin@exa.net.uk>2020-04-14 09:46:46 +0100
committerThomas Mangin <thomas.mangin@exa.net.uk>2020-04-14 12:04:43 +0100
commite0aa8aa20b835f17a74e0cf2a0dc2ca6e7823a3c (patch)
tree8357fd78883ab114b4cdc019e7eeeb6920368d00 /python/vyos/ifconfig/operational.py
parentfdfd050431f786f9c9b7bfdcb51b5e8aca3f79f5 (diff)
downloadvyos-1x-e0aa8aa20b835f17a74e0cf2a0dc2ca6e7823a3c.tar.gz
vyos-1x-e0aa8aa20b835f17a74e0cf2a0dc2ca6e7823a3c.zip
ifconfig: T2223: group all operational commands
All operational command are moved within an Operational class and an inherited on for wireguard.
Diffstat (limited to 'python/vyos/ifconfig/operational.py')
-rw-r--r--python/vyos/ifconfig/operational.py179
1 files changed, 179 insertions, 0 deletions
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 <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
+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}')