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

Merge branch 'feature/REPORTING-311-add-msr-asn-peers' into 'develop'

Feature/reporting 311 add msr asn peers

See merge request !3
parents 9fabc328 a5822127
Branches
Tags
1 merge request!3Feature/reporting 311 add msr asn peers
...@@ -74,7 +74,13 @@ These endpoints are intended for use by MSR. ...@@ -74,7 +74,13 @@ These endpoints are intended for use by MSR.
/msr/vpn-proxy /msr/vpn-proxy
-------------------------------------------- --------------------------------------------
.. autofunction:: inventory_provider.routes.msr.vpn-proxy .. autofunction:: inventory_provider.routes.msr.vpn_proxy
/msr/asn-peers
--------------------------------------------
.. autofunction:: inventory_provider.routes.msr.asn_peers
helpers helpers
...@@ -356,6 +362,40 @@ DOMAIN_TO_POP_MAPPING = { ...@@ -356,6 +362,40 @@ DOMAIN_TO_POP_MAPPING = {
"ams.nl": "Amsterdam" "ams.nl": "Amsterdam"
} }
# very similar to PEERING_LIST_SCHEMA but
# with a field for NREN, which is required
ASN_PEER_LIST_SCHEMA = {
'$schema': 'http://json-schema.org/draft-07/schema#',
'definitions': {
'peering-instance': {
'type': 'object',
'properties': {
'address': {'type': 'string'},
'description': {'type': 'string'},
'logical-system': {'type': 'string'},
'group': {'type': 'string'},
'hostname': {'type': 'string'},
'remote-asn': {'type': 'integer'},
'local-asn': {'type': 'integer'},
'instance': {'type': 'string'},
'nren': {'type': 'string'}
},
# only vrr peerings have remote-asn
# only group peerings have local-asn or instance
# not all group peerings have 'description'
# and only vrr or vpn-proxy peerings are within a logical system
'required': [
'address',
'group',
'hostname',
'nren'],
'additionalProperties': False
}
},
'type': 'array',
'items': {'$ref': '#/definitions/peering-instance'}
}
@routes.after_request @routes.after_request
def after_request(resp): def after_request(resp):
...@@ -1173,3 +1213,88 @@ def vpn_proxy(): ...@@ -1173,3 +1213,88 @@ def vpn_proxy():
peerings = list(_format_peerings(vpnproxy)) peerings = list(_format_peerings(vpnproxy))
response = json.dumps(peerings) response = json.dumps(peerings)
return Response(response, mimetype='application/json') return Response(response, mimetype='application/json')
@routes.route('/asn-peers', methods=['GET', 'POST'], defaults={'asn': None})
@routes.route('/asn-peers/<int:asn>', methods=['GET', 'POST'])
@common.require_accepts_json
def asn_peers(asn):
"""
Handler for `/asn-peers`
This method returns a list of all peers filtered by `group` and `instance`,
which can be passed either as URL query parameters or as entries in a
POST request with a JSON body that matches this schema:
`{
"group": "group to filter by",
"instance": "instance to filter by"
}`
Results are returned where all filters given are true, and exact string
matches.
An optional URL parameter can be used to also filter by a specific ASN.
The response will be formatted according to the following schema:
.. asjson::
inventory_provider.routes.msr.ASN_PEER_LIST_SCHEMA
:param asn: specific ASN to get peers for
:return:
"""
r = common.get_current_redis()
def _get_filtered_peers_for_asn(asn, nren, group, instance):
peers = json.loads(r.get(f'juniper-peerings:peer-asn:{asn}'))
def _attribute_filter(peer, name, value):
if value is None:
return True # no filter parameter given in request
if name not in peer:
return False # no value exists, cannot meet condition
return peer[name] == value
for peer in peers:
if _attribute_filter(peer, "group", group) and \
_attribute_filter(peer, "instance", instance):
peer['nren'] = nren
yield peer
def _get_filtered_peers(asn_nren_map, group, instance):
for asn, nren in asn_nren_map.items():
asn_peers = _get_filtered_peers_for_asn(asn, nren, group, instance)
for peer in asn_peers:
yield peer
# handle getting parameters regardless of method of input
if request.method == 'GET':
group = request.args.get('group')
instance = request.args.get('instance')
else:
params = json.loads(request.json)
group = params.get('group', None)
instance = params.get('instance', None)
cache_key = f'classifier-cache:msr:asn-peers:{asn}:{group}:{instance}'
response = _ignore_cache_or_retrieve(request, cache_key, r)
if not response:
config = current_app.config['INVENTORY_PROVIDER_CONFIG']
# set up quick lookup based on ASN
asn_nren_map = {
item['asn']: item['nren'] for item in config['nren-asn-map']
}
if asn is not None:
nren = asn_nren_map.get(asn, None)
peers = list(
_get_filtered_peers_for_asn(asn, nren, group, instance)
)
else:
peers = list(
_get_filtered_peers(asn_nren_map, group, instance)
)
response = json.dumps(peers)
r.set(cache_key, response.encode('utf-8'))
return Response(response, mimetype='application/json')
...@@ -72,6 +72,10 @@ def data_config_filename(): ...@@ -72,6 +72,10 @@ def data_config_filename():
{ {
"nren": "BAT", "nren": "BAT",
"asn": 2200 "asn": 2200
},
{
"nren": "BAZ",
"asn": 1853
} }
] ]
} }
......
...@@ -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, VPN_PROXY_LIST_SCHEMA MDVPN_LIST_SCHEMA, VPN_PROXY_LIST_SCHEMA, ASN_PEER_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
...@@ -346,3 +346,48 @@ def test_get_vpn_proxy_peerings(client, mocked_redis): ...@@ -346,3 +346,48 @@ def test_get_vpn_proxy_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, VPN_PROXY_LIST_SCHEMA) jsonschema.validate(response_data, VPN_PROXY_LIST_SCHEMA)
assert response_data # test data is non-empty assert response_data # test data is non-empty
@pytest.mark.parametrize('endpoint_variant', [
"", # default, no filter
"/1853",
"?group=IAS-NRENS",
"?instance=IAS",
"?group=IAS-NRENS&instance=IAS",
"/1853?group=IAS-NRENS",
"/1853?instance=IAS",
"/1853?group=IAS-NRENS&instance=IAS"
])
def test_get_asn_peers_get(endpoint_variant, client, mocked_redis):
rv = client.get(
f'/msr/asn-peers{endpoint_variant}',
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, ASN_PEER_LIST_SCHEMA)
assert response_data # test data is non-empty
@pytest.mark.parametrize('endpoint_variant,post_body', [
("", '{}'),
("", '{"group": "IAS-NRENS"}'),
("", '{"instance": "IAS"}'),
("", '{"group": "IAS-NRENS", "instance": "IAS"}'),
("/1853", '{}'),
("/1853", '{"group": "IAS-NRENS"}'),
("/1853", '{"instance": "IAS"}'),
("/1853", '{"group": "IAS-NRENS", "instance": "IAS"}')
])
def test_get_asn_peers_post(endpoint_variant, post_body, client, mocked_redis):
rv = client.post(
f'/msr/asn-peers{endpoint_variant}',
headers=DEFAULT_REQUEST_HEADERS,
json=post_body
)
assert rv.status_code == 200
assert rv.is_json
response_data = json.loads(rv.data.decode('utf-8'))
jsonschema.validate(response_data, ASN_PEER_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 to comment