diff --git a/inventory_provider/routes/classifier.py b/inventory_provider/routes/classifier.py index 20ac55849468dbf70f23808916800e9defe6d571..bdb9e961c9a3e96b3db37f08e917e9a3c368fdfc 100644 --- a/inventory_provider/routes/classifier.py +++ b/inventory_provider/routes/classifier.py @@ -717,11 +717,10 @@ def get_fiberlink_trap_metadata(ne_name_str: str, object_name_str: str) \ :return: """ - objects = object_name_str.split('_') - shelves = [x.split('-')[0] for x in objects] + interfaces = object_name_str.split('_') p = r'([a-zA-Z\d]+?-(OLA|DTNX)\d+(-\d)?)' matches = re.findall(p, ne_name_str) - if len(matches) != 2 or len(shelves) != 2: + if len(matches) != 2 or len(interfaces) != 2: raise ClassifierProcessingError( f'unable to parse {ne_name_str} {object_name_str} ' 'into two elements') @@ -747,64 +746,83 @@ def get_fiberlink_trap_metadata(ne_name_str: str, object_name_str: str) \ else: equipment_a = matches[0][0] equipment_b = matches[1][0] - nes_a = f'{equipment_a}-{shelves[0]}' - nes_b = f'{equipment_b}-{shelves[1]}' - result = [] - df_a = r.get(f'ims:ne_fibre_spans:{nes_a}') - df_b = r.get(f'ims:ne_fibre_spans:{nes_b}') - - if df_a and df_b: - a = json.loads(df_a.decode('utf-8')) - b = json.loads(df_b.decode('utf-8')) - - matches = [x for x in itertools.product(a, b) if - x[0]['df_route_id'] == x[1]['df_route_id']] - if matches: - match = matches[0] - location_a = _location_from_equipment(equipment_a, r) - location_b = _location_from_equipment(equipment_b, r) - if location_a: - loc_a = location_a - else: - loc_a = _LOCATION(equipment_a, '', '') - if location_b: - loc_b = location_b - else: - loc_b = _LOCATION(equipment_b, '', '') - - # added locations in preparation for refactoring to be in-line - # with other location data. Once Dashboard has been altered to - # use this for fiberlink alarms the 'ends' attribute can be - # removed - result = { - 'locations': [ - build_locations(loc_a, loc_b) - ], - 'ends': { - 'a': { - 'pop': loc_a['name'], - 'pop_abbreviation': loc_a['abbreviation'], - 'equipment': loc_a['equipment'] - }, - 'b': { - 'pop': loc_b['name'], - 'pop_abbreviation': loc_b['abbreviation'], - 'equipment': loc_b['equipment'] - }, + interface_a = interfaces[0] + interface_b = interfaces[1] + circuits_a = \ + r.get(f'ims:interface_services:{equipment_a}:{interface_a}') + logger.debug(f'ims:interface_services:{equipment_a}:{interface_a}') + circuits_b = \ + r.get(f'ims:interface_services:{equipment_b}:{interface_b}') + logger.debug(f'ims:interface_services:{equipment_b}:{interface_b}') + + def _get_fr(circs): + for c in circs: + h = r.get(f'ims:circuit_hierarchy:{c["id"]}') + if h: + h = json.loads(h.decode('utf-8')) + for sc in h: + yield from sc['fibre-routes'] + + fr_a_ids = set() + fr_b_ids = set() + all_frs = {} + if circuits_a: + circuits_a = json.loads(circuits_a.decode('utf-8')) + for fr in _get_fr(circuits_a): + fr_a_ids.add(fr['id']) + all_frs[fr['id']] = fr + if circuits_b: + circuits_b = json.loads(circuits_b.decode('utf-8')) + for fr in _get_fr(circuits_b): + fr_b_ids.add(fr['id']) + all_frs[fr['id']] = fr + + fr_ids = fr_a_ids & fr_b_ids + if not fr_ids: + fr_ids = fr_a_ids | fr_b_ids + fibre_routes = [all_frs[x] for x in fr_ids] + + if fibre_routes: + location_a = _location_from_equipment(equipment_a, r) + location_b = _location_from_equipment(equipment_b, r) + if location_a: + loc_a = location_a + else: + loc_a = _LOCATION(equipment_a, '', '') + if location_b: + loc_b = location_b + else: + loc_b = _LOCATION(equipment_b, '', '') + + # added locations in preparation for refactoring to be in-line + # with other location data. Once Dashboard has been altered to + # use this for fiberlink alarms the 'ends' attribute can be + # removed + result = { + 'locations': [ + build_locations(loc_a, loc_b) + ], + 'ends': { + 'a': { + 'pop': loc_a['name'], + 'pop_abbreviation': loc_a['abbreviation'], + 'equipment': loc_a['equipment'] }, - 'df_route': { - 'id': match[0]['df_route_id'], - 'name': match[0]['df_route'], - 'status': match[0]['df_status'], + 'b': { + 'pop': loc_b['name'], + 'pop_abbreviation': loc_b['abbreviation'], + 'equipment': loc_b['equipment'] }, - 'related-services': - get_top_level_services(match[0]['df_route_id'], r) - } - for rs in result['related-services']: - rs.pop('id', None) - - result = json.dumps(result) - r.set(cache_key, result) + }, + 'df_route': fibre_routes[0], + 'related-services': + get_top_level_services(fibre_routes[0]['id'], r) + } + for rs in result['related-services']: + rs.pop('id', None) + + result = json.dumps(result) + r.set(cache_key, result) if not result: return Response( response="no available info for " diff --git a/inventory_provider/tasks/worker.py b/inventory_provider/tasks/worker.py index eb2a6c2e5c6c1a8a0bb0de817491c1d9df241006..3724ef449c62c2887fe21c0ca648edafcf07fb52 100644 --- a/inventory_provider/tasks/worker.py +++ b/inventory_provider/tasks/worker.py @@ -8,6 +8,7 @@ import tempfile import time from datetime import datetime from enum import IntFlag +from functools import lru_cache from pathlib import Path from uuid import uuid4 @@ -422,7 +423,6 @@ def launch_refresh_cache_all(config): update_neteng_managed_device_list.apply_async(), update_equipment_locations.apply_async(), update_lg_routers.apply_async(), - update_fibre_spans.apply_async(), ] [x.get() for x in subtasks] @@ -471,30 +471,6 @@ def internal_refresh_phase_2(self): raise -@app.task(base=InventoryTask, bind=True, name='update_fibre_spans') -@log_task_entry_and_exit -def update_fibre_spans(self, use_current=False): - if use_current: - r = get_current_redis(InventoryTask.config) - else: - r = get_next_redis(InventoryTask.config) - rp = r.pipeline() - # scan with bigger batches, to mitigate network latency effects - for key in r.scan_iter('ims:ne_fibre_spans:*', count=1000): - rp.delete(key) - rp.execute() - - c = InventoryTask.config["ims"] - ds = IMS(c['api'], c['username'], c['password']) - - rp = r.pipeline() - for ne, fs in ims_data.get_fibre_info(ds): - rp.set( - f'ims:ne_fibre_spans:{ne}', - json.dumps(fs)) - rp.execute() - - @app.task( base=InventoryTask, bind=True, name='update_interfaces_to_services') @log_task_entry_and_exit @@ -546,6 +522,8 @@ def update_interfaces_to_services(self, use_current=False): s.pop('port_a_id', None) s.pop('port_b_id', None) + interface_services = defaultdict(list) + for port_info in port_id_details.values(): service_ids = set() services = [] @@ -555,16 +533,20 @@ def update_interfaces_to_services(self, use_current=False): services.append(service) service_ids.add(service['id']) + if_key = f'{port_info["equipment_name"]}:{port_info["interface_name"]}' + interface_services[if_key].extend(services) + + for k, v in interface_services.items(): rp.set( - f'ims:interface_services:{port_info["equipment_name"]}:' - f'{port_info["interface_name"]}', - json.dumps(services)) + f'ims:interface_services:{k}', + json.dumps(v)) rp.execute() @app.task(base=InventoryTask, bind=True, name='update_circuit_hierarchy') @log_task_entry_and_exit def update_circuit_hierarchy(self, use_current=False): + hierarchy = {} if use_current: r = get_current_redis(InventoryTask.config) else: @@ -577,9 +559,28 @@ def update_circuit_hierarchy(self, use_current=False): c = InventoryTask.config["ims"] ds = IMS(c['api'], c['username'], c['password']) + hierarchy = {d['id']: d for d in ims_data.get_circuit_hierarchy(ds)} + + def _get_fibre_routes(c_id): + _circ = hierarchy.get(c_id, None) + if _circ is None: + return + if _circ['speed'].lower() == 'fibre_route': + yield _circ['id'] + else: + for cc in _circ['carrier-circuits']: + yield from _get_fibre_routes(cc) + rp = r.pipeline() - for d in ims_data.get_circuit_hierarchy(ds): - rp.set(f'ims:circuit_hierarchy:{d["id"]}', json.dumps([d])) + for circ in hierarchy.values(): + circ['fibre-routes'] = \ + [{ + 'id': hierarchy[x]['id'], + 'name': hierarchy[x]['name'], + 'status': hierarchy[x]['status'] + } for x in set(_get_fibre_routes(circ['id']))] + rp.set(f'ims:circuit_hierarchy:{circ["id"]}', json.dumps([circ])) + rp.execute() diff --git a/test/data/router-info.json b/test/data/router-info.json index 3178260f5e965e5247b4a8b741d6383526009ad7..312689ccb3dc13297f8820d69f6499c9987f5084 100644 Binary files a/test/data/router-info.json and b/test/data/router-info.json differ diff --git a/test/test_classifier_routes.py b/test/test_classifier_routes.py index 8cd01b94734b3dd6e05c7ad1f9d8293a347e8b58..dc205b916ac9fe6a758c793f5459096cac1e157f 100644 --- a/test/test_classifier_routes.py +++ b/test/test_classifier_routes.py @@ -4,7 +4,8 @@ import pytest from inventory_provider.routes.classifier_schema \ import JUNIPER_LINK_RESPONSE_SCHEMA, PEER_INFO_RESPONSE_SCHEMA, \ - INFINERA_LAMBDA_INFO_RESPONSE_SCHEMA, CORIANT_INFO_RESPONSE_SCHEMA + INFINERA_LAMBDA_INFO_RESPONSE_SCHEMA, CORIANT_INFO_RESPONSE_SCHEMA, \ + INFINERA_FIBERLINK_INFO_RESPONSE_SCHEMA DEFAULT_REQUEST_HEADERS = { "Content-type": "application/json", @@ -165,17 +166,17 @@ def test_infinera_fiberlink_not_found(client): # Temporarily removed for merge -# def test_infinera_fiberlink(client): -# -# rv = client.get( -# '/classifier/infinera-fiberlink-info/' -# 'OOS-OLA1-GHE-OLA1/1-A-3-L1_1-A-2-L1', -# 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, INFINERA_FIBERLINK_INFO_RESPONSE_SCHEMA) +def test_infinera_fiberlink(client): + + rv = client.get( + '/classifier/infinera-fiberlink-info/' + 'OOS-OLA1-GHE-OLA1/1-A-3-L1_1-A-2-L1', + 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, INFINERA_FIBERLINK_INFO_RESPONSE_SCHEMA) def test_infinera_lambda(client):