From 0a755a0745755ae333b5f3b2b6be43d6816cbfc6 Mon Sep 17 00:00:00 2001
From: Pim van Pelt <pim@ipng.nl>
Date: Tue, 5 Apr 2022 15:50:49 +0000
Subject: [PATCH] Add bond/bridge YAML emitter

Add int_to_mode() and int_to_lb() in config/bondethernet.py to map back
the retrieved values from VPP into their config strings.

Implement bond and bridge settings dumper, dumping all settings even if
they are default. This helps the user understand the configurable
options.
---
 config/bondethernet.py      |  29 ++++++++
 config/test_bondethernet.py |  18 +++++
 vpp/vppapi.py               |  15 ++++
 yaml                        | 140 ++++++++++++++++++++++++++++++++++++
 4 files changed, 202 insertions(+)
 create mode 100644 yaml

diff --git a/config/bondethernet.py b/config/bondethernet.py
index 2d7d364..f842236 100644
--- a/config/bondethernet.py
+++ b/config/bondethernet.py
@@ -81,6 +81,20 @@ def mode_to_int(mode):
     return -1
 
 
+def int_to_mode(mode):
+    """ Returns the string representation in VPP of a given bondethernet mode,
+        or "" if 'mode' is not a valid id.
+
+        See src/vnet/bonding/bond.api and schema.yaml for valid pairs. """
+
+    ret = { 1: 'round-robin', 2: 'active-backup', 3: 'xor', 4: 'broadcast', 5: 'lacp' }
+    try:
+        return ret[mode]
+    except:
+        pass
+    return ""
+
+
 def get_lb(yaml, ifname):
     """ Return the loadbalance strategy of the BondEthernet as a string. Only
         'xor' and 'lacp' modes have loadbalance strategies, so return None if
@@ -116,6 +130,21 @@ def lb_to_int(lb):
     return -1
 
 
+def int_to_lb(lb):
+    """ Returns the string representation in VPP of a given load-balance strategy,
+        or "" if 'lb' is not a valid int.
+
+        See src/vnet/bonding/bond.api and schema.yaml for valid pairs, although
+        bond.api defined more than we use in vppcfg. """
+
+    ret = { 0: 'l2', 1: 'l34', 2: 'l23', 3: 'round-robin', 4: 'broadcast', 5: 'active-backup' }
+    try:
+        return ret[lb]
+    except:
+        pass
+    return ""
+
+
 def validate_bondethernets(yaml):
     result = True
     msgs = []
diff --git a/config/test_bondethernet.py b/config/test_bondethernet.py
index c394b9f..c2754d7 100644
--- a/config/test_bondethernet.py
+++ b/config/test_bondethernet.py
@@ -49,6 +49,15 @@ class TestBondEthernetMethods(unittest.TestCase):
         self.assertEqual(5, bondethernet.mode_to_int("lacp"))
         self.assertEqual(-1, bondethernet.mode_to_int("not-exist"))
 
+    def test_int_to_mode(self):
+        self.assertEqual("round-robin", bondethernet.int_to_mode(1))
+        self.assertEqual("active-backup", bondethernet.int_to_mode(2))
+        self.assertEqual("xor", bondethernet.int_to_mode(3))
+        self.assertEqual("broadcast", bondethernet.int_to_mode(4))
+        self.assertEqual("lacp", bondethernet.int_to_mode(5))
+        self.assertEqual("", bondethernet.int_to_mode(0))
+        self.assertEqual("", bondethernet.int_to_mode(6))
+
     def test_get_lb(self):
         self.assertEqual('l34', bondethernet.get_lb(self.cfg, "BondEthernet0"))
         self.assertEqual('l2', bondethernet.get_lb(self.cfg, "BondEthernet1"))
@@ -62,3 +71,12 @@ class TestBondEthernetMethods(unittest.TestCase):
         self.assertEqual(4, bondethernet.lb_to_int("broadcast"))
         self.assertEqual(5, bondethernet.lb_to_int("active-backup"))
         self.assertEqual(-1, bondethernet.lb_to_int("not-exist"))
+
+    def test_int_to_lb(self):
+        self.assertEqual("l2", bondethernet.int_to_lb(0))
+        self.assertEqual("l34", bondethernet.int_to_lb(1))
+        self.assertEqual("l23", bondethernet.int_to_lb(2))
+        self.assertEqual("round-robin", bondethernet.int_to_lb(3))
+        self.assertEqual("broadcast", bondethernet.int_to_lb(4))
+        self.assertEqual("active-backup", bondethernet.int_to_lb(5))
+        self.assertEqual("", bondethernet.int_to_lb(-1))
diff --git a/vpp/vppapi.py b/vpp/vppapi.py
index c497551..69ae98d 100644
--- a/vpp/vppapi.py
+++ b/vpp/vppapi.py
@@ -10,6 +10,7 @@ import fnmatch
 import logging
 import socket
 import yaml
+import config.bondethernet as bondethernet
 
 class VPPApi():
     def __init__(self, address='/run/vpp/api.sock', clientname='vppcfg'):
@@ -275,6 +276,10 @@ class VPPApiDumper(VPPApi):
             bond = {"description": ""}
             if iface.sw_if_index in self.cache['bondethernet_members']:
                 bond['interfaces'] = [self.cache['interfaces'][x].interface_name for x in self.cache['bondethernet_members'][iface.sw_if_index]]
+            mode = bondethernet.int_to_mode(iface.mode)
+            bond['mode'] = mode
+            if mode in ['xor', 'lacp']:
+                bond['load-balance'] = bondethernet.int_to_lb(iface.lb)
             config['bondethernets'][iface.interface_name] = bond
 
         for numtags in [ 0, 1, 2 ]:
@@ -344,6 +349,16 @@ class VPPApiDumper(VPPApi):
             bridge_name = "bd%d" % idx
             mtu = 1500
             bridge = {"description": ""}
+            settings = {}
+            settings['learn'] = iface.learn
+            settings['unicast-flood'] = iface.flood
+            settings['unknown-unicast-flood'] = iface.uu_flood
+            settings['unicast-forward'] = iface.forward
+            settings['arp-termination'] = iface.arp_term
+            settings['arp-unicast-forward'] = iface.arp_ufwd
+            settings['mac-age-minutes'] = int(iface.mac_age)
+            bridge['settings'] = settings
+
             bvi = None
             if iface.bvi_sw_if_index != 2**32-1:
                 bvi = self.cache['interfaces'][iface.bvi_sw_if_index]
diff --git a/yaml b/yaml
new file mode 100644
index 0000000..fb64080
--- /dev/null
+++ b/yaml
@@ -0,0 +1,140 @@
+bondethernets:
+  BondEthernet0:
+    description: ''
+    interfaces:
+    - GigabitEthernet3/0/0
+    - GigabitEthernet3/0/1
+    load-balance: l34
+    mode: lacp
+  BondEthernet1:
+    description: ''
+    interfaces: []
+    load-balance: l2
+    mode: xor
+  BondEthernet2:
+    description: ''
+    interfaces: []
+    mode: round-robin
+bridgedomains:
+  bd1:
+    bvi: loop2
+    description: ''
+    interfaces:
+    - BondEthernet0.500
+    - BondEthernet0.501
+    mtu: 2000
+    settings:
+      arp-termination: false
+      arp-unicast-forward: false
+      learn: false
+      mac-age-minutes: 10
+      unicast-flood: true
+      unicast-forward: true
+      unknown-unicast-flood: true
+  bd11:
+    description: ''
+    mtu: 1500
+    settings:
+      arp-termination: false
+      arp-unicast-forward: false
+      learn: true
+      mac-age-minutes: 0
+      unicast-flood: true
+      unicast-forward: true
+      unknown-unicast-flood: true
+interfaces:
+  BondEthernet0:
+    description: ''
+    lcp: be0
+    mtu: 9000
+    sub-interfaces:
+      100:
+        description: ''
+        encapsulation:
+          dot1q: 100
+          exact-match: false
+        l2xc: BondEthernet0.200
+        mtu: 2500
+      200:
+        description: ''
+        encapsulation:
+          dot1q: 200
+          exact-match: false
+        l2xc: BondEthernet0.100
+        mtu: 2500
+      500:
+        description: ''
+        encapsulation:
+          dot1ad: 500
+          exact-match: false
+        mtu: 2000
+      501:
+        description: ''
+        encapsulation:
+          dot1ad: 501
+          exact-match: false
+        mtu: 2000
+  BondEthernet1:
+    description: ''
+    mtu: 9000
+    state: down
+  BondEthernet2:
+    description: ''
+    mtu: 9000
+    state: down
+  GigabitEthernet3/0/0:
+    description: ''
+    mtu: 9000
+  GigabitEthernet3/0/1:
+    description: ''
+    mtu: 9000
+  HundredGigabitEthernet12/0/0:
+    description: ''
+    lcp: ice0
+    mtu: 1500
+  HundredGigabitEthernet12/0/1:
+    addresses:
+    - 192.0.2.17/30
+    - 2001:db8:3::1/64
+    description: ''
+    lcp: ice1
+    mtu: 9000
+    sub-interfaces:
+      1234:
+        description: ''
+        encapsulation:
+          dot1q: 1234
+          exact-match: true
+        lcp: ice1.1234
+        mtu: 1500
+      1235:
+        description: ''
+        encapsulation:
+          dot1q: 1234
+          exact-match: true
+          inner-dot1q: 1000
+        lcp: ice1.1234.1000
+        mtu: 1400
+  vxlan_tunnel1:
+    description: ''
+    mtu: 1500
+loopbacks:
+  loop1:
+    addresses:
+    - 192.0.2.1/30
+    description: ''
+    lcp: bvi1
+    mtu: 1500
+  loop2:
+    addresses:
+    - 192.0.2.5/30
+    description: ''
+    lcp: bvi2
+    mtu: 1500
+vxlan_tunnels:
+  vxlan_tunnel1:
+    description: ''
+    local: 192.0.2.1
+    remote: 192.0.2.2
+    vni: 101
+