diff --git a/example.yaml b/example.yaml index cfa6137..99d595f 100644 --- a/example.yaml +++ b/example.yaml @@ -1,6 +1,7 @@ bondethernets: BondEthernet0: interfaces: [ GigabitEthernet3/0/0, GigabitEthernet3/0/1 ] + mac: 02:b0:b0:01:02:03 mode: xor load-balance: l2 @@ -14,6 +15,7 @@ interfaces: HundredGigabitEthernet12/0/0: lcp: "ice12-0-0" + mac: f2:01:00:12:00:00 mtu: 9000 addresses: [ 192.0.2.17/30, 2001:db8:3::1/64 ] sub-interfaces: @@ -74,6 +76,7 @@ loopbacks: loop1: lcp: "bvi1" mtu: 2000 + mac: 02:de:ad:01:be:ef addresses: [ 10.0.1.1/24, 2001:db8:1::1/64 ] bridgedomains: diff --git a/intest/hippo-empty.yaml b/intest/hippo-empty.yaml index 35e1811..68c59bc 100644 --- a/intest/hippo-empty.yaml +++ b/intest/hippo-empty.yaml @@ -1,17 +1,21 @@ interfaces: GigabitEthernet3/0/0: mtu: 1500 + mac: 00:25:90:0c:05:00 state: down description: Not Used GigabitEthernet3/0/1: mtu: 1500 + mac: 00:25:90:0c:05:01 state: down description: Not Used HundredGigabitEthernet12/0/0: mtu: 1500 + mac: b4:96:91:b3:b1:10 state: down description: Not Used HundredGigabitEthernet12/0/1: mtu: 1500 + mac: b4:96:91:b3:b1:11 state: down description: Not Used diff --git a/intest/hippo12.yaml b/intest/hippo12.yaml index 6cf930f..d198bc3 100644 --- a/intest/hippo12.yaml +++ b/intest/hippo12.yaml @@ -70,6 +70,7 @@ interfaces: loopbacks: loop0: lcp: "lo0" + mac: de:ad:00:be:ef:00 addresses: [ 10.0.0.1/32, 2001:db8::1/128 ] loop1: mtu: 2000 diff --git a/intest/hippo13.yaml b/intest/hippo13.yaml index 28a9d0b..1ec0cc8 100644 --- a/intest/hippo13.yaml +++ b/intest/hippo13.yaml @@ -1,5 +1,6 @@ bondethernets: BondEthernet0: + mac: 02:b0:b0:00:00:01 interfaces: [ GigabitEthernet3/0/0, GigabitEthernet3/0/1 ] interfaces: diff --git a/intest/hippo3.yaml b/intest/hippo3.yaml index cc7911c..ddcce4b 100644 --- a/intest/hippo3.yaml +++ b/intest/hippo3.yaml @@ -1,5 +1,6 @@ bondethernets: BondEthernet1: + mac: 02:b0:b0:00:00:02 interfaces: [ GigabitEthernet3/0/0, GigabitEthernet3/0/1 ] mode: round-robin diff --git a/intest/hippo4.yaml b/intest/hippo4.yaml index eaca1e4..9a0b2f9 100644 --- a/intest/hippo4.yaml +++ b/intest/hippo4.yaml @@ -1,5 +1,6 @@ interfaces: GigabitEthernet3/0/0: + mac: 12:00:ba:03:00:00 mtu: 9216 sub-interfaces: 100: diff --git a/intest/hippo7.yaml b/intest/hippo7.yaml index c4c0991..db363e7 100644 --- a/intest/hippo7.yaml +++ b/intest/hippo7.yaml @@ -1,5 +1,6 @@ interfaces: GigabitEthernet3/0/0: + mac: 02:ff:ba:03:00:00 mtu: 9216 sub-interfaces: 100: diff --git a/intest/hippo8.yaml b/intest/hippo8.yaml index ed56410..92bce2d 100644 --- a/intest/hippo8.yaml +++ b/intest/hippo8.yaml @@ -45,6 +45,7 @@ interfaces: loopbacks: loop11: mtu: 3000 + mac: de:ad:00:be:ef:11 lcp: "bvi11" addresses: [ 2001:db8:1::1/64, 192.0.2.1/30 ] diff --git a/intest/hippo9.yaml b/intest/hippo9.yaml index 1a83d97..0200740 100644 --- a/intest/hippo9.yaml +++ b/intest/hippo9.yaml @@ -1,5 +1,6 @@ bondethernets: BondEthernet0: + mac: 02:b0:b0:00:00:00 interfaces: [ GigabitEthernet3/0/0, GigabitEthernet3/0/1 ] interfaces: @@ -11,6 +12,7 @@ interfaces: description: "LAG #2" HundredGigabitEthernet12/0/0: + mac: 02:ff:ba:12:00:00 lcp: "ice0" HundredGigabitEthernet12/0/1: diff --git a/intest/intest.sh b/intest/intest.sh index 2aff749..eca54b2 100755 --- a/intest/intest.sh +++ b/intest/intest.sh @@ -25,5 +25,9 @@ echo " - Checking that from $j to $j is empty" ../vppcfg plan -s ../schema.yaml -c $j -o /tmp/vppcfg-exec_${j}_${j}_null + [ -s /tmp/vppcfg-exec_${j}_${j}_null ] && { + echo " - ERROR Transition is not empty" + cat /tmp/vppcfg-exec_${j}_${j}_null + } done done diff --git a/vpp/reconciler.py b/vpp/reconciler.py index d0bef87..9e43781 100644 --- a/vpp/reconciler.py +++ b/vpp/reconciler.py @@ -613,6 +613,9 @@ class Reconciler(): continue instance = int(ifname[4:]) cli="create loopback interface instance %d" % (instance) + ifname, iface = loopback.get_by_name(self.cfg, ifname) + if 'mac' in iface: + cli += " mac %s" % iface['mac'] self.cli['create'].append(cli); return True @@ -627,6 +630,8 @@ class Reconciler(): lb = bondethernet.get_lb(self.cfg, ifname) if lb: cli += " load-balance %s" % lb + if 'mac' in iface: + cli += " hw-addr %s" % iface['mac'] self.cli['create'].append(cli); return True @@ -727,6 +732,9 @@ class Reconciler(): def sync(self): ret = True + if not self.sync_loopbacks(): + self.logger.warning("Could not sync Loopbacks in VPP") + ret = False if not self.sync_bondethernets(): self.logger.warning("Could not sync bondethernets in VPP") ret = False @@ -742,18 +750,46 @@ class Reconciler(): if not self.sync_addresses(): self.logger.warning("Could not sync interface addresses in VPP") ret = False + if not self.sync_phys(): + self.logger.warning("Could not sync PHYs in VPP") + ret = False if not self.sync_admin_state(): self.logger.warning("Could not sync interface adminstate in VPP") ret = False return ret + def sync_loopbacks(self): + for ifname in loopback.get_loopbacks(self.cfg): + if not ifname in self.vpp.cache['interface_names']: + ## New loopback + continue + vpp_iface = self.vpp.cache['interface_names'][ifname] + config_ifname, config_iface = loopback.get_by_name(self.cfg, ifname) + if 'mac' in config_iface and config_iface['mac'] != str(vpp_iface.l2_address): + cli="set interface mac address %s %s" % (config_ifname, config_iface['mac']) + self.cli['sync'].append(cli) + return True + + def sync_phys(self): + for ifname in interface.get_phys(self.cfg): + if not ifname in self.vpp.cache['interface_names']: + ## New interface + continue + vpp_iface = self.vpp.cache['interface_names'][ifname] + config_ifname, config_iface = interface.get_by_name(self.cfg, ifname) + if 'mac' in config_iface and config_iface['mac'] != str(vpp_iface.l2_address): + cli="set interface mac address %s %s" % (config_ifname, config_iface['mac']) + self.cli['sync'].append(cli) + return True + def sync_bondethernets(self): for ifname in bondethernet.get_bondethernets(self.cfg): if ifname in self.vpp.cache['interface_names']: - vpp_bond_sw_if_index = self.vpp.cache['interface_names'][ifname].sw_if_index - vpp_members = [self.vpp.cache['interfaces'][x].interface_name for x in self.vpp.cache['bondethernet_members'][vpp_bond_sw_if_index]] + vpp_iface = self.vpp.cache['interface_names'][ifname] + vpp_members = [self.vpp.cache['interfaces'][x].interface_name for x in self.vpp.cache['bondethernet_members'][vpp_iface.sw_if_index]] else: ## New BondEthernet + vpp_iface = None vpp_members = [] config_bond_ifname, config_bond_iface = bondethernet.get_by_name(self.cfg, ifname) @@ -769,7 +805,10 @@ class Reconciler(): bondmac = member_iface.l2_address cli="bond add %s %s" % (config_bond_ifname, member_iface.interface_name) self.cli['sync'].append(cli); - if bondmac and 'lcp' in config_iface: + if vpp_iface and 'mac' in config_iface and str(vpp_iface.l2_address) != config_iface['mac']: + cli="set interface mac address %s %s" % (config_ifname, config_iface['mac']) + self.cli['sync'].append(cli); + elif bondmac and 'lcp' in config_iface: ## TODO(pim) - Ensure LCP has the same MAC as the BondEthernet ## VPP, when creating a BondEthernet, will give it an ephemeral MAC. Then, when the ## first member is enslaved, the MAC address changes to that of the first member. diff --git a/vpp/vppapi.py b/vpp/vppapi.py index 5802b3f..c828a79 100644 --- a/vpp/vppapi.py +++ b/vpp/vppapi.py @@ -272,17 +272,19 @@ class VPPApiDumper(VPPApi): def cache_to_config(self): config = {"loopbacks": {}, "bondethernets": {}, "interfaces": {}, "bridgedomains": {}, "vxlan_tunnels": {} } - for idx, iface in self.cache['bondethernets'].items(): + for idx, bond_iface in self.cache['bondethernets'].items(): bond = {"description": ""} - if iface.sw_if_index in self.cache['bondethernet_members']: - members = [self.cache['interfaces'][x].interface_name for x in self.cache['bondethernet_members'][iface.sw_if_index]] + if bond_iface.sw_if_index in self.cache['bondethernet_members']: + members = [self.cache['interfaces'][x].interface_name for x in self.cache['bondethernet_members'][bond_iface.sw_if_index]] if len(members) > 0: bond['interfaces'] = members - mode = bondethernet.int_to_mode(iface.mode) + mode = bondethernet.int_to_mode(bond_iface.mode) bond['mode'] = mode if mode in ['xor', 'lacp']: - bond['load-balance'] = bondethernet.int_to_lb(iface.lb) + bond['load-balance'] = bondethernet.int_to_lb(bond_iface.lb) + iface = self.cache['interfaces'][bond_iface.sw_if_index] + bond['mac'] = str(iface.l2_address) config['bondethernets'][iface.interface_name] = bond for numtags in [ 0, 1, 2 ]: @@ -296,6 +298,7 @@ class VPPApiDumper(VPPApi): continue loop = {"description": ""} loop['mtu'] = iface.mtu[0] + loop['mac'] = str(iface.l2_address) if iface.sw_if_index in self.cache['lcps']: loop['lcp'] = self.cache['lcps'][iface.sw_if_index].host_if_name if iface.sw_if_index in self.cache['interface_addresses']: @@ -315,6 +318,8 @@ class VPPApiDumper(VPPApi): if not self.cache['interfaces'][idx].flags & 1: # IF_STATUS_API_FLAG_ADMIN_UP i['state'] = 'down' + if iface.interface_dev_type == 'dpdk': + i['mac'] = str(iface.l2_address) i['mtu'] = iface.mtu[0] if iface.sub_number_of_tags == 0: config['interfaces'][iface.interface_name] = i