From da7765569f515070ba0a65585d16f8dcb500ebed Mon Sep 17 00:00:00 2001 From: Pim van Pelt Date: Sun, 10 Apr 2022 14:47:37 +0000 Subject: [PATCH] Refactor VPPApi VPPApiDumper() is its own file, preparing for VPPApiApplier() in an upcoming commit. VPPApi() itself remains read-only. No need for an empty __init__.py file. Update vppcfg to use the correct vpp/dumper.py import --- vpp/__init__.py | 24 -------- vpp/dumper.py | 143 ++++++++++++++++++++++++++++++++++++++++++++++++ vpp/vppapi.py | 141 +---------------------------------------------- vppcfg | 2 +- 4 files changed, 146 insertions(+), 164 deletions(-) delete mode 100644 vpp/__init__.py create mode 100644 vpp/dumper.py diff --git a/vpp/__init__.py b/vpp/__init__.py deleted file mode 100644 index 3b20c4b..0000000 --- a/vpp/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2022 Pim van Pelt -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# http://www.apache.org/licenses/LICENSE-2.0 -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# -*- coding: utf-8 -*- -from __future__ import ( - absolute_import, - division, - print_function, -) - -import logging -from vpp.vppapi import VPPApi -from vpp.reconciler import Reconciler diff --git a/vpp/dumper.py b/vpp/dumper.py new file mode 100644 index 0000000..97ff6e2 --- /dev/null +++ b/vpp/dumper.py @@ -0,0 +1,143 @@ +''' +The functions in this file interact with the VPP API to retrieve certain +interface metadata and write it to a YAML file. +''' + +from vpp.vppapi import VPPApi +import sys +import yaml +import config.bondethernet as bondethernet + +class VPPApiDumper(VPPApi): + def __init__(self, address='/run/vpp/api.sock', clientname='vppcfg'): + VPPApi.__init__(self, address, clientname) + + def write(self, outfile): + if outfile and outfile == '-': + fh = sys.stdout + outfile = "(stdout)" + else: + fh = open(outfile, 'w') + + config = self.cache_to_config() + + print(yaml.dump(config), file=fh) + + if fh is not sys.stdout: + fh.close() + self.logger.info("Wrote YAML config to %s" % (outfile)) + + def cache_to_config(self): + config = {"loopbacks": {}, "bondethernets": {}, "interfaces": {}, "bridgedomains": {}, "vxlan_tunnels": {} } + for idx, bond_iface in self.cache['bondethernets'].items(): + bond = {"description": ""} + 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(bond_iface.mode) + bond['mode'] = mode + if mode in ['xor', 'lacp']: + 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 ]: + for idx, iface in self.cache['interfaces'].items(): + if iface.sub_number_of_tags != numtags: + continue + + if iface.interface_dev_type=='Loopback': + if iface.sub_id > 0: + self.logger.warning("Refusing to export sub-interfaces of loopback devices (%s)" % iface.interface_name) + 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']: + if len(self.cache['interface_addresses'][iface.sw_if_index]) > 0: + loop['addresses'] = self.cache['interface_addresses'][iface.sw_if_index] + config['loopbacks'][iface.interface_name] = loop + elif iface.interface_dev_type in ['bond', 'VXLAN', 'dpdk']: + i = {"description": "" } + if iface.sw_if_index in self.cache['lcps']: + i['lcp'] = self.cache['lcps'][iface.sw_if_index].host_if_name + if iface.sw_if_index in self.cache['interface_addresses']: + if len(self.cache['interface_addresses'][iface.sw_if_index]) > 0: + i['addresses'] = self.cache['interface_addresses'][iface.sw_if_index] + if iface.sw_if_index in self.cache['l2xcs']: + l2xc = self.cache['l2xcs'][iface.sw_if_index] + i['l2xc'] = self.cache['interfaces'][l2xc.tx_sw_if_index].interface_name + 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 + continue + + encap = {} + if iface.sub_if_flags&8: + encap['dot1ad'] = iface.sub_outer_vlan_id + else: + encap['dot1q'] = iface.sub_outer_vlan_id + if iface.sub_inner_vlan_id > 0: + encap['inner-dot1q'] = iface.sub_inner_vlan_id + encap['exact-match'] = bool(iface.sub_if_flags&16) + i['encapsulation'] = encap + + sup_iface = self.cache['interfaces'][iface.sup_sw_if_index] + if iface.mtu[0] > 0: + i['mtu'] = iface.mtu[0] + else: + i['mtu'] = sup_iface.mtu[0] + if not 'sub-interfaces' in config['interfaces'][sup_iface.interface_name]: + config['interfaces'][sup_iface.interface_name]['sub-interfaces'] = {} + config['interfaces'][sup_iface.interface_name]['sub-interfaces'][iface.sub_id] = i + + for idx, iface in self.cache['vxlan_tunnels'].items(): + vpp_iface = self.cache['interfaces'][iface.sw_if_index] + vxlan = { "description": "", + "vni": int(iface.vni), + "local": str(iface.src_address), + "remote": str(iface.dst_address) } + config['vxlan_tunnels'][vpp_iface.interface_name] = vxlan + + for idx, iface in self.cache['bridgedomains'].items(): + # self.logger.info("%d: %s" % (idx, iface)) + 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] + mtu = bvi.mtu[0] + bridge['bvi'] = bvi.interface_name + members = [] + for member in iface.sw_if_details: + if bvi and bvi.interface_name == self.cache['interfaces'][member.sw_if_index].interface_name == bvi.interface_name: + continue + members.append(self.cache['interfaces'][member.sw_if_index].interface_name) + mtu = self.cache['interfaces'][member.sw_if_index].mtu[0] + if len(members) > 0: + bridge['interfaces'] = members + bridge['mtu'] = mtu + config['bridgedomains'][bridge_name] = bridge + + return config diff --git a/vpp/vppapi.py b/vpp/vppapi.py index c828a79..4402691 100644 --- a/vpp/vppapi.py +++ b/vpp/vppapi.py @@ -1,16 +1,13 @@ ''' The functions in this file interact with the VPP API to retrieve certain -interface metadata. +interface metadata. Its base class will never change state. See the +derived classes VPPApiDumper() and VPPApiApplier() ''' from vpp_papi import VPPApiClient import os -import sys 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'): @@ -250,137 +247,3 @@ class VPPApi(): if lcp.phy_sw_if_index == sw_if_index: return lcp return None - -class VPPApiDumper(VPPApi): - def __init__(self, address='/run/vpp/api.sock', clientname='vppcfg'): - VPPApi.__init__(self, address, clientname) - - def write(self, outfile): - if outfile and outfile == '-': - fh = sys.stdout - outfile = "(stdout)" - else: - fh = open(outfile, 'w') - - config = self.cache_to_config() - - print(yaml.dump(config), file=fh) - - if fh is not sys.stdout: - fh.close() - self.logger.info("Wrote YAML config to %s" % (outfile)) - - def cache_to_config(self): - config = {"loopbacks": {}, "bondethernets": {}, "interfaces": {}, "bridgedomains": {}, "vxlan_tunnels": {} } - for idx, bond_iface in self.cache['bondethernets'].items(): - bond = {"description": ""} - 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(bond_iface.mode) - bond['mode'] = mode - if mode in ['xor', 'lacp']: - 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 ]: - for idx, iface in self.cache['interfaces'].items(): - if iface.sub_number_of_tags != numtags: - continue - - if iface.interface_dev_type=='Loopback': - if iface.sub_id > 0: - self.logger.warning("Refusing to export sub-interfaces of loopback devices (%s)" % iface.interface_name) - 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']: - if len(self.cache['interface_addresses'][iface.sw_if_index]) > 0: - loop['addresses'] = self.cache['interface_addresses'][iface.sw_if_index] - config['loopbacks'][iface.interface_name] = loop - elif iface.interface_dev_type in ['bond', 'VXLAN', 'dpdk']: - i = {"description": "" } - if iface.sw_if_index in self.cache['lcps']: - i['lcp'] = self.cache['lcps'][iface.sw_if_index].host_if_name - if iface.sw_if_index in self.cache['interface_addresses']: - if len(self.cache['interface_addresses'][iface.sw_if_index]) > 0: - i['addresses'] = self.cache['interface_addresses'][iface.sw_if_index] - if iface.sw_if_index in self.cache['l2xcs']: - l2xc = self.cache['l2xcs'][iface.sw_if_index] - i['l2xc'] = self.cache['interfaces'][l2xc.tx_sw_if_index].interface_name - 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 - continue - - encap = {} - if iface.sub_if_flags&8: - encap['dot1ad'] = iface.sub_outer_vlan_id - else: - encap['dot1q'] = iface.sub_outer_vlan_id - if iface.sub_inner_vlan_id > 0: - encap['inner-dot1q'] = iface.sub_inner_vlan_id - encap['exact-match'] = bool(iface.sub_if_flags&16) - i['encapsulation'] = encap - - sup_iface = self.cache['interfaces'][iface.sup_sw_if_index] - if iface.mtu[0] > 0: - i['mtu'] = iface.mtu[0] - else: - i['mtu'] = sup_iface.mtu[0] - if not 'sub-interfaces' in config['interfaces'][sup_iface.interface_name]: - config['interfaces'][sup_iface.interface_name]['sub-interfaces'] = {} - config['interfaces'][sup_iface.interface_name]['sub-interfaces'][iface.sub_id] = i - - for idx, iface in self.cache['vxlan_tunnels'].items(): - vpp_iface = self.cache['interfaces'][iface.sw_if_index] - vxlan = { "description": "", - "vni": int(iface.vni), - "local": str(iface.src_address), - "remote": str(iface.dst_address) } - config['vxlan_tunnels'][vpp_iface.interface_name] = vxlan - - for idx, iface in self.cache['bridgedomains'].items(): - # self.logger.info("%d: %s" % (idx, iface)) - 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] - mtu = bvi.mtu[0] - bridge['bvi'] = bvi.interface_name - members = [] - for member in iface.sw_if_details: - if bvi and bvi.interface_name == self.cache['interfaces'][member.sw_if_index].interface_name == bvi.interface_name: - continue - members.append(self.cache['interfaces'][member.sw_if_index].interface_name) - mtu = self.cache['interfaces'][member.sw_if_index].mtu[0] - if len(members) > 0: - bridge['interfaces'] = members - bridge['mtu'] = mtu - config['bridgedomains'][bridge_name] = bridge - - return config diff --git a/vppcfg b/vppcfg index 6cd878c..b97f331 100755 --- a/vppcfg +++ b/vppcfg @@ -19,7 +19,7 @@ import yaml import logging from config import Validator from vpp.reconciler import Reconciler -from vpp.vppapi import VPPApiDumper +from vpp.dumper import VPPApiDumper try: import argparse