Skip to content
Snippets Groups Projects
Commit 96c89102 authored by Erik Reid's avatar Erik Reid
Browse files

Finished feature DBOARD3-406-bgp-groups-api.

parents e184a1c5 b960eaa0
No related branches found
Tags 0.36
No related merge requests found
......@@ -14,7 +14,33 @@ These endpoints are intended for use by MSR.
.. autofunction:: inventory_provider.routes.msr.access_services
/msr/logical-system-peerings</name>
/msr/bgp/logical-systems
-------------------------------------
.. autofunction:: inventory_provider.routes.msr.logical_system_peerings
\ No newline at end of file
.. autofunction:: inventory_provider.routes.msr.get_logical_systems
/msr/bgp/logical-system-peerings</name>
------------------------------------------
.. autofunction:: inventory_provider.routes.msr.logical_system_peerings
/msr/bgp/groups
-------------------------------------
.. autofunction:: inventory_provider.routes.msr.get_peering_groups
/msr/bgp/group-peerings</name>
-------------------------------------
.. autofunction:: inventory_provider.routes.msr.bgp_group_peerings
helpers
-------------------------------------
.. autofunction:: inventory_provider.routes.msr._handle_peering_group_list_request
.. autofunction:: inventory_provider.routes.msr._handle_peering_group_request
......@@ -38,8 +38,14 @@ ACCESS_SERVICES_LIST_SCHEMA = {
"items": {"$ref": "#/definitions/service"}
}
PEERING_GROUP_LIST_SCHEMA = {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "array",
"items": {"type": "string"}
}
LOGICAL_SYSTEM_PEERING_LIST_SCHEMA = {
PEERING_LIST_SCHEMA = {
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"peering-instance": {
......@@ -50,13 +56,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 +123,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, or 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():
def _get_all_subkeys():
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 +157,6 @@ 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,9 +166,9 @@ 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()))
gen_list = list(map(_load_list_items, _get_all_subkeys()))
items = itertools.chain(*gen_list)
items = list(items)
......@@ -171,3 +181,117 @@ 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`
""" # noqa: E501
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`
""" # noqa: E501
return _handle_peering_group_request(
name=name,
cache_key='classifier-cache:msr:group-peerings',
group_key_base='juniper-peerings:group')
def _handle_peering_group_list_request(cache_key, group_key_base):
"""
Common method for used by
:meth:`inventory_provider.routes.msr.get_logical_systems` and
:meth:`inventory_provider.routes.msr.get_peering_groups`.
This method will return a list of all immediate subkeys of
`group_key_base`.
The response will be formatted according to the following schema:
.. asjson::
inventory_provider.routes.msr.PEERING_GROUP_LIST_SCHEMA
: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_subkeys():
for k in r.scan_iter(f'{group_key_base}:*', count=1000):
k = k.decode('utf-8')
yield k[len(group_key_base) + 1:]
names = r.get(cache_key)
if names:
names = json.loads(names.decode('utf-8'))
else:
names = list(_get_all_subkeys())
if not names:
return Response(
response='no groups found',
status=404,
mimetype="text/html")
names = sorted(names)
r.set(cache_key, json.dumps(names).encode('utf-8'))
return jsonify(names)
@routes.route("/bgp/logical-systems", methods=['GET', 'POST'])
@common.require_accepts_json
def get_logical_systems():
"""
Handler for `/msr/bgp/logical-systems`
Returns a list of logical system names for which peering
information is available.
:return: see :meth:`inventory_provider.routes.msr._handle_peering_group_list_request`
""" # noqa: E501
return _handle_peering_group_list_request(
cache_key='classifier-cache:msr:logical-systems',
group_key_base='juniper-peerings:logical-system')
@routes.route("/bgp/groups", methods=['GET', 'POST'])
@common.require_accepts_json
def get_peering_groups():
"""
Handler for `/msr/bgp/groups`
Returns a list of group names for which peering
information is available.
:return: see :meth:`inventory_provider.routes.msr._handle_peering_group_list_request`
""" # noqa: E501
return _handle_peering_group_list_request(
cache_key='classifier-cache:msr:peering-groups',
group_key_base='juniper-peerings:group')
......@@ -3,8 +3,8 @@ import jsonschema
import pytest
from inventory_provider.routes.msr \
import ACCESS_SERVICES_LIST_SCHEMA, LOGICAL_SYSTEM_PEERING_LIST_SCHEMA
from inventory_provider.routes.msr import ACCESS_SERVICES_LIST_SCHEMA, \
PEERING_LIST_SCHEMA, PEERING_GROUP_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,53 @@ 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(
'/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_group_peerings_404(client, name):
rv = client.get(
f'/msr/bgp/logical-system-peerings/{name}',
headers=DEFAULT_REQUEST_HEADERS)
assert rv.status_code == 404
@pytest.mark.parametrize('uri', [
'/msr/bgp/logical-systems',
'/msr/bgp/groups'])
def test_peerings_group_list(client, uri):
rv = client.get(uri, 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_GROUP_LIST_SCHEMA)
assert response_data # test data is non-empty
......@@ -9,7 +9,7 @@ import jsonschema
from inventory_provider.tasks import worker
from inventory_provider.tasks import common
from inventory_provider.routes.msr import LOGICAL_SYSTEM_PEERING_LIST_SCHEMA
from inventory_provider.routes import msr
def backend_db():
......@@ -141,7 +141,7 @@ def test_build_juniper_peering_db(mocked_worker_module):
"additionalProperties": False
}
PEERING_LIST_SCHEMA = {
DETAILED_PEERING_LIST_SCHEMA = {
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"top-level-peering": TOP_LEVEL_PEERING_SCHEMA,
......@@ -194,15 +194,16 @@ def test_build_juniper_peering_db(mocked_worker_module):
assert address == canonical
continue
jsonschema.validate(value, PEERING_LIST_SCHEMA)
jsonschema.validate(value, DETAILED_PEERING_LIST_SCHEMA)
if 'logical-system:' in key:
jsonschema.validate(value, LOGICAL_SYSTEM_PEERING_LIST_SCHEMA)
jsonschema.validate(value, msr.PEERING_LIST_SCHEMA)
m = re.match(r'.*logical-system:(.+)$', key)
assert all(p['logical-system'] == m.group(1) for p in value)
found_logical_system = True
if 'group:' in key:
jsonschema.validate(value, msr.PEERING_LIST_SCHEMA)
m = re.match(r'.*group:(.+)$', key)
assert all(p['group'] == m.group(1) for p in value)
found_group = True
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment