From 0a3f6377c3e21519e5a17a8b2bc4ade72fa3e6db Mon Sep 17 00:00:00 2001 From: Erik Reid <erik.reid@geant.org> Date: Mon, 1 Mar 2021 17:53:37 +0100 Subject: [PATCH] refactoring bgp neighbor parsing ... wip --- inventory_provider/juniper.py | 88 +++++++++------------------- test/per_router/test_juniper_data.py | 24 +++++++- 2 files changed, 52 insertions(+), 60 deletions(-) diff --git a/inventory_provider/juniper.py b/inventory_provider/juniper.py index 189b2df0..ddd2fce0 100644 --- a/inventory_provider/juniper.py +++ b/inventory_provider/juniper.py @@ -112,55 +112,20 @@ UNIT_SCHEMA = """<?xml version="1.1" encoding="UTF-8" ?> PEERING_LIST_SCHEMA = { "$schema": "http://json-schema.org/draft-07/schema#", "definitions": { - "top-level-peering": { - "type": "object", - "properties": { - "group": {"type": "string"}, - "description": {"type": "string"}, - "address": {"type": "string"}, - "remote-asn": {"type": "integer"}, - "local-asn": {"type": "integer"} - }, - # lots of internal peerings - so maybe no explicit asn's - "required": ["group", "address"], - "additionalProperties": False - }, - "instance-peering": { + "peering": { "type": "object", "properties": { - "instance": {"type": "string"}, "group": {"type": "string"}, "description": {"type": "string"}, "address": {"type": "string"}, "remote-asn": {"type": "integer"}, "local-asn": {"type": "integer"}, - }, - # description and-or local-asn is not always present, - # just based on empirical tests - not a problem - "required": ["instance", "group", "address", "remote-asn"], - "additionalProperties": False - }, - "logical-system-peering": { - "type": "object", - "properties": { + "instance": {"type": "string"}, "logical-system": {"type": "string"}, - "group": {"type": "string"}, - "description": {"type": "string"}, - "address": {"type": "string"}, - "remote-asn": {"type": "integer"}, - "local-asn": {"type": "integer"} }, - # local/remote-asn and/or description are not always present, - # just based on empirical tests - not a problem - "required": ["logical-system", "group", "address"], + # lots of internal peerings - so maybe no explicit asn's + "required": ["group", "address"], "additionalProperties": False - }, - "peering": { - "oneOf": [ - {"$ref": "#/definitions/top-level-peering"}, - {"$ref": "#/definitions/instance-peering"}, - {"$ref": "#/definitions/logical-system-peering"} - ] } }, "type": "array", @@ -287,7 +252,7 @@ def list_interfaces(netconf_config): yield u -def all_bgp_peers(netconf_config): +def _system_bgp_peers(system_node): def _peering_params(neighbor_node): address = neighbor_node.find('name').text @@ -306,34 +271,39 @@ def all_bgp_peers(netconf_config): info['description'] = description.text return info - for group in netconf_config.xpath('//configuration/protocols/bgp/group'): + def _neighbors(group_node): + for neighbor in group_node.xpath('./neighbor'): + inactive = neighbor.get('inactive') + if inactive == 'inactive': + continue + yield _peering_params(neighbor) + + for group in system_node.xpath('./protocols/bgp/group'): group_name = group.find('name').text - for neighbor in group.xpath('./neighbor'): - peer = _peering_params(neighbor) + for peer in _neighbors(group): peer['group'] = group_name yield peer - for instance in netconf_config.xpath( - '//configuration/routing-instances/instance'): + for instance in system_node.xpath( + './routing-instances/instance'): instance_name = instance.find('name').text - for group in instance.xpath('./protocols/bgp/group'): - group_name = group.find('name').text - for neighbor in group.xpath('./neighbor'): - peer = _peering_params(neighbor) - peer['instance'] = instance_name - peer['group'] = group_name - yield peer + for peer in _system_bgp_peers(instance): + peer['instance'] = instance_name + yield peer + + +def all_bgp_peers(netconf_config): + + for base_system in netconf_config.xpath('//configuration'): + # there should only be one + yield from _system_bgp_peers(base_system) for logical_system in netconf_config.xpath( '//configuration/logical-systems'): logical_system_name = logical_system.find('name').text - for group in logical_system.xpath('./protocols/bgp/group'): - group_name = group.find('name').text - for neighbor in group.xpath('./neighbor'): - peer = _peering_params(neighbor) - peer['logical-system'] = logical_system_name - peer['group'] = group_name - yield peer + for peer in _system_bgp_peers(logical_system): + peer['logical-system'] = logical_system_name + yield peer def interface_addresses(netconf_config): diff --git a/test/per_router/test_juniper_data.py b/test/per_router/test_juniper_data.py index 81418755..9f88dc06 100644 --- a/test/per_router/test_juniper_data.py +++ b/test/per_router/test_juniper_data.py @@ -35,9 +35,22 @@ def test_interface_list(netconf_doc): jsonschema.validate(interfaces, schema) assert interfaces # at least shouldn't be empty - +from lxml import etree def test_bgp_peering_data(netconf_doc): + active_peers = set() + inactive_peers = set() + for neighbor in netconf_doc.xpath('//group/neighbor'): + address = neighbor.find('name').text + address = ipaddress.ip_address(address).exploded + inactive = neighbor.get('inactive') + if inactive == 'inactive': + inactive_peers.add(address) + else: + if address == '62.40.99.50': + print('here') + active_peers.add(address) + peerings = list(juniper.all_bgp_peers(netconf_doc)) jsonschema.validate(peerings, juniper.PEERING_LIST_SCHEMA) assert peerings # there's always at least one @@ -47,6 +60,15 @@ def test_bgp_peering_data(netconf_doc): canonical_address = ipaddress.ip_address(p['address']).exploded assert p['address'] == canonical_address + # confirm we got all of the active peerings + returned_addresses = {p['address'] for p in peerings} + missing = active_peers - returned_addresses + if missing: + print('here') + assert returned_addresses == active_peers + + # confirm that none of the inactive peerings were returned + assert not inactive_peers & returned_addresses def test_snmp_community_string(mocked_netifaces, netconf_doc): assert juniper.snmp_community_string(netconf_doc) == '0pBiFbD' -- GitLab