From e835aef3a0ebbc957abd38e54f8040f5b67157ce Mon Sep 17 00:00:00 2001
From: Thomas Mangin <thomas.mangin@exa.net.uk>
Date: Sat, 11 Apr 2020 14:27:57 +0100
Subject: ifconfig: T2223: rename Register to Section

While the class does indeed all the registration, it work is
really to map classes to interface section.
ie: interface ethernet -> EthernetIf

Therefore it can also list which interface are from which
type, therefore the name change. Other function name will
also be renamed as a consequence
---
 python/vyos/ifconfig/__init__.py       |  1 +
 python/vyos/ifconfig/control.py        |  4 +-
 python/vyos/ifconfig/register.py       | 95 ----------------------------------
 python/vyos/ifconfig/section.py        | 95 ++++++++++++++++++++++++++++++++++
 src/completion/list_openvpn_clients.py |  4 +-
 5 files changed, 100 insertions(+), 99 deletions(-)
 delete mode 100644 python/vyos/ifconfig/register.py
 create mode 100644 python/vyos/ifconfig/section.py

diff --git a/python/vyos/ifconfig/__init__.py b/python/vyos/ifconfig/__init__.py
index 1f9956af0..cd1696ca1 100644
--- a/python/vyos/ifconfig/__init__.py
+++ b/python/vyos/ifconfig/__init__.py
@@ -14,6 +14,7 @@
 # License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 
 
+from vyos.ifconfig.section import Section
 from vyos.ifconfig.interface import Interface
 
 from vyos.ifconfig.bond import BondIf
diff --git a/python/vyos/ifconfig/control.py b/python/vyos/ifconfig/control.py
index c7a2fa2d6..464cd585e 100644
--- a/python/vyos/ifconfig/control.py
+++ b/python/vyos/ifconfig/control.py
@@ -18,10 +18,10 @@ import os
 
 from vyos.util import debug, debug_msg
 from vyos.util import popen, cmd
-from vyos.ifconfig.register import Register
+from vyos.ifconfig.section import Section
 
 
-class Control(Register):
+class Control(Section):
     _command_get = {}
     _command_set = {}
 
diff --git a/python/vyos/ifconfig/register.py b/python/vyos/ifconfig/register.py
deleted file mode 100644
index c90782b70..000000000
--- a/python/vyos/ifconfig/register.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# Copyright 2020 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 netifaces
-
-
-class Register:
-    # the known interface prefixes
-    _prefixes = {}
-
-    # class need to define: definition['prefixes']
-    # the interface prefixes declared by a class used to name interface with
-    # prefix[0-9]*(\.[0-9]+)?(\.[0-9]+)?, such as lo, eth0 or eth0.1.2
-
-    @classmethod
-    def register(cls, klass):
-        if not klass.definition.get('prefixes',[]):
-            raise RuntimeError(f'valid interface prefixes not defined for {klass.__name__}')
-
-        for ifprefix in klass.definition['prefixes']:
-            if ifprefix in cls._prefixes:
-                raise RuntimeError(f'only one class can be registered for prefix "{ifprefix}" type')
-            cls._prefixes[ifprefix] = klass
-
-        return klass
-
-    @classmethod
-    def _basename (cls, name, vlan):
-        # remove number from interface name
-        name = name.rstrip('0123456789')
-        name = name.rstrip('.')
-        if vlan:
-            name = name.rstrip('0123456789')
-        return name
-
-    @classmethod
-    def section(cls, name, vlan=True):
-        # return the name of a section an interface should be under
-        name = cls._basename(name, vlan)
-
-        # XXX: To leave as long as vti and input are not moved to vyos
-        if name == 'vti':
-            return 'vti'
-        if name == 'ifb':
-            return 'input'
-
-        if name in cls._prefixes:
-            return cls._prefixes[name].definition['section']
-        return ''
- 
-    @classmethod
-    def klass(cls, name, vlan=True):
-        name = cls._basename(name, vlan)
-        if name in cls._prefixes:
-            return cls._prefixes[name]
-        raise ValueError(f'No type found for interface name: {name}')
-
-    @classmethod
-    def _listing (cls,section=''):
-        interfaces = netifaces.interfaces()
-
-        for ifname in interfaces:
-            # XXX: Temporary hack as vti and input are not yet moved from vyatta to vyos
-            if ifname.startswith('vti') or ifname.startswith('input'):
-                yield ifname
-                continue
-
-            ifsection = cls.section(ifname)
-            if not ifsection:
-                continue
-
-            if section and ifsection != section:
-                continue
-
-            yield ifname
-
-    @classmethod
-    def listing(cls, section=''):
-        return list(cls._listing(section))
-
-
-# XXX: TODO - limit name for VRF interfaces
-
diff --git a/python/vyos/ifconfig/section.py b/python/vyos/ifconfig/section.py
new file mode 100644
index 000000000..eeec1968a
--- /dev/null
+++ b/python/vyos/ifconfig/section.py
@@ -0,0 +1,95 @@
+# Copyright 2020 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 netifaces
+
+
+class Section:
+    # the known interface prefixes
+    _prefixes = {}
+
+    # class need to define: definition['prefixes']
+    # the interface prefixes declared by a class used to name interface with
+    # prefix[0-9]*(\.[0-9]+)?(\.[0-9]+)?, such as lo, eth0 or eth0.1.2
+
+    @classmethod
+    def register(cls, klass):
+        if not klass.definition.get('prefixes',[]):
+            raise RuntimeError(f'valid interface prefixes not defined for {klass.__name__}')
+
+        for ifprefix in klass.definition['prefixes']:
+            if ifprefix in cls._prefixes:
+                raise RuntimeError(f'only one class can be registered for prefix "{ifprefix}" type')
+            cls._prefixes[ifprefix] = klass
+
+        return klass
+
+    @classmethod
+    def _basename (cls, name, vlan):
+        # remove number from interface name
+        name = name.rstrip('0123456789')
+        name = name.rstrip('.')
+        if vlan:
+            name = name.rstrip('0123456789')
+        return name
+
+    @classmethod
+    def section(cls, name, vlan=True):
+        # return the name of a section an interface should be under
+        name = cls._basename(name, vlan)
+
+        # XXX: To leave as long as vti and input are not moved to vyos
+        if name == 'vti':
+            return 'vti'
+        if name == 'ifb':
+            return 'input'
+
+        if name in cls._prefixes:
+            return cls._prefixes[name].definition['section']
+        return ''
+ 
+    @classmethod
+    def klass(cls, name, vlan=True):
+        name = cls._basename(name, vlan)
+        if name in cls._prefixes:
+            return cls._prefixes[name]
+        raise ValueError(f'No type found for interface name: {name}')
+
+    @classmethod
+    def _listing (cls,section=''):
+        interfaces = netifaces.interfaces()
+
+        for ifname in interfaces:
+            # XXX: Temporary hack as vti and input are not yet moved from vyatta to vyos
+            if ifname.startswith('vti') or ifname.startswith('input'):
+                yield ifname
+                continue
+
+            ifsection = cls.section(ifname)
+            if not ifsection:
+                continue
+
+            if section and ifsection != section:
+                continue
+
+            yield ifname
+
+    @classmethod
+    def listing(cls, section=''):
+        return list(cls._listing(section))
+
+
+# XXX: TODO - limit name for VRF interfaces
+
diff --git a/src/completion/list_openvpn_clients.py b/src/completion/list_openvpn_clients.py
index 17b0c7008..177ac90c9 100755
--- a/src/completion/list_openvpn_clients.py
+++ b/src/completion/list_openvpn_clients.py
@@ -18,7 +18,7 @@ import os
 import sys
 import argparse
 
-from vyos.ifconfig import Interface
+from vyos.ifconfig import Section
 
 def get_client_from_interface(interface):
     clients = []
@@ -50,7 +50,7 @@ if __name__ == "__main__":
     if args.interface:
         clients = get_client_from_interface(args.interface)
     elif args.all:
-        for interface in Interface.listing("openvpn"):
+        for interface in Section.interfaces("openvpn"):
             clients += get_client_from_interface(interface)
 
     print(" ".join(clients))
-- 
cgit v1.2.3


From d3aa0bd68bb72bc35eb290b1c54cae4dfa4efb57 Mon Sep 17 00:00:00 2001
From: Thomas Mangin <thomas.mangin@exa.net.uk>
Date: Sat, 11 Apr 2020 14:43:28 +0100
Subject: ifconfig: T2223: rename Section.listing to interfaces

update all code using the API. Interface.interfaces() could be
used but the code was change to use Section.interfaces() which
make more sense when reading it.
---
 python/vyos/ifconfig/section.py       | 30 +++++++++++++++++++++++++-----
 src/completion/list_interfaces.py     | 30 +++++++++++++++---------------
 src/conf_mode/flow_accounting_conf.py |  7 ++++---
 3 files changed, 44 insertions(+), 23 deletions(-)

diff --git a/python/vyos/ifconfig/section.py b/python/vyos/ifconfig/section.py
index eeec1968a..4f76258a7 100644
--- a/python/vyos/ifconfig/section.py
+++ b/python/vyos/ifconfig/section.py
@@ -26,6 +26,11 @@ class Section:
 
     @classmethod
     def register(cls, klass):
+        """
+        A function to use as decorator the interfaces classes
+        It register the prefix for the interface (eth, dum, vxlan, ...)
+        with the class which can handle it (EthernetIf, DummyIf,VXLANIf, ...)
+        """
         if not klass.definition.get('prefixes',[]):
             raise RuntimeError(f'valid interface prefixes not defined for {klass.__name__}')
 
@@ -38,7 +43,11 @@ class Section:
 
     @classmethod
     def _basename (cls, name, vlan):
-        # remove number from interface name
+        """
+        remove the number at the end of interface name
+        name: name of the interface
+        vlan: if vlan is True, do not stop at the vlan number
+        """
         name = name.rstrip('0123456789')
         name = name.rstrip('.')
         if vlan:
@@ -47,7 +56,11 @@ class Section:
 
     @classmethod
     def section(cls, name, vlan=True):
-        # return the name of a section an interface should be under
+        """
+        return the name of a section an interface should be under
+        name: name of the interface (eth0, dum1, ...)
+        vlan: should we try try to remove the VLAN from the number
+        """
         name = cls._basename(name, vlan)
 
         # XXX: To leave as long as vti and input are not moved to vyos
@@ -68,7 +81,10 @@ class Section:
         raise ValueError(f'No type found for interface name: {name}')
 
     @classmethod
-    def _listing (cls,section=''):
+    def _intf_under_section (cls,section=''):
+        """
+        return a generator with the name of the interface which are under a section
+        """
         interfaces = netifaces.interfaces()
 
         for ifname in interfaces:
@@ -87,8 +103,12 @@ class Section:
             yield ifname
 
     @classmethod
-    def listing(cls, section=''):
-        return list(cls._listing(section))
+    def interfaces(cls, section=''):
+        """
+        return a list of the name of the interface which are under a section
+        if no section is provided, then it returns all configured interfaces
+        """
+        return list(cls._intf_under_section(section))
 
 
 # XXX: TODO - limit name for VRF interfaces
diff --git a/src/completion/list_interfaces.py b/src/completion/list_interfaces.py
index 98b32797a..64aecef60 100755
--- a/src/completion/list_interfaces.py
+++ b/src/completion/list_interfaces.py
@@ -2,7 +2,7 @@
 
 import sys
 import argparse
-from vyos.ifconfig import Interface
+from vyos.ifconfig import Section
 
 parser = argparse.ArgumentParser()
 group = parser.add_mutually_exclusive_group()
@@ -19,33 +19,33 @@ args = parser.parse_args()
 
 if args.type:
     try:
-        interfaces = Interface.listing(args.type)
+        interfaces = Section.interfaces(args.type)
 
     except ValueError as e:
         print(e, file=sys.stderr)
         print("")
 
 elif args.broadcast:
-    eth = Interface.listing("ethernet")
-    bridge = Interface.listing("bridge")
-    bond = Interface.listing("bonding")
+    eth = Section.interfaces("ethernet")
+    bridge = Section.interfaces("bridge")
+    bond = Section.interfaces("bonding")
     interfaces = eth + bridge + bond
 
 elif args.bridgeable:
-    eth = Interface.listing("ethernet")
-    bond = Interface.listing("bonding")
-    l2tpv3 = Interface.listing("l2tpv3")
-    openvpn = Interface.listing("openvpn")
-    wireless = Interface.listing("wireless")
-    tunnel = Interface.listing("tunnel")
-    vxlan = Interface.listing("vxlan")
-    geneve = Interface.listing("geneve")
+    eth = Section.interfaces("ethernet")
+    bond = Section.interfaces("bonding")
+    l2tpv3 = Section.interfaces("l2tpv3")
+    openvpn = Section.interfaces("openvpn")
+    wireless = Section.interfaces("wireless")
+    tunnel = Section.interfaces("tunnel")
+    vxlan = Section.interfaces("vxlan")
+    geneve = Section.interfaces("geneve")
 
     interfaces = eth + bond + l2tpv3 + openvpn + vxlan + tunnel + wireless + geneve
 
 elif args.bondable:
     interfaces = []
-    eth = Interface.listing("ethernet")
+    eth = Section.interfaces("ethernet")
 
     # we need to filter out VLAN interfaces identified by a dot (.) in their name
     for intf in eth:
@@ -53,6 +53,6 @@ elif args.bondable:
             interfaces.append(intf)
 
 else:
-    interfaces = Interface.listing()
+    interfaces = Section.interfaces()
 
 print(" ".join(interfaces))
diff --git a/src/conf_mode/flow_accounting_conf.py b/src/conf_mode/flow_accounting_conf.py
index 1008f3fae..eef32687e 100755
--- a/src/conf_mode/flow_accounting_conf.py
+++ b/src/conf_mode/flow_accounting_conf.py
@@ -22,6 +22,7 @@ from ipaddress import ip_address
 from jinja2 import FileSystemLoader, Environment
 from sys import exit
 
+from vyos.ifconfig import Section
 from vyos.ifconfig import Interface
 from vyos.config import Config
 from vyos.defaults import directories as vyos_data_dir
@@ -60,7 +61,7 @@ def _sflow_default_agentip(config):
         return config.return_value('protocols ospfv3 parameters router-id')
 
     # if router-id was not found, use first available ip of any interface
-    for iface in Interface.listing():
+    for iface in Section.interfaces():
         for address in Interface(iface).get_addr():
             # return an IP, if this is not loopback
             regex_filter = re.compile('^(?!(127)|(::1)|(fe80))(?P<ipaddr>[a-f\d\.:]+)/\d+$')
@@ -234,7 +235,7 @@ def verify(config):
 
     # check that all configured interfaces exists in the system
     for iface in config['interfaces']:
-        if not iface in Interface.listing():
+        if not iface in Section.interfaces():
             # chnged from error to warning to allow adding dynamic interfaces and interface templates
             # raise ConfigError("The {} interface is not presented in the system".format(iface))
             print("Warning: the {} interface is not presented in the system".format(iface))
@@ -262,7 +263,7 @@ def verify(config):
 
         # check if configured sFlow agent-id exist in the system
         agent_id_presented = None
-        for iface in Interface.listing():
+        for iface in Section.interfaces():
             for address in Interface(iface).get_addr():
                 # check an IP, if this is not loopback
                 regex_filter = re.compile('^(?!(127)|(::1)|(fe80))(?P<ipaddr>[a-f\d\.:]+)/\d+$')
-- 
cgit v1.2.3


From 8bc12847568582d3b34f3859946470af1344f9ea Mon Sep 17 00:00:00 2001
From: Thomas Mangin <thomas.mangin@exa.net.uk>
Date: Sat, 11 Apr 2020 16:42:40 +0100
Subject: completion: T2238: use interface data

Use the data in the default dict of the interface to generate
the output of list interface.
---
 python/vyos/ifconfig/section.py   | 23 ++++++++++++++++++++--
 src/completion/list_interfaces.py | 40 ++++++++++++---------------------------
 2 files changed, 33 insertions(+), 30 deletions(-)

diff --git a/python/vyos/ifconfig/section.py b/python/vyos/ifconfig/section.py
index 4f76258a7..234c9a6cc 100644
--- a/python/vyos/ifconfig/section.py
+++ b/python/vyos/ifconfig/section.py
@@ -19,6 +19,7 @@ import netifaces
 class Section:
     # the known interface prefixes
     _prefixes = {}
+    _classes = []
 
     # class need to define: definition['prefixes']
     # the interface prefixes declared by a class used to name interface with
@@ -34,6 +35,8 @@ class Section:
         if not klass.definition.get('prefixes',[]):
             raise RuntimeError(f'valid interface prefixes not defined for {klass.__name__}')
 
+        cls._classes.append(klass)
+
         for ifprefix in klass.definition['prefixes']:
             if ifprefix in cls._prefixes:
                 raise RuntimeError(f'only one class can be registered for prefix "{ifprefix}" type')
@@ -110,6 +113,22 @@ class Section:
         """
         return list(cls._intf_under_section(section))
 
+    @classmethod
+    def _intf_with_feature(cls, feature=''):
+        """
+        return a generator with the name of the interface which have 
+        a particular feature set in their definition such as:
+        bondable, broadcast, bridgeable, ...
+        """
+        for klass in cls._classes:
+            if klass.definition[feature]:
+                yield klass.definition['section']
 
-# XXX: TODO - limit name for VRF interfaces
-
+    @classmethod
+    def feature(cls, feature=''):
+        """
+        return list with the name of the interface which have 
+        a particular feature set in their definition such as:
+        bondable, broadcast, bridgeable, ...
+        """
+        return list(cls._intf_with_feature(feature))
diff --git a/src/completion/list_interfaces.py b/src/completion/list_interfaces.py
index 64aecef60..e27281433 100755
--- a/src/completion/list_interfaces.py
+++ b/src/completion/list_interfaces.py
@@ -4,6 +4,13 @@ import sys
 import argparse
 from vyos.ifconfig import Section
 
+
+def matching(feature):
+    for section in Section.feature(feature):
+        for intf in Section.interfaces(section):
+            yield intf
+
+
 parser = argparse.ArgumentParser()
 group = parser.add_mutually_exclusive_group()
 group.add_argument("-t", "--type", type=str, help="List interfaces of specific type")
@@ -13,46 +20,23 @@ group.add_argument("-bo", "--bondable", action="store_true", help="List all bond
 
 args = parser.parse_args()
 
-# XXX: Need to be rewritten using the data in the class definition
-# XXX: It can be done once vti and input are moved into vyos
-# XXX: We store for each class what type they are (broadcast, bridgeabe, ...)
-
 if args.type:
     try:
         interfaces = Section.interfaces(args.type)
-
+        print(" ".join(interfaces))
     except ValueError as e:
         print(e, file=sys.stderr)
         print("")
 
 elif args.broadcast:
-    eth = Section.interfaces("ethernet")
-    bridge = Section.interfaces("bridge")
-    bond = Section.interfaces("bonding")
-    interfaces = eth + bridge + bond
+    print(" ".join(matching("broadcast")))
 
 elif args.bridgeable:
-    eth = Section.interfaces("ethernet")
-    bond = Section.interfaces("bonding")
-    l2tpv3 = Section.interfaces("l2tpv3")
-    openvpn = Section.interfaces("openvpn")
-    wireless = Section.interfaces("wireless")
-    tunnel = Section.interfaces("tunnel")
-    vxlan = Section.interfaces("vxlan")
-    geneve = Section.interfaces("geneve")
-
-    interfaces = eth + bond + l2tpv3 + openvpn + vxlan + tunnel + wireless + geneve
+    print(" ".join(matching("bridgeable")))
 
 elif args.bondable:
-    interfaces = []
-    eth = Section.interfaces("ethernet")
-
     # we need to filter out VLAN interfaces identified by a dot (.) in their name
-    for intf in eth:
-        if not '.' in intf:
-            interfaces.append(intf)
+    print(" ".join([intf for intf in matching("bondable") if '.' not in intf]))
 
 else:
-    interfaces = Section.interfaces()
-
-print(" ".join(interfaces))
+    print(" ".join(Section.interfaces()))
-- 
cgit v1.2.3