Skip to content
Snippets Groups Projects
Commit fc73b25d authored by Sam Roberts's avatar Sam Roberts
Browse files

Merge branch 'feature/REPORTING-307-msr-vpn-proxy-endpoint' into 'develop'

Feature/reporting 307 msr vpn proxy endpoint

See merge request !2
parents bfddc0f3 4b880f6c
No related branches found
No related tags found
1 merge request!2Feature/reporting 307 msr vpn proxy endpoint
...@@ -64,12 +64,19 @@ These endpoints are intended for use by MSR. ...@@ -64,12 +64,19 @@ These endpoints are intended for use by MSR.
.. autofunction:: inventory_provider.routes.msr.mdvpn .. autofunction:: inventory_provider.routes.msr.mdvpn
/msr/services /msr/services
-------------------------------------------- --------------------------------------------
.. autofunction:: inventory_provider.routes.msr.get_system_correlation_services .. autofunction:: inventory_provider.routes.msr.get_system_correlation_services
/msr/vpn-proxy
--------------------------------------------
.. autofunction:: inventory_provider.routes.msr.vpn-proxy
helpers helpers
------------------------------------- -------------------------------------
...@@ -321,6 +328,34 @@ MDVPN_LIST_SCHEMA = { ...@@ -321,6 +328,34 @@ MDVPN_LIST_SCHEMA = {
'items': {'$ref': '#/definitions/mdvpn_group'} 'items': {'$ref': '#/definitions/mdvpn_group'}
} }
VPN_PROXY_LIST_SCHEMA = {
'$schema': 'http://json-schema.org/draft-07/schema#',
'definitions': {
'vpn_proxy_peering': {
'type': 'object',
'properties': {
'pop': {'type': 'string'},
'nren': {'type': 'string'},
'group': {'type': 'string'},
'v4': {'type': 'string'}
},
'additionalProperties': False
}
},
'type': 'array',
'items': {'$ref': '#/definitions/vpn_proxy_peering'}
}
DOMAIN_TO_POP_MAPPING = {
"mad.es": "Madrid",
"bra.sk": "Bratislava",
"vie.at": "Vienna",
"gen.ch": "Geneva",
"fra.de": "Frankfurt",
"pra.cz": "Prague",
"ams.nl": "Amsterdam"
}
@routes.after_request @routes.after_request
def after_request(resp): def after_request(resp):
...@@ -1010,22 +1045,19 @@ def mdvpn(): ...@@ -1010,22 +1045,19 @@ def mdvpn():
yield formatted_peering yield formatted_peering
def _vpnrr_peerings(asn, vpnrr_index): def _vpnrr_peerings(asn, vpnrr_index):
if asn in vpnrr_index: # rearrange into index using ipv4 as key
vrr_peering_group = vpnrr_index[asn] # this will collect related entries under the same ipv4
# rearrange into index using ipv4 as key ip_index = _make_group_index(vpnrr_index.get(asn, []), 'address')
# this will collect related entries under the same ipv4 for ip, ip_details in ip_index.items():
ip_index = _make_group_index(vrr_peering_group, 'address') hostnames = [item['hostname'] for item in ip_details]
for ip in ip_index: description = ip_details[0]['description']
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 = { formatted_peering = {
"description": _get_consistent_description(description), "description": _get_consistent_description(description),
"v4": ip, "v4": ip,
"hostname": hostnames "hostname": hostnames
} }
yield formatted_peering yield formatted_peering
def _peerings_for_nren(asn, bgplu_index, vpnrr_index): def _peerings_for_nren(asn, bgplu_index, vpnrr_index):
return { return {
...@@ -1053,3 +1085,91 @@ def mdvpn(): ...@@ -1053,3 +1085,91 @@ def mdvpn():
for pair in nren_asn_map] for pair in nren_asn_map]
response = json.dumps(nren_details) response = json.dumps(nren_details)
return Response(response, mimetype='application/json') return Response(response, mimetype='application/json')
@routes.route('/vpn-proxy', methods=['GET', 'POST'])
@common.require_accepts_json
def vpn_proxy():
"""
Handler for `/vpn-proxy`
This method returns a list of all L3VPN related VPN proxy peerings.
The response will be formatted according to the following schema:
.. asjson::
inventory_provider.routes.msr.VPN_PROXY_LIST_SCHEMA
:return:
"""
def _is_relevant(item):
"""
Determine if a given peering in the VPN-PROXY logical system is
relevant to this endpoint (whether it's related to L3VPN)
:param item: peering dict
:return: True if the peering is L3VPN relevant, False otherwise
"""
desc = item.get("description")
if desc is None:
return False
return "L3VPN" in desc
def _look_up_city_from_hostname(hostname):
"""
Get the city name for a peering from a partial hostname match.
This uses a hardcoded lookup table.
:param hostname: hostname for the peering
:return: city name if found, "Unknown" otherwise
"""
for snippet in DOMAIN_TO_POP_MAPPING:
if snippet in hostname:
return DOMAIN_TO_POP_MAPPING[snippet]
return "Unknown"
def _extract_nren_from_description(desc, group):
"""
Retrieve the relevant NREN from the peering description and group.
This approach is, by its nature, very fragile to any changes to
descriptions, and should be revisited when that happens.
:param desc: description of a VPN-Proxy peering
:param group: group of the same VPN-Proxy peering
:return: name of the NREN
"""
if group == "PRACE":
# common trait: the NREN is the first word in the description
return desc.split(' ')[0]
else:
# only other group is XiFi, and only CESNet is relevant
return 'CESNet'
def _format_peerings(vpnproxy):
"""
Generator that iterates through a list of peering dicts, yielding
appropriately reformatted peerings if they are relevant to L3VPN.
:param vpnproxy: list of peering dicts taken from current redis
:return: generator of reformated peerings
"""
for peering in vpnproxy:
if _is_relevant(peering):
desc = peering["description"]
group = peering["group"]
hostname = peering["hostname"]
formatted_peering = {
"pop": _look_up_city_from_hostname(hostname),
"nren": _extract_nren_from_description(desc, group),
"group": group,
"v4": peering.get("address")
}
yield formatted_peering
r = common.get_current_redis()
cache_key = 'classifier-cache:msr:vpn-proxy'
response = _ignore_cache_or_retrieve(request, cache_key, r)
if not response:
vpnproxy = json.loads(
r.get('juniper-peerings:logical-system:VPN-PROXY').decode('utf-8'))
peerings = list(_format_peerings(vpnproxy))
response = json.dumps(peerings)
return Response(response, mimetype='application/json')
...@@ -6,7 +6,7 @@ import pytest ...@@ -6,7 +6,7 @@ import pytest
from inventory_provider.routes.msr import PEERING_LIST_SCHEMA, \ from inventory_provider.routes.msr import PEERING_LIST_SCHEMA, \
PEERING_GROUP_LIST_SCHEMA, PEERING_ADDRESS_SERVICES_LIST, \ PEERING_GROUP_LIST_SCHEMA, PEERING_ADDRESS_SERVICES_LIST, \
SYSTEM_CORRELATION_SERVICES_LIST_SCHEMA, _get_services_for_address, \ SYSTEM_CORRELATION_SERVICES_LIST_SCHEMA, _get_services_for_address, \
MDVPN_LIST_SCHEMA MDVPN_LIST_SCHEMA, VPN_PROXY_LIST_SCHEMA
from inventory_provider.routes.poller import SERVICES_LIST_SCHEMA from inventory_provider.routes.poller import SERVICES_LIST_SCHEMA
from inventory_provider.tasks.common import _get_redis from inventory_provider.tasks.common import _get_redis
...@@ -334,3 +334,15 @@ def test_get_mdvpn_peerings(client, mocked_redis): ...@@ -334,3 +334,15 @@ def test_get_mdvpn_peerings(client, mocked_redis):
response_data = json.loads(rv.data.decode('utf-8')) response_data = json.loads(rv.data.decode('utf-8'))
jsonschema.validate(response_data, MDVPN_LIST_SCHEMA) jsonschema.validate(response_data, MDVPN_LIST_SCHEMA)
assert response_data # test data is non-empty assert response_data # test data is non-empty
def test_get_vpn_proxy_peerings(client, mocked_redis):
rv = client.get(
'/msr/vpn-proxy',
headers=DEFAULT_REQUEST_HEADERS
)
assert rv.status_code == 200
assert rv.is_json
response_data = json.loads(rv.data.decode('utf-8'))
jsonschema.validate(response_data, VPN_PROXY_LIST_SCHEMA)
assert response_data # test data is non-empty
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment