diff --git a/inventory_provider/routes/msr.py b/inventory_provider/routes/msr.py index 58693b68a2ae1b80cffa33891ded878847ec1e78..1f31d86208fd648ccb1a4ad7d88ee96e9108e847 100644 --- a/inventory_provider/routes/msr.py +++ b/inventory_provider/routes/msr.py @@ -39,7 +39,7 @@ ACCESS_SERVICES_LIST_SCHEMA = { } -LOGICAL_SYSTEM_PEERING_LIST_SCHEMA = { +PEERING_LIST_SCHEMA = { "$schema": "http://json-schema.org/draft-07/schema#", "definitions": { "peering-instance": { @@ -50,13 +50,16 @@ LOGICAL_SYSTEM_PEERING_LIST_SCHEMA = { "logical-system": {"type": "string"}, "group": {"type": "string"}, "hostname": {"type": "string"}, - "remote-asn": {"type": "integer"} + "remote-asn": {"type": "integer"}, + "local-asn": {"type": "integer"}, + "instance": {"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", - "description", - "logical-system", "group", "hostname"], "additionalProperties": False @@ -114,30 +117,32 @@ def access_services(): return jsonify(result) -@routes.route("/logical-system-peerings", methods=['GET', 'POST']) -@routes.route("/logical-system-peerings/<name>", methods=['GET', 'POST']) -@common.require_accepts_json -def logical_system_peerings(name=None): +def _handle_peering_group_request(name, cache_key, group_key_base): """ - Handler for `/msr/logical-system-peerings` + Common method for used by + :meth:`inventory_provider.routes.msr.logical_system_peerings` and + :meth:`inventory_provider.routes.msr.bgp_group_peerings`. This method will return a list of all peerings configured - for the requested logical-system name on any router, or for any - logical system if no parameter is given. + for the specified group `name on any router, + or for all group names if `name` None. The response will be formatted according to the following schema: .. asjson:: - inventory_provider.routes.msr.LOGICAL_SYSTEM_PEERING_LIST_SCHEMA + inventory_provider.routes.msr.PEERING_LIST_SCHEMA - :return: + :param name: group/logical-system name, nor None + :param cache_key: base cache key for this type of request + :param group_key_base: key above which the peerings are grouped + :return: a json list, formatted as above """ r = common.get_current_redis() def _get_all_ls_keys(): keys = [] - for k in r.scan_iter('juniper-peerings:logical-system:*', count=1000): + for k in r.scan_iter(f'{group_key_base}:*', count=1000): keys.append(k.decode('utf-8')) return keys @@ -146,7 +151,7 @@ def logical_system_peerings(name=None): if value: yield from json.loads(value.decode('utf-8')) - cache_key = 'classifier-cache:msr:logical-system-peerings' + if name: cache_key = f'{cache_key}:{name}' @@ -156,7 +161,7 @@ def logical_system_peerings(name=None): items = json.loads(items.decode('utf-8')) else: if name: - items = _load_list_items(f'juniper-peerings:logical-system:{name}') + items = _load_list_items(f'{group_key_base}:{name}') else: gen_list = list(map(_load_list_items, _get_all_ls_keys())) items = itertools.chain(*gen_list) @@ -171,3 +176,41 @@ def logical_system_peerings(name=None): r.set(cache_key, json.dumps(items).encode('utf-8')) return jsonify(items) + + +@routes.route("/bgp/logical-system-peerings", methods=['GET', 'POST']) +@routes.route("/bgp/logical-system-peerings/<name>", methods=['GET', 'POST']) +@common.require_accepts_json +def logical_system_peerings(name=None): + """ + Handler for `/msr/bgp/logical-system-peerings` + + This method will return a list of all peerings configured + for the requested logical-system name on any router, or for any + logical system if no parameter is given. + + :return: see :meth:`inventory_provider.routes.msr._handle_peering_group_request` + """ + return _handle_peering_group_request( + name=name, + cache_key='classifier-cache:msr:logical-system-peerings', + group_key_base='juniper-peerings:logical-system') + + +@routes.route("/bgp/group-peerings", methods=['GET', 'POST']) +@routes.route("/bgp/group-peerings/<name>", methods=['GET', 'POST']) +@common.require_accepts_json +def bgp_group_peerings(name=None): + """ + Handler for `/msr/bgp/group-peerings` + + This method will return a list of all peerings configured + for the requested logical-system name on any router, or for any + logical system if no parameter is given. + + :return: see :meth:`inventory_provider.routes.msr._handle_peering_group_request` + """ + return _handle_peering_group_request( + name=name, + cache_key='classifier-cache:msr:group-peerings', + group_key_base='juniper-peerings:group') diff --git a/test/test_msr_routes.py b/test/test_msr_routes.py index a54695271cee86b1377ba8c8cd4aa57e2e33ac29..a30015f6853c58d2da6a09556061eec9791a5437 100644 --- a/test/test_msr_routes.py +++ b/test/test_msr_routes.py @@ -4,7 +4,7 @@ import jsonschema import pytest from inventory_provider.routes.msr \ - import ACCESS_SERVICES_LIST_SCHEMA, LOGICAL_SYSTEM_PEERING_LIST_SCHEMA + import ACCESS_SERVICES_LIST_SCHEMA, PEERING_LIST_SCHEMA DEFAULT_REQUEST_HEADERS = { "Content-type": "application/json", @@ -26,25 +26,26 @@ def test_access_services(client): def test_logical_system_peerings_all(client): rv = client.get( - '/msr/logical-system-peerings', + '/msr/bgp/logical-system-peerings', 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, LOGICAL_SYSTEM_PEERING_LIST_SCHEMA) + jsonschema.validate(response_data, PEERING_LIST_SCHEMA) assert response_data # test data is non-empty + assert all('logical-system' in p for p in response_data) @pytest.mark.parametrize('name', ['VRR', 'VPN-PROXY']) def test_logical_system_peerings_specific(client, name): rv = client.get( - f'/msr/logical-system-peerings/{name}', + f'/msr/bgp/logical-system-peerings/{name}', 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, LOGICAL_SYSTEM_PEERING_LIST_SCHEMA) + jsonschema.validate(response_data, PEERING_LIST_SCHEMA) assert response_data # test data is non-empty assert all(p['logical-system'] == name for p in response_data) @@ -59,6 +60,43 @@ def test_logical_system_peerings_specific(client, name): ]) def test_logical_system_peerings_404(client, name): rv = client.get( - f'/msr/logical-system-peerings/{name}', + f'/msr/bgp/logical-system-peerings/{name}', headers=DEFAULT_REQUEST_HEADERS) assert rv.status_code == 404 + + +def test_group_peerings_all(client): + rv = client.get( + f'/msr/bgp/group-peerings', + 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, PEERING_LIST_SCHEMA) + + assert response_data # test data is non-empty + + +@pytest.mark.parametrize('name', ['BGPLU', 'eGEANT', 'eGEANT-mcast']) +def test_group_peerings_specific(client, name): + rv = client.get( + f'/msr/bgp/group-peerings/{name}', + 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, PEERING_LIST_SCHEMA) + + assert response_data # test data is non-empty + assert all(p['group'] == name for p in response_data) + + +@pytest.mark.parametrize('name', ['EGEANT', 'eGEANT mcast']) +def test_logical_system_peerings_404(client, name): + rv = client.get( + f'/msr/bgp/logical-system-peerings/{name}', + headers=DEFAULT_REQUEST_HEADERS) + assert rv.status_code == 404 + + +