Skip to content
Snippets Groups Projects
Commit 5c67a34f authored by Robert Latta's avatar Robert Latta
Browse files

added bgp peer extraction and test. RE DBOARD3-958

parent c5a2f91c
No related branches found
No related tags found
No related merge requests found
from lxml import etree
import ipaddress
import logging
import re
from lxml import etree
from ncclient import manager, xml_
from inventory_provider.tasks.common import asn_to_int
logger = logging.getLogger(__name__)
BREAKOUT_PATTERN = re.compile(
......@@ -88,6 +91,11 @@ def remove_xml_namespaces(etree_doc):
return etree_doc
def _get_admin_state_from_element(element):
admin_state_element = element.find('admin-state')
return admin_state_element.text if admin_state_element is not None else 'enable'
def load_docs(hostname, ssh_params):
"""
Load the running and state docs for the given hostname
......@@ -168,10 +176,9 @@ def get_interfaces_state(state_doc):
def get_ports_config(netconf_config):
def _port_info(e):
pi = {
'port-id': e.find('port-id').text
'port-id': e.find('port-id').text,
'admin-state': _get_admin_state_from_element(e)
}
admin_state = e.find('admin-state')
pi['admin-state'] = admin_state.text if admin_state is not None else 'enabled' # assume enabled if not present
description = e.find('description')
pi['description'] = description.text if description is not None else ''
......@@ -211,14 +218,11 @@ def get_lags_config(netconf_config):
port_elements = e.findall('./port')
port_ids = (p.find('./port-id').text for p in port_elements)
admin_state = e.find('./admin-state')
description_e = e.find('description')
ifc = {
'name': _name,
'description': (
description_e.text if description_e is not None else ''
),
'admin-state': admin_state.text if admin_state is not None else 'enabled', # assume enabled if not present
'description': description_e.text if description_e is not None else '',
'admin-state': _get_admin_state_from_element(e),
'ports': [p_id for p_id in port_ids if p_id in enabled_ports],
}
return ifc
......@@ -235,14 +239,11 @@ def get_interfaces_config(netconf_config):
"interface-name": interface.find('interface-name').text,
"ipv4": [],
"ipv6": [],
'admin-state': _get_admin_state_from_element(interface)
}
description = interface.find('description')
details["description"] = description.text if description is not None else ""
admin_state = interface.find('admin-state')
if admin_state is not None:
details["admin-state"] = admin_state.text
for element in interface.xpath('ipv4/primary | ipv4/secondary'):
address = element.find('address').text
prefix_length = element.find('prefix-length').text
......@@ -254,3 +255,59 @@ def get_interfaces_config(netconf_config):
details["ipv6"].append(f'{address}/{prefix_length}')
yield details
def _get_neighbors_by_group(neighbor_elements):
neighbors_by_group = {}
for neighbor in neighbor_elements:
# admin_state = _get_admin_state_from_element(neighbor) # do we want to do anything with this?
group_name = neighbor.find('group').text
address = neighbor.find('ip-address').text
info = {
'address': ipaddress.ip_address(address).exploded
}
peer_as = neighbor.find('peer-as')
if peer_as is not None:
info['remote-asn'] = asn_to_int(peer_as.text)
description = neighbor.find('description')
if description is not None:
info['description'] = description.text
neighbors_by_group.setdefault(group_name, []).append(info)
return neighbors_by_group
def get_peer_info(neighbors_by_group, group_elements):
for bgp_group in group_elements:
# admin_state = _get_admin_state_from_element(bgp_group) # do we want to do anything with this?
group_name = bgp_group.find('group-name').text
details = {
'group': group_name
}
local_as = bgp_group.find('local-as')
if local_as is not None:
asn_value_node = local_as.find('as-number')
details['local-asn'] = asn_to_int(asn_value_node.text)
for neighbor in neighbors_by_group.get(group_name, []):
neighbor.update(details)
yield neighbor
def get_router_peers(netconf_config):
neighbors_by_group = _get_neighbors_by_group(netconf_config.xpath('configure/router/bgp/neighbor'))
group_elements = netconf_config.xpath('configure/router/bgp/group')
yield from get_peer_info(neighbors_by_group, group_elements)
def get_all_vprn_peers(netconf_config):
for vprn in netconf_config.xpath('configure/service/vprn'):
service_name = vprn.find('service-name').text
neighbors_by_group = _get_neighbors_by_group(vprn.xpath('bgp/neighbor'))
group_elements = vprn.xpath('bgp/group')
for peer in get_peer_info(neighbors_by_group, group_elements):
peer['instance'] = service_name # just to match the data from Juniper
yield peer
def get_all_bgp_peers(netconf_config):
yield from get_router_peers(netconf_config)
yield from get_all_vprn_peers(netconf_config)
import ipaddress
import os
import pathlib
from copy import deepcopy
from functools import lru_cache
import pytest
......@@ -23,6 +25,21 @@ PORT_SCHEMA = {
'additionalProperties': True
}
PEERS_SCHEMA = {
'$schema': 'https://json-schema.org/draft/2020-12/schema',
'type': 'object',
'properties': {
'address': {'type': 'string'},
'remote-asn': {'type': 'integer'},
'local-asn': {'type': 'integer'},
'description': {'type': 'string'},
'group': {'type': 'string'},
'instance': {'type': 'string'}
},
'required': ['address', 'group'],
'additionalProperties': False
}
@lru_cache
def _load_xml_doc(filename):
......@@ -107,7 +124,7 @@ def test_get_lags(hostname, expected_bundles):
)),
])
def test_interface_info(hostname, all_expected_data):
netconf_doc = nokia.remove_xml_namespaces(_load_netconf_config(hostname=hostname))
netconf_doc = _load_netconf_config(hostname=hostname)
interfaces_by_id = {ifc['interface-name']: ifc for ifc in
nokia.get_interfaces_config(netconf_doc)}
assert len(interfaces_by_id) == len(all_expected_data)
......@@ -127,7 +144,7 @@ def test_interface_info(hostname, all_expected_data):
('rt0.lon2.uk.geant.net', 139),
])
def test_get_ports(hostname, port_count):
netconf_doc = nokia.remove_xml_namespaces(_load_netconf_config(hostname=hostname))
netconf_doc = _load_netconf_config(hostname=hostname)
ports = list(nokia.get_ports_config(netconf_doc))
assert len(ports) == port_count
......@@ -186,3 +203,29 @@ def test_snmp_index():
"management": 1280,
}
assert {ifc["interface-name"]: ifc["if-index"] for ifc in interfaces} == expected
@pytest.mark.parametrize('hostname', [
'rt0.ams.nl.lab.office.geant.net',
'rt0.ams.nl.geant.net',
'rt0.fra.de.geant.net',
'rt0.gen.ch.geant.net',
'rt0.lon.uk.geant.net',
'rt0.lon2.uk.geant.net'
])
def test_get_peers(hostname):
netconf_doc = _load_netconf_config(hostname)
all_peers_from_doc = set()
all_peers_from_call = set()
for neighbor_element in netconf_doc.xpath('//bgp/neighbor'):
address = neighbor_element.find('ip-address').text
all_peers_from_doc.add(ipaddress.ip_address(address).exploded)
for peer in nokia.get_all_vprn_peers(netconf_doc):
jsonschema.validate(peer, PEERS_SCHEMA)
all_peers_from_call.add(peer['address'])
router_peers_schema = deepcopy(PEERS_SCHEMA)
del router_peers_schema['properties']['instance']
for peer in nokia.get_router_peers(netconf_doc):
jsonschema.validate(peer, PEERS_SCHEMA)
all_peers_from_call.add(peer['address'])
assert all_peers_from_doc == all_peers_from_call
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment