summaryrefslogtreecommitdiff
path: root/python/vyos/ifconfig/interface.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/vyos/ifconfig/interface.py')
-rw-r--r--python/vyos/ifconfig/interface.py738
1 files changed, 738 insertions, 0 deletions
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
new file mode 100644
index 000000000..22c71a464
--- /dev/null
+++ b/python/vyos/ifconfig/interface.py
@@ -0,0 +1,738 @@
+# 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
+import re
+import json
+import glob
+import time
+from time import sleep
+from os.path import isfile
+from copy import deepcopy
+from datetime import timedelta
+
+from hurry.filesize import size, alternative
+from ipaddress import IPv4Network, IPv6Address, IPv6Network
+from netifaces import ifaddresses, AF_INET, AF_INET6
+from tabulate import tabulate
+
+from vyos.util import mac2eui64
+from vyos import ConfigError
+from vyos.ifconfig.dhcp import DHCP
+from vyos.validate import is_ipv4
+from vyos.validate import is_ipv6
+from vyos.validate import is_intf_addr_assigned
+from vyos.validate import assert_boolean
+from vyos.validate import assert_list
+from vyos.validate import assert_mac
+from vyos.validate import assert_mtu
+from vyos.validate import assert_positive
+from vyos.validate import assert_range
+
+
+class Interface(DHCP):
+ options = []
+ required = []
+ default = {
+ 'type': '',
+ 'debug': True,
+ 'create': True,
+ }
+ definition = {
+ 'section': '',
+ 'prefixes': [],
+ 'vlan': False,
+ 'bondable': False,
+ 'broadcast': False,
+ 'bridgeable': False,
+ }
+
+ _command_get = {
+ 'admin_state': {
+ 'shellcmd': 'ip -json link show dev {ifname}',
+ 'format': lambda j: 'up' if 'UP' in json.loads(j)[0]['flags'] else 'down',
+ }
+ }
+
+ _command_set = {
+ 'admin_state': {
+ 'validate': lambda v: assert_list(v, ['up', 'down']),
+ 'shellcmd': 'ip link set dev {ifname} {value}',
+ },
+ 'mac': {
+ 'validate': assert_mac,
+ 'shellcmd': 'ip link set dev {ifname} address {value}',
+ },
+ 'vrf': {
+ 'convert': lambda v: f'master {v}' if v else 'nomaster',
+ 'shellcmd': 'ip link set dev {ifname} {value}',
+ },
+ }
+
+ _sysfs_get = {
+ 'alias': {
+ 'location': '/sys/class/net/{ifname}/ifalias',
+ },
+ 'mac': {
+ 'location': '/sys/class/net/{ifname}/address',
+ },
+ 'mtu': {
+ 'location': '/sys/class/net/{ifname}/mtu',
+ },
+ 'oper_state':{
+ 'location': '/sys/class/net/{ifname}/operstate',
+ },
+ }
+
+ _sysfs_set = {
+ 'alias': {
+ 'convert': lambda name: name if name else '\0',
+ 'location': '/sys/class/net/{ifname}/ifalias',
+ },
+ 'mtu': {
+ 'validate': assert_mtu,
+ 'location': '/sys/class/net/{ifname}/mtu',
+ },
+ 'arp_cache_tmo': {
+ 'convert': lambda tmo: (int(tmo) * 1000),
+ 'location': '/proc/sys/net/ipv4/neigh/{ifname}/base_reachable_time_ms',
+ },
+ 'arp_filter': {
+ 'validate': assert_boolean,
+ 'location': '/proc/sys/net/ipv4/conf/{ifname}/arp_filter',
+ },
+ 'arp_accept': {
+ 'validate': lambda arp: assert_range(arp,0,2),
+ 'location': '/proc/sys/net/ipv4/conf/{ifname}/arp_accept',
+ },
+ 'arp_announce': {
+ 'validate': assert_boolean,
+ 'location': '/proc/sys/net/ipv4/conf/{ifname}/arp_announce',
+ },
+ 'arp_ignore': {
+ 'validate': assert_boolean,
+ 'location': '/proc/sys/net/ipv4/conf/{ifname}/arp_ignore',
+ },
+ 'ipv6_autoconf': {
+ 'validate': lambda fwd: assert_range(fwd,0,2),
+ 'location': '/proc/sys/net/ipv6/conf/{ifname}/autoconf',
+ },
+ 'ipv6_forwarding': {
+ 'validate': lambda fwd: assert_range(fwd,0,2),
+ 'location': '/proc/sys/net/ipv6/conf/{ifname}/forwarding',
+ },
+ 'ipv6_dad_transmits': {
+ 'validate': assert_positive,
+ 'location': '/proc/sys/net/ipv6/conf/{ifname}/dad_transmits',
+ },
+ 'proxy_arp': {
+ 'validate': assert_boolean,
+ 'location': '/proc/sys/net/ipv4/conf/{ifname}/proxy_arp',
+ },
+ 'proxy_arp_pvlan': {
+ 'validate': assert_boolean,
+ 'location': '/proc/sys/net/ipv4/conf/{ifname}/proxy_arp_pvlan',
+ },
+ # link_detect vs link_filter name weirdness
+ 'link_detect': {
+ 'validate': lambda link: assert_range(link,0,3),
+ 'location': '/proc/sys/net/ipv4/conf/{ifname}/link_filter',
+ },
+ }
+
+ def __init__(self, ifname, **kargs):
+ """
+ This is the base interface class which supports basic IP/MAC address
+ operations as well as DHCP(v6). Other interface which represent e.g.
+ and ethernet bridge are implemented as derived classes adding all
+ additional functionality.
+
+ For creation you will need to provide the interface type, otherwise
+ the existing interface is used
+
+ DEBUG:
+ This class has embedded debugging (print) which can be enabled by
+ creating the following file:
+ vyos@vyos# touch /tmp/vyos.ifconfig.debug
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> i = Interface('eth0')
+ """
+
+ self.config = deepcopy(self.default)
+ for k in self.options:
+ if k in kargs:
+ self.config[k] = kargs[k]
+
+ # make sure the ifname is the first argument and not from the dict
+ self.config['ifname'] = ifname
+
+ # we must have updated config before initialising the Interface
+ super().__init__(ifname, **kargs)
+
+ if not os.path.exists('/sys/class/net/{}'.format(self.config['ifname'])):
+ # Any instance of Interface, such as Interface('eth0')
+ # can be used safely to access the generic function in this class
+ # as 'type' is unset, the class can not be created
+ if not self.config['type']:
+ raise Exception('interface "{}" not found'.format(self.config['ifname']))
+
+ # Should an Instance of a child class (EthernetIf, DummyIf, ..)
+ # be required, then create should be set to False to not accidentally create it.
+ # In case a subclass does not define it, we use get to set the default to True
+ if self.config.get('create',True):
+ for k in self.required:
+ if k not in kargs:
+ name = self.default['type']
+ raise ConfigError(f'missing required option {k} for {name} {ifname} creation')
+
+ self._create()
+ # If we can not connect to the interface then let the caller know
+ # as the class could not be correctly initialised
+ else:
+ raise Exception('interface "{}" not found'.format(self.config['ifname']))
+
+ # list of assigned IP addresses
+ self._addr = []
+
+ def _create(self):
+ cmd = 'ip link add dev {ifname} type {type}'.format(**self.config)
+ self._cmd(cmd)
+
+ def remove(self):
+ """
+ Remove interface from operating system. Removing the interface
+ deconfigures all assigned IP addresses and clear possible DHCP(v6)
+ client processes.
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> i = Interface('eth0')
+ >>> i.remove()
+ """
+ # stop DHCP(v6) if running
+ self._del_dhcp()
+ self._del_dhcpv6()
+
+ # remove all assigned IP addresses from interface - this is a bit redundant
+ # as the kernel will remove all addresses on interface deletion, but we
+ # can not delete ALL interfaces, see below
+ for addr in self.get_addr():
+ self.del_addr(addr)
+
+ # ---------------------------------------------------------------------
+ # A code refactoring is required as this type check is present as
+ # Interface implement behaviour for one of it's sub-class.
+
+ # It is required as the current pattern for vlan is:
+ # Interface('name').remove() to delete an interface
+ # The code should be modified to have a class method called connect and
+ # have Interface.connect('name').remove()
+
+ # each subclass should register within Interface the pattern for that
+ # interface ie: (ethX, etc.) and use this to create an instance of
+ # the right class (EthernetIf, ...)
+
+ # Ethernet interfaces can not be removed
+
+ # Commented out as nowhere in the code do we call Interface()
+ # This would also cause an import loop
+ # if self.__class__ == EthernetIf:
+ # return
+
+ # ---------------------------------------------------------------------
+
+ self._delete()
+
+ def _delete(self):
+ # NOTE (Improvement):
+ # after interface removal no other commands should be allowed
+ # to be called and instead should raise an Exception:
+ cmd = 'ip link del dev {}'.format(self.config['ifname'])
+ return self._cmd(cmd)
+
+ def get_mtu(self):
+ """
+ Get/set interface mtu in bytes.
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').get_mtu()
+ '1500'
+ """
+ return self.get_interface('mtu')
+
+ def set_mtu(self, mtu):
+ """
+ Get/set interface mtu in bytes.
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').set_mtu(1400)
+ >>> Interface('eth0').get_mtu()
+ '1400'
+ """
+ return self.set_interface('mtu', mtu)
+
+ def get_mac(self):
+ """
+ Get current interface MAC (Media Access Contrl) address used.
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').get_mac()
+ '00:50:ab:cd:ef:00'
+ """
+ return self.get_interface('mac')
+
+ def set_mac(self, mac):
+ """
+ Set interface MAC (Media Access Contrl) address to given value.
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').set_mac('00:50:ab:cd:ef:01')
+ """
+
+ # If MAC is unchanged, bail out early
+ if mac == self.get_mac():
+ return None
+
+ # MAC address can only be changed if interface is in 'down' state
+ prev_state = self.get_admin_state()
+ if prev_state == 'up':
+ self.set_admin_state('down')
+
+ self.set_interface('mac', mac)
+
+ def set_vrf(self, vrf=''):
+ """
+ Add/Remove interface from given VRF instance.
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').set_vrf('foo')
+ >>> Interface('eth0').set_vrf()
+ """
+ self.set_interface('vrf', vrf)
+
+ def set_arp_cache_tmo(self, tmo):
+ """
+ Set ARP cache timeout value in seconds. Internal Kernel representation
+ is in milliseconds.
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').set_arp_cache_tmo(40)
+ """
+ return self.set_interface('arp_cache_tmo', tmo)
+
+ def set_arp_filter(self, arp_filter):
+ """
+ Filter ARP requests
+
+ 1 - Allows you to have multiple network interfaces on the same
+ subnet, and have the ARPs for each interface be answered
+ based on whether or not the kernel would route a packet from
+ the ARP'd IP out that interface (therefore you must use source
+ based routing for this to work). In other words it allows control
+ of which cards (usually 1) will respond to an arp request.
+
+ 0 - (default) The kernel can respond to arp requests with addresses
+ from other interfaces. This may seem wrong but it usually makes
+ sense, because it increases the chance of successful communication.
+ IP addresses are owned by the complete host on Linux, not by
+ particular interfaces. Only for more complex setups like load-
+ balancing, does this behaviour cause problems.
+ """
+ return self.set_interface('arp_filter', arp_filter)
+
+ def set_arp_accept(self, arp_accept):
+ """
+ Define behavior for gratuitous ARP frames who's IP is not
+ already present in the ARP table:
+ 0 - don't create new entries in the ARP table
+ 1 - create new entries in the ARP table
+
+ Both replies and requests type gratuitous arp will trigger the
+ ARP table to be updated, if this setting is on.
+
+ If the ARP table already contains the IP address of the
+ gratuitous arp frame, the arp table will be updated regardless
+ if this setting is on or off.
+ """
+ return self.set_interface('arp_accept', arp_accept)
+
+ def set_arp_announce(self, arp_announce):
+ """
+ Define different restriction levels for announcing the local
+ source IP address from IP packets in ARP requests sent on
+ interface:
+ 0 - (default) Use any local address, configured on any interface
+ 1 - Try to avoid local addresses that are not in the target's
+ subnet for this interface. This mode is useful when target
+ hosts reachable via this interface require the source IP
+ address in ARP requests to be part of their logical network
+ configured on the receiving interface. When we generate the
+ request we will check all our subnets that include the
+ target IP and will preserve the source address if it is from
+ such subnet.
+
+ Increasing the restriction level gives more chance for
+ receiving answer from the resolved target while decreasing
+ the level announces more valid sender's information.
+ """
+ return self.set_interface('arp_announce', arp_announce)
+
+ def set_arp_ignore(self, arp_ignore):
+ """
+ Define different modes for sending replies in response to received ARP
+ requests that resolve local target IP addresses:
+
+ 0 - (default): reply for any local target IP address, configured
+ on any interface
+ 1 - reply only if the target IP address is local address
+ configured on the incoming interface
+ """
+ return self.set_interface('arp_ignore', arp_ignore)
+
+ def set_ipv6_autoconf(self, autoconf):
+ """
+ Autoconfigure addresses using Prefix Information in Router
+ Advertisements.
+ """
+ return self.set_interface('ipv6_autoconf', autoconf)
+
+ def set_ipv6_eui64_address(self, prefix):
+ """
+ Extended Unique Identifier (EUI), as per RFC2373, allows a host to
+ assign iteslf a unique IPv6 address based on a given IPv6 prefix.
+
+ If prefix is passed address is assigned, if prefix is '' address is
+ removed from interface.
+ """
+ # if prefix is an empty string convert it to None so mac2eui64 works
+ # as expected
+ if not prefix:
+ prefix = None
+
+ eui64 = mac2eui64(self.get_mac(), prefix)
+
+ if not prefix:
+ # if prefix is empty - thus removed - we need to walk through all
+ # interface IPv6 addresses and find the one with the calculated
+ # EUI-64 identifier. The address is then removed
+ for addr in self.get_addr():
+ addr_wo_prefix = addr.split('/')[0]
+ if is_ipv6(addr_wo_prefix):
+ if eui64 in IPv6Address(addr_wo_prefix).exploded:
+ self.del_addr(addr)
+
+ return None
+
+ # calculate and add EUI-64 IPv6 address
+ if IPv6Network(prefix):
+ # we also need to take the subnet length into account
+ prefix = prefix.split('/')[1]
+ eui64 = f'{eui64}/{prefix}'
+ self.add_addr(eui64 )
+
+ def set_ipv6_forwarding(self, forwarding):
+ """
+ Configure IPv6 interface-specific Host/Router behaviour.
+
+ False:
+
+ By default, Host behaviour is assumed. This means:
+
+ 1. IsRouter flag is not set in Neighbour Advertisements.
+ 2. If accept_ra is TRUE (default), transmit Router
+ Solicitations.
+ 3. If accept_ra is TRUE (default), accept Router
+ Advertisements (and do autoconfiguration).
+ 4. If accept_redirects is TRUE (default), accept Redirects.
+
+ True:
+
+ If local forwarding is enabled, Router behaviour is assumed.
+ This means exactly the reverse from the above:
+
+ 1. IsRouter flag is set in Neighbour Advertisements.
+ 2. Router Solicitations are not sent unless accept_ra is 2.
+ 3. Router Advertisements are ignored unless accept_ra is 2.
+ 4. Redirects are ignored.
+ """
+ return self.set_interface('ipv6_forwarding', forwarding)
+
+ def set_ipv6_dad_messages(self, dad):
+ """
+ The amount of Duplicate Address Detection probes to send.
+ Default: 1
+ """
+ return self.set_interface('ipv6_dad_transmits', dad)
+
+ def set_link_detect(self, link_filter):
+ """
+ Configure kernel response in packets received on interfaces that are 'down'
+
+ 0 - Allow packets to be received for the address on this interface
+ even if interface is disabled or no carrier.
+
+ 1 - Ignore packets received if interface associated with the incoming
+ address is down.
+
+ 2 - Ignore packets received if interface associated with the incoming
+ address is down or has no carrier.
+
+ Default value is 0. Note that some distributions enable it in startup
+ scripts.
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').set_link_detect(1)
+ """
+ return self.set_interface('link_detect', link_filter)
+
+ def get_alias(self):
+ """
+ Get interface alias name used by e.g. SNMP
+
+ Example:
+ >>> Interface('eth0').get_alias()
+ 'interface description as set by user'
+ """
+ return self.get_interface('alias')
+
+ def set_alias(self, ifalias=''):
+ """
+ Set interface alias name used by e.g. SNMP
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').set_alias('VyOS upstream interface')
+
+ to clear alias e.g. delete it use:
+
+ >>> Interface('eth0').set_ifalias('')
+ """
+ self.set_interface('alias', ifalias)
+
+ def get_admin_state(self):
+ """
+ Get interface administrative state. Function will return 'up' or 'down'
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').get_admin_state()
+ 'up'
+ """
+ return self.get_interface('admin_state')
+
+ def set_admin_state(self, state):
+ """
+ Set interface administrative state to be 'up' or 'down'
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').set_admin_state('down')
+ >>> Interface('eth0').get_admin_state()
+ 'down'
+ """
+ return self.set_interface('admin_state', state)
+
+ def get_oper_state(self):
+ """
+ Get interface operational state
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').get_oper_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')
+
+ def set_proxy_arp(self, enable):
+ """
+ Set per interface proxy ARP configuration
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').set_proxy_arp(1)
+ """
+ self.set_interface('proxy_arp', enable)
+
+ def set_proxy_arp_pvlan(self, enable):
+ """
+ Private VLAN proxy arp.
+ Basically allow proxy arp replies back to the same interface
+ (from which the ARP request/solicitation was received).
+
+ This is done to support (ethernet) switch features, like RFC
+ 3069, where the individual ports are NOT allowed to
+ communicate with each other, but they are allowed to talk to
+ the upstream router. As described in RFC 3069, it is possible
+ to allow these hosts to communicate through the upstream
+ router by proxy_arp'ing. Don't need to be used together with
+ proxy_arp.
+
+ This technology is known by different names:
+ In RFC 3069 it is called VLAN Aggregation.
+ Cisco and Allied Telesyn call it Private VLAN.
+ Hewlett-Packard call it Source-Port filtering or port-isolation.
+ Ericsson call it MAC-Forced Forwarding (RFC Draft).
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').set_proxy_arp_pvlan(1)
+ """
+ self.set_interface('proxy_arp_pvlan', enable)
+
+ def get_addr(self):
+ """
+ Retrieve assigned IPv4 and IPv6 addresses from given interface.
+ This is done using the netifaces and ipaddress python modules.
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').get_addrs()
+ ['172.16.33.30/24', 'fe80::20c:29ff:fe11:a174/64']
+ """
+
+ ipv4 = []
+ ipv6 = []
+
+ if AF_INET in ifaddresses(self.config['ifname']).keys():
+ for v4_addr in ifaddresses(self.config['ifname'])[AF_INET]:
+ # we need to manually assemble a list of IPv4 address/prefix
+ prefix = '/' + \
+ str(IPv4Network('0.0.0.0/' + v4_addr['netmask']).prefixlen)
+ ipv4.append(v4_addr['addr'] + prefix)
+
+ if AF_INET6 in ifaddresses(self.config['ifname']).keys():
+ for v6_addr in ifaddresses(self.config['ifname'])[AF_INET6]:
+ # Note that currently expanded netmasks are not supported. That means
+ # 2001:db00::0/24 is a valid argument while 2001:db00::0/ffff:ff00:: not.
+ # see https://docs.python.org/3/library/ipaddress.html
+ bits = bin(
+ int(v6_addr['netmask'].replace(':', ''), 16)).count('1')
+ prefix = '/' + str(bits)
+
+ # we alsoneed to remove the interface suffix on link local
+ # addresses
+ v6_addr['addr'] = v6_addr['addr'].split('%')[0]
+ ipv6.append(v6_addr['addr'] + prefix)
+
+ return ipv4 + ipv6
+
+ def add_addr(self, addr):
+ """
+ Add IP(v6) address to interface. Address is only added if it is not
+ already assigned to that interface.
+
+ addr: can be an IPv4 address, IPv6 address, dhcp or dhcpv6!
+ IPv4: add IPv4 address to interface
+ IPv6: add IPv6 address to interface
+ dhcp: start dhclient (IPv4) on interface
+ dhcpv6: start dhclient (IPv6) on interface
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> j = Interface('eth0')
+ >>> j.add_addr('192.0.2.1/24')
+ >>> j.add_addr('2001:db8::ffff/64')
+ >>> j.get_addr()
+ ['192.0.2.1/24', '2001:db8::ffff/64']
+ """
+
+ # cache new IP address which is assigned to interface
+ self._addr.append(addr)
+
+ # we can not have both DHCP and static IPv4 addresses assigned to an interface
+ if 'dhcp' in self._addr:
+ for addr in self._addr:
+ # do not change below 'if' ordering esle you will get an exception as:
+ # ValueError: 'dhcp' does not appear to be an IPv4 or IPv6 address
+ if addr != 'dhcp' and is_ipv4(addr):
+ raise ConfigError("Can't configure both static IPv4 and DHCP address on the same interface")
+
+ if addr == 'dhcp':
+ self._set_dhcp()
+ elif addr == 'dhcpv6':
+ self._set_dhcpv6()
+ else:
+ if not is_intf_addr_assigned(self.config['ifname'], addr):
+ cmd = 'ip addr add "{}" dev "{}"'.format(addr, self.config['ifname'])
+ return self._cmd(cmd)
+
+ def del_addr(self, addr):
+ """
+ Delete IP(v6) address to interface. Address is only added if it is
+ assigned to that interface.
+
+ addr: can be an IPv4 address, IPv6 address, dhcp or dhcpv6!
+ IPv4: delete IPv4 address from interface
+ IPv6: delete IPv6 address from interface
+ dhcp: stop dhclient (IPv4) on interface
+ dhcpv6: stop dhclient (IPv6) on interface
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> j = Interface('eth0')
+ >>> j.add_addr('2001:db8::ffff/64')
+ >>> j.add_addr('192.0.2.1/24')
+ >>> j.get_addr()
+ ['192.0.2.1/24', '2001:db8::ffff/64']
+ >>> j.del_addr('192.0.2.1/24')
+ >>> j.get_addr()
+ ['2001:db8::ffff/64']
+ """
+ if addr == 'dhcp':
+ self._del_dhcp()
+ elif addr == 'dhcpv6':
+ self._del_dhcpv6()
+ else:
+ if is_intf_addr_assigned(self.config['ifname'], addr):
+ cmd = 'ip addr del "{}" dev "{}"'.format(addr, self.config['ifname'])
+ return self._cmd(cmd)
+
+ def op_show_interface_stats(self):
+ stats = self.get_interface_stats()
+ rx = [['bytes','packets','errors','dropped','overrun','mcast'],[stats['rx_bytes'],stats['rx_packets'],stats['rx_errors'],stats['rx_dropped'],stats['rx_over_errors'],stats['multicast']]]
+ tx = [['bytes','packets','errors','dropped','carrier','collisions'],[stats['tx_bytes'],stats['tx_packets'],stats['tx_errors'],stats['tx_dropped'],stats['tx_carrier_errors'],stats['collisions']]]
+ output = "RX: \n"
+ output += tabulate(rx,headers="firstrow",numalign="right",tablefmt="plain")
+ output += "\n\nTX: \n"
+ output += tabulate(tx,headers="firstrow",numalign="right",tablefmt="plain")
+ print(' '.join(('\n'+output.lstrip()).splitlines(True)))
+
+ def get_interface_stats(self):
+ interface_stats = dict()
+ devices = [f for f in glob.glob("/sys/class/net/**/statistics")]
+ for dev_path in devices:
+ metrics = [f for f in glob.glob(dev_path +"/**")]
+ dev = re.findall(r"/sys/class/net/(.*)/statistics",dev_path)[0]
+ dev_dict = dict()
+ for metric_path in metrics:
+ metric = metric_path.replace(dev_path+"/","")
+ if isfile(metric_path):
+ data = open(metric_path, 'r').read()[:-1]
+ dev_dict[metric] = int(data)
+ interface_stats[dev] = dev_dict
+
+ return interface_stats[self.config['ifname']]
+