From 5af59cf3fb481e25226f3ee85e8d2a1080646b6c Mon Sep 17 00:00:00 2001
From: Sam Roberts <sam.roberts@geant.org>
Date: Wed, 4 Sep 2024 10:37:06 +0000
Subject: [PATCH] Feature/pol1 430 eap nrens

---
 inventory_provider/db/ims_data.py   |  12 +++++
 inventory_provider/routes/poller.py |  72 +++++++++++++++++++++++++++-
 inventory_provider/tasks/worker.py  |  12 ++++-
 test/data/router-info.json          | Bin 137282171 -> 137282566 bytes
 test/test_general_poller_routes.py  |   9 ++++
 test/test_worker.py                 |  17 +++++++
 6 files changed, 118 insertions(+), 4 deletions(-)

diff --git a/inventory_provider/db/ims_data.py b/inventory_provider/db/ims_data.py
index 43f65cc3..fca38c90 100644
--- a/inventory_provider/db/ims_data.py
+++ b/inventory_provider/db/ims_data.py
@@ -647,3 +647,15 @@ def get_router_vendors(ds: IMS):
             ed_nav_properties):
         for r in ed.get('nodes', []):
             yield r['name'], ed.get('vendor', {}).get('name', None)
+
+
+@log_entry_and_exit
+def get_customer_regions(ds: IMS):
+    customer_id_map = {_c['id']: _c['name'] for _c in ds.get_all_entities('Customer', step_count=500)}
+    for _d in ds.get_filtered_entities(
+            'ExtraFieldValue',
+            'extrafield.id == 3329',  # 3329: region field, will not change
+            EXTRA_FIELD_VALUE_PROPERTIES['ExtraFieldValueObjectInfo']):
+        _obj_id = _d['objectid']
+        if _obj_id in customer_id_map:
+            yield {'id': _obj_id, 'name': customer_id_map[_obj_id], 'region': _d['value']}
diff --git a/inventory_provider/routes/poller.py b/inventory_provider/routes/poller.py
index 230d6036..b6293ec7 100644
--- a/inventory_provider/routes/poller.py
+++ b/inventory_provider/routes/poller.py
@@ -60,6 +60,12 @@ These endpoints are intended for use by BRIAN.
 .. autofunction:: inventory_provider.routes.poller.error_report_interfaces
 
 
+/poller/regions
+---------------
+
+.. autofunction:: inventory_provider.routes.poller.get_nren_regions
+
+
 support method: _get_dashboards
 ---------------------------------
 
@@ -132,6 +138,9 @@ class BRIAN_DASHBOARDS(Enum):
     # NREN customer
     NREN = auto()
 
+    # EAP NRENs
+    EAP = auto()
+
 
 class PORT_TYPES(Enum):
     ACCESS = auto()
@@ -438,6 +447,22 @@ STRING_LIST_SCHEMA = {
     'items': {'type': 'string'}
 }
 
+NREN_REGION_LIST_SCHEMA = {
+    '$schema': 'https://json-schema.org/draft-07/schema#',
+    'definitions': {
+        'nren_region': {
+            'type': 'object',
+            'properties': {
+                'name': {'type': 'string'},
+                'region': {'type': 'string'}
+            }
+        }
+    },
+
+    'type': 'array',
+    'items': {'$ref': '#/definitions/nren_region'}
+}
+
 
 @routes.after_request
 def after_request(resp):
@@ -520,7 +545,10 @@ def _get_dashboards(interface):
         yield BRIAN_DASHBOARDS.ANA
 
 
-def _get_dashboard_data(ifc, customers):
+def _get_dashboard_data(ifc, customers, regions=None):
+    if regions is None:
+        regions = []
+
     def _get_interface_type(description):
         if re.match(r'^PHY', description):
             return INTERFACE_TYPES.PHYSICAL
@@ -577,6 +605,15 @@ def _get_dashboard_data(ifc, customers):
     if not names:
         return ifc
 
+    # add region-based dashboard names
+    region_names = []
+    for name in names:
+        if name in regions:
+            region = regions[name]
+            if region == 'EAP':
+                region_names.append(BRIAN_DASHBOARDS.EAP.name)
+    names.extend(region_names)
+
     # to maintain compatability with current brian dashboard manager we will
     # continue to return dashboard_info with the first customer name. We will
     # also return dashboards_info (note the plural of dashboards) with up to
@@ -809,6 +846,20 @@ def _get_port_type(description):
     return PORT_TYPES.UNKNOWN.name
 
 
+def _load_nren_regions(config, use_next_redis=False):
+    result = defaultdict(dict)
+
+    key_pattern = 'ims:cache:customer_regions'
+    if use_next_redis:
+        r = tasks_common.get_next_redis(config)
+    else:
+        r = tasks_common.get_current_redis(config)
+    for id, nren in json.loads(r.get(key_pattern).decode('utf-8')).items():
+        result[nren['name']] = nren['region']
+
+    return result
+
+
 def load_interfaces_to_poll(config, hostname=None, no_lab=False, use_next_redis=False):
     def is_relevant(ifc):
         return not re.match(r"^(lt-|so-|dsc\.|fxp\d|lo\d).*", ifc["name"])
@@ -820,6 +871,7 @@ def load_interfaces_to_poll(config, hostname=None, no_lab=False, use_next_redis=
     services_and_customers = \
         _get_services_and_customers(config, hostname, use_next_redis)
     snmp_indexes = common.load_snmp_indexes(config, hostname, use_next_redis)
+    nren_regions = _load_nren_regions(config, use_next_redis)
 
     def _get_populated_interfaces(all_interfaces):
         if use_next_redis:
@@ -851,7 +903,7 @@ def load_interfaces_to_poll(config, hostname=None, no_lab=False, use_next_redis=
                 ifc['dashboards'] = sorted([d.name for d in dashboards])
 
                 ifc = _get_dashboard_data(
-                    ifc, ifc_services_and_customers.get('customers', []))
+                    ifc, ifc_services_and_customers.get('customers', []), nren_regions)
                 port_type = _get_port_type(ifc['description'])
                 ifc['port_type'] = port_type
                 yield ifc
@@ -1703,3 +1755,19 @@ def gws_direct_config():
         response=page,
         status=200,
         mimetype="text/html")
+
+
+@routes.route('/regions', methods=['GET'])
+@common.require_accepts_json
+def get_nren_regions():
+    """
+    Handler for fetching regions of NRENs, as cached by the Inventory Provider update process.
+    :return:
+    """
+    config_params = current_app.config['INVENTORY_PROVIDER_CONFIG']
+    nren_region_dict = _load_nren_regions(config_params)
+    nren_regions = [{
+        'nren': nren,
+        'region': region
+    } for nren, region in nren_region_dict.items()]
+    return jsonify(nren_regions)
diff --git a/inventory_provider/tasks/worker.py b/inventory_provider/tasks/worker.py
index 3f96af19..8ddfed32 100644
--- a/inventory_provider/tasks/worker.py
+++ b/inventory_provider/tasks/worker.py
@@ -1099,6 +1099,7 @@ def _extract_ims_data(ims_api_url, ims_username, ims_password, verify_ssl):
     additional_circuit_customers = {}
     flexils_data = {}
     customers = {}
+    customer_regions = {}
 
     hierarchy = {}
     port_id_details = defaultdict(list)
@@ -1191,6 +1192,11 @@ def _extract_ims_data(ims_api_url, ims_username, ims_password, verify_ssl):
         nonlocal customers
         customers = {c['id']: c for c in _ds().get_all_entities('customer')}
 
+    @log_task_entry_and_exit
+    def _populate_customer_regions():
+        nonlocal customer_regions
+        customer_regions = {c['id']: c for c in ims_data.get_customer_regions(ds=_ds())}
+
     @log_task_entry_and_exit
     def _populate_flexils_data():
         nonlocal flexils_data
@@ -1223,7 +1229,8 @@ def _extract_ims_data(ims_api_url, ims_username, ims_password, verify_ssl):
             executor.submit(_populate_port_id_details): 'port_id_details',
             executor.submit(_populate_circuit_info): 'circuit_info',
             executor.submit(_populate_flexils_data): 'flexils_data',
-            executor.submit(_populate_customers): 'customers'
+            executor.submit(_populate_customers): 'customers',
+            executor.submit(_populate_customer_regions): 'customer_regions'
         }
 
         for future in concurrent.futures.as_completed(futures):
@@ -1248,7 +1255,8 @@ def _extract_ims_data(ims_api_url, ims_username, ims_password, verify_ssl):
         'port_id_services': port_id_services,
         'geant_nodes': geant_nodes,
         'flexils_data': flexils_data,
-        'customers': customers
+        'customers': customers,
+        'customer_regions': customer_regions
     }
 
 
diff --git a/test/data/router-info.json b/test/data/router-info.json
index 5352029a65d23d054d1e445be633de72732417e2..71f098ab2cfa331b4b89ec4accc60640faac0a75 100644
GIT binary patch
delta 10142
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%0DmVt=nAYuiGSP3Flfr!;0VhxB`3nJEm
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
R+8{!QWB<C-x*TV}0RTuRH<SPX

delta 9963
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

diff --git a/test/test_general_poller_routes.py b/test/test_general_poller_routes.py
index 6c7b7e0d..1b9a6741 100644
--- a/test/test_general_poller_routes.py
+++ b/test/test_general_poller_routes.py
@@ -527,3 +527,12 @@ def test_get_single_router_error_report_interfaces(client):
     response_data = json.loads(rv.data)
     jsonschema.validate(response_data, poller.ERROR_REPORT_INTERFACE_LIST_SCHEMA)
     assert {ifc['router'] for ifc in response_data} == {"mx1.ams.nl.geant.net"}
+
+
+def test_get_nren_regions(client):
+    rv = client.get("/poller/regions", headers=DEFAULT_REQUEST_HEADERS)
+    assert rv.status_code == 200
+    assert rv.is_json
+    response_data = json.loads(rv.data)
+    jsonschema.validate(response_data, poller.NREN_REGION_LIST_SCHEMA)
+    assert {nren_region['region'] for nren_region in response_data}  # no values should be empty strings
diff --git a/test/test_worker.py b/test/test_worker.py
index 8dbd8767..9118ee1e 100644
--- a/test/test_worker.py
+++ b/test/test_worker.py
@@ -122,6 +122,16 @@ def test_extract_ims_data(mocker):
             }
         ]
     )
+    mocker.patch(
+        'inventory_provider.tasks.worker.ims_data.get_customer_regions',
+        return_value=[
+            {
+                'id': 1,
+                'name': 'Cust 1',
+                'region': 'REGION'
+            }
+        ]
+    )
     res = extract_ims_data()
     assert res['locations'] == {'loc_a': 'LOC A', 'loc_b': 'LOC B'}
     assert res['site_locations'] == {
@@ -129,6 +139,7 @@ def test_extract_ims_data(mocker):
                     'name': 'JEN-SPL'}}
     assert res['lg_routers'] == ['lg router 1', 'lg router 2']
     assert res['customer_contacts'] == {'123': 'CON A', '456': 'CON B'}
+    assert res['customer_regions'] == {1: {'id': 1, 'name': 'Cust 1', 'region': 'REGION'}}
     assert res['planned_work_contacts'] == \
         {'223': 'CON PW A', '556': 'CON PW B'}
     assert res['circuit_ids_to_monitor'] == [123, 456, 789]
@@ -820,6 +831,9 @@ def test_populate_poller_interfaces_cache(
             "port_type": "SERVICE"
         }
     ]
+    nren_regions = {
+        'NREN A': 'REGION A'
+    }
 
     for k in r.keys("lab:netconf-interfaces-hosts:*"):
         r.delete(k)
@@ -835,6 +849,9 @@ def test_populate_poller_interfaces_cache(
     mocker.patch(
         'inventory_provider.routes.poller._get_services_and_customers',
         return_value=services_and_customers)
+    mocker.patch(
+        'inventory_provider.routes.poller._load_nren_regions',
+        return_value=nren_regions)
     mocker.patch(
         'inventory_provider.tasks.worker.InventoryTask.config'
     )
-- 
GitLab