Skip to content
Snippets Groups Projects

Feature/reporting 297 msr mdvpn endpoint

Merged Sam Roberts requested to merge feature/REPORTING-297-msr-mdvpn-endpoint into develop
2 unresolved threads
2 files
+ 0
8
Compare changes
  • Side-by-side
  • Inline
Files
2
@@ -59,6 +59,11 @@ These endpoints are intended for use by MSR.
.. autofunction:: inventory_provider.routes.msr.bgp_all_peerings
/msr/mdpvn
--------------------------------------------
.. autofunction:: inventory_provider.routes.msr.mdvpn
/msr/services
--------------------------------------------
@@ -256,6 +261,66 @@ SYSTEM_CORRELATION_SERVICES_LIST_SCHEMA = {
'minItems': 1 # otherwise the route should return 404
}
MDVPN_LIST_SCHEMA = {
'$schema': 'http://json-schema.org/draft-07/schema#',
'definitions': {
'mdvpn_group': {
'type': 'object',
'properties': {
'asn': {'type': 'integer'},
'AP': {'$ref': '#/definitions/ap_peerings'},
'VRR': {'$ref': '#/definitions/vrr_peerings'}
},
'required': [
'asn', 'AP', 'VRR'
],
'additionalProperties': False
},
'ap_peerings': {
'type': 'array',
'items': {
'$ref': '#/definitions/bgplu_peering'
}
},
'bgplu_peering': {
'type': 'object',
'properties': {
'name': {'type': 'string'},
'v4': {'type': 'string'},
'v6': {'type': 'string'},
'hostname': {'type': 'string'}
},
'required': [
'name', 'v4', 'v6', 'hostname'
],
'additionalProperties': False
},
'vrr_peerings': {
'type': 'array',
'items': {
'$ref': '#/definitions/vpn_peering'
}
},
'vpn_peering': {
'type': 'object',
'properties': {
'description': {'type': 'string'},
'v4': {'type': 'string'},
'hostname': {
'type': 'array',
'items': {
'type': 'string'
},
'minItems': 1
}
},
'additionalProperties': False
}
},
'type': 'array',
'items': {'$ref': '#/definitions/mdvpn_group'}
}
@routes.after_request
def after_request(resp):
@@ -875,3 +940,116 @@ def bgp_all_peerings():
r = common.get_current_redis()
response = r.get('juniper-peerings:all')
return Response(response.decode('utf-8'), mimetype="application/json")
@routes.route('/mdvpn', methods=['GET', 'POST'])
@common.require_accepts_json
def mdvpn():
"""
Handler for `/mdvpn`
This method returns a list of all BGP-LU peerings, and the VR peerings
for both Paris & Ljubljana.
The response will be formatted according to the following schema:
.. asjson::
inventory_provider.routes.msr.MDVPN_LIST_SCHEMA
:return:
"""
def _get_consistent_description(description):
"""
The same interface in VRR peerings can have multiple names.
These names are (currently) the same but with a different local prefix,
with no ordering guaranteed by the redis cache.
As only one description is returned by this endpoint for each
IPv4 address, this serves as a quick and dirty way of merging these
multiple descriptions into one an external user can use to identify
the peering reliably.
:param description: The raw description for a VRR peering
:return: The same description with location prefix removed
"""
# it is incredibly likely this will need revision later down the line
expected_prefixes = [
"MD-VPN-VRR-PARIS-",
"MD-VPN-VRR-LJUBLJANA-"
]
for prefix in expected_prefixes:
if description.startswith(prefix):
return description.replace(prefix, '')
return description
def _make_group_index(group, index_key):
"""
Utility function to take a list and make it a dict based off a given
key, for fast lookup of a specific key field.
:param group: A list of dicts which should all have `index_key` as a
field
:param index_key: Name of the key to index on
:return: Dict with `index_key` as the key field and a list of all
matching dicts as the value
"""
index = {}
for peering in group:
key = peering.get(index_key)
index.setdefault(key, []).append(peering)
return index
def _bgplu_peerings(asn, bgplu_index):
for peering in bgplu_index.get(asn, []):
formatted_peering = {
"name": peering['description'],
"v4": peering['address'],
"v6": '',
"hostname": peering['hostname']
}
yield formatted_peering
def _vpnrr_peerings(asn, vpnrr_index):
if asn in vpnrr_index:
vrr_peering_group = vpnrr_index[asn]
# rearrange into index using ipv4 as key
# this will collect related entries under the same ipv4
ip_index = _make_group_index(vrr_peering_group, 'address')
for ip in ip_index:
ip_details = ip_index[ip] # a list of all info for given ipv4
hostnames = [item['hostname'] for item in ip_details]
description = ip_details[0]['description']
formatted_peering = {
"description": _get_consistent_description(description),
"v4": ip,
"hostname": hostnames
}
yield formatted_peering
def _peerings_for_nren(asn, bgplu_index, vpnrr_index):
return {
"asn": asn,
"AP": list(_bgplu_peerings(asn, bgplu_index)),
"VRR": list(_vpnrr_peerings(asn, vpnrr_index))
}
r = common.get_current_redis()
cache_key = 'classifier-cache:msr:mdvpn'
response = _ignore_cache_or_retrieve(request, cache_key, r)
if not response:
bgplu = json.loads(
r.get('juniper-peerings:group:BGPLU').decode('utf-8'))
vpnrr = json.loads(
r.get('juniper-peerings:group:VPN-RR').decode('utf-8'))
bgplu_index = _make_group_index(bgplu, 'remote-asn')
vpnrr_index = _make_group_index(vpnrr, 'remote-asn')
config = current_app.config['INVENTORY_PROVIDER_CONFIG']
nren_asn_map = config['nren-asn-map']
nren_details = [
_peerings_for_nren(pair['asn'],
bgplu_index,
vpnrr_index)
for pair in nren_asn_map]
response = json.dumps(nren_details)
return Response(response, mimetype='application/json')
Loading