From a48b7cd5d1943223e388f5968b1baff7dd843c80 Mon Sep 17 00:00:00 2001
From: Erik Reid <erik.reid@geant.org>
Date: Tue, 16 Feb 2021 12:46:16 +0100
Subject: [PATCH] added group peering list api

---
 inventory_provider/routes/msr.py | 75 +++++++++++++++++++++++++-------
 test/test_msr_routes.py          | 50 ++++++++++++++++++---
 2 files changed, 103 insertions(+), 22 deletions(-)

diff --git a/inventory_provider/routes/msr.py b/inventory_provider/routes/msr.py
index 58693b68..1f31d862 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 a5469527..a30015f6 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
+
+
+
-- 
GitLab