From 90a97a3d7bf1ef47f1fdeddf2435ccaaa81fd49f Mon Sep 17 00:00:00 2001 From: Pim van Pelt Date: Sat, 26 Mar 2022 11:14:30 +0000 Subject: [PATCH] Add Create Phase This is rather straight forward: for each object (in correct order), if the object exists in VPP, we can skip it. We know that it will exist only if it was valid (ie correct encapsulation, tc). If it does not exist in VPP, issue the correct creation request to VPP. Implement the creation of all types, in the following order: - create_loopbacks() and create_bvis() - create_bondethernets() - create_vxlan_tunnels() - create_sub_interfaces() first 1-tag, then 2-tag - create_bridgedomains() - create_lcps() bottomsup: first 0-tag, then 1-tag, then 2-tag names. Add a placeholder TODO to fix a bug with prune_sub_interfaces() which should allow for TAPs belonging to LCPs; will fix in followup commit. --- README.md | 7 ++- vpp/reconciler.py | 132 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 132 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 316dd6c..7b9edcd 100644 --- a/README.md +++ b/README.md @@ -172,11 +172,10 @@ and finally all objects are synchronized with the configuration (IP addresses, M ### Creating 1. Loopbacks -1. BondEthernets -1. Dot1Q and Dot1AD sub-interfaces -1. Qin1Q and Qin1AD sub-interfaces -1. Tunnels 1. BVIs +1. BondEthernets +1. Tunnels +1. Sub Interfaces: First Dot1Q and Dot1AD (one tag), and then QinQ and QinAD (two tags) 1. Bridge Domains 1. LCP pairs for Tunnels (TUN type) 1. LCP pairs for PHYs, BondEthernets, Dot1Q/Dot1AD and finally QinQ/QinAD (TAP type) diff --git a/vpp/reconciler.py b/vpp/reconciler.py index 2b074e7..348ba78 100644 --- a/vpp/reconciler.py +++ b/vpp/reconciler.py @@ -55,7 +55,7 @@ class Reconciler(): self.logger.warning("Could not prune LCPs from VPP") ret = False if not self.prune_loopbacks(): - self.logger.warning("Could not prune loopbacks from VPP") + self.logger.warning("Could not prune Loopbacks from VPP") ret = False if not self.prune_bridgedomains(): self.logger.warning("Could not prune BridgeDomains from VPP") @@ -67,7 +67,7 @@ class Reconciler(): self.logger.warning("Could not prune L2 Cross Connects from VPP") ret = False if not self.prune_sub_interfaces(): - self.logger.warning("Could not prune sub-interfaces from VPP") + self.logger.warning("Could not prune Sub Interfaces from VPP") ret = False if not self.prune_vxlan_tunnels(): self.logger.warning("Could not prune VXLAN Tunnels from VPP") @@ -306,6 +306,7 @@ class Reconciler(): vpp_iface = self.vpp.config['interface_names'][vpp_ifname] if vpp_iface.sub_number_of_tags != numtags: continue + ## TODO(pim) - if the sub-int is a TAP belonging to an LCP, it should be allowed config_ifname, config_iface = interface.get_by_name(self.cfg, vpp_ifname) if not config_iface: self.prune_addresses(vpp_ifname, []) @@ -551,7 +552,132 @@ class Reconciler(): return True def create(self): - return False + """ Create all objects in VPP that occur in the config but not in VPP. For an indepth + explanation of how and why this particular pruning order is chosen, see README.md + section on Reconciling. """ + ret = True + if not self.create_loopbacks(): + self.logger.warning("Could not create Loopbacks in VPP") + ret = False + if not self.create_bvis(): + self.logger.warning("Could not create BVIs in VPP") + ret = False + if not self.create_bondethernets(): + self.logger.warning("Could not create BondEthernets in VPP") + ret = False + if not self.create_vxlan_tunnels(): + self.logger.warning("Could not create VXLAN Tunnels in VPP") + ret = False + if not self.create_sub_interfaces(): + self.logger.warning("Could not create Sub Interfaces in VPP") + ret = False + if not self.create_bridgedomains(): + self.logger.warning("Could not create BridgeDomains in VPP") + ret = False + if not self.create_lcps(): + self.logger.warning("Could not create LCPs in VPP") + ret = False + return ret + + def create_loopbacks(self): + for ifname in loopback.get_loopbacks(self.cfg): + if ifname in self.vpp.config['interface_names']: + continue + instance = int(ifname[4:]) + self.logger.info("1> create loopback interface instance %d" % (instance)) + return True + + def create_bvis(self): + for ifname in bridgedomain.get_bridgedomains(self.cfg): + ifname, iface = bridgedomain.get_by_name(self.cfg, ifname) + instance = int(ifname[2:]) + if not 'lcp' in iface: + continue + if 'bvi%d'%instance in self.vpp.config['interface_names']: + continue + self.logger.info("1> bvi create instance %d" % (instance)) + return True + + def create_bondethernets(self): + for ifname in bondethernet.get_bondethernets(self.cfg): + if ifname in self.vpp.config['interface_names']: + continue + ifname, iface = bondethernet.get_by_name(self.cfg, ifname) + instance = int(ifname[12:]) + self.logger.info("1> create bond mode lacp load-balance l34 id %d" % (instance)) + return True + + def create_vxlan_tunnels(self): + for ifname in vxlan_tunnel.get_vxlan_tunnels(self.cfg): + if ifname in self.vpp.config['interface_names']: + continue + ifname, iface = vxlan_tunnel.get_by_name(self.cfg, ifname) + instance = int(ifname[12:]) + self.logger.info("1> create vxlan tunnel src %s dst %s instance %d vni %d decap-next l2" % ( + iface['local'], iface['remote'], instance, iface['vni'])) + return True + + def create_sub_interfaces(self): + ## First create 1-tag (Dot1Q/Dot1AD), and then create 2-tag (Qin*) sub-interfaces + for do_qinx in [False, True]: + for ifname in interface.get_sub_interfaces(self.cfg): + if not do_qinx == interface.is_qinx(self.cfg, ifname): + continue + + ifname, iface = interface.get_by_name(self.cfg, ifname) + if ifname in self.vpp.config['interface_names']: + continue + + ## Assemble the encapsulation string + encap = interface.get_encapsulation(self.cfg, ifname) + if encap['dot1ad'] > 0: + encapstr = "dot1ad %d" % encap['dot1ad'] + else: + encapstr = "dot1q %d" % encap['dot1q'] + if do_qinx: + encapstr += " inner-dot1q %d" % encap['inner-dot1q'] + if encap['exact-match'] == True: + encapstr += " exact-match" + parent, subid = ifname.split('.') + self.logger.info("1> create sub %s %d %s" % (parent, int(subid), encapstr)) + return True + + def create_bridgedomains(self): + for ifname in bridgedomain.get_bridgedomains(self.cfg): + ifname, iface = bridgedomain.get_by_name(self.cfg, ifname) + instance = int(ifname[2:]) + if instance in self.vpp.config['bridgedomains']: + continue + self.logger.info("1> create bridge-domain %s" % (instance)) + return True + + def create_lcps(self): + lcpnames = [self.vpp.config['lcps'][x].host_if_name for x in self.vpp.config['lcps']] + + ## First create untagged ... + for ifname in interface.get_interfaces(self.cfg): + if interface.is_sub(self.cfg, ifname): + continue + + ifname, iface = interface.get_by_name(self.cfg, ifname) + if not 'lcp' in iface: + continue + if iface['lcp'] in lcpnames: + continue + self.logger.info("1> lcp create %s host-if %s" % (ifname, iface['lcp'])) + + ## ... then 1-tag (Dot1Q/Dot1AD), and then create 2-tag (Qin*) LCPs + for do_qinx in [False, True]: + for ifname in interface.get_sub_interfaces(self.cfg): + if not do_qinx == interface.is_qinx(self.cfg, ifname): + continue + ifname, iface = interface.get_by_name(self.cfg, ifname) + if not 'lcp' in iface: + continue + if iface['lcp'] in lcpnames: + continue + self.logger.info("1> lcp create %s host-if %s" % (ifname, iface['lcp'])) + return True def sync(self): return False