diff --git a/inventory_provider/routes/ims_classifier.py b/inventory_provider/routes/ims_classifier.py index 02bc9f3b1a4119cbac9c6e3fe48c35537447a5d0..61e1487a4273092393f15e5da5276677c419741e 100644 --- a/inventory_provider/routes/ims_classifier.py +++ b/inventory_provider/routes/ims_classifier.py @@ -1,4 +1,5 @@ import ipaddress +import itertools import json import logging import re @@ -12,9 +13,6 @@ routes = Blueprint("ims-inventory-data-classifier-support-routes", __name__) logger = logging.getLogger(__name__) - - - def _LOCATION(equipment, name, abbreviation): return { 'equipment': equipment, @@ -54,24 +52,30 @@ def _locations_from_router(router_name): }] -def _location_from_service_dict(s): - location = { - 'a': _LOCATION( - equipment=s['equipment'], - name=s['pop_name'], - abbreviation=s['pop_abbreviation']) - } - - if all(s[n] for n in ( - 'other_end_equipment', - 'other_end_pop_name', - 'other_end_pop_abbreviation')): - location['b'] = _LOCATION( - equipment=s['other_end_equipment'], - name=s['other_end_pop_name'], - abbreviation=s['other_end_pop_abbreviation']) - - return location +# once the switchover is done then will refactor to get rid of +# _locations_from_router +def _location_from_equipment(equipment_name): + return _locations_from_router(equipment_name) + + +# def _location_from_service_dict(s): +# location = { +# 'a': _LOCATION( +# equipment=s['equipment'], +# name=s['pop_name'], +# abbreviation=s['pop_abbreviation']) +# } +# +# if all(s[n] for n in ( +# 'other_end_equipment', +# 'other_end_pop_name', +# 'other_end_pop_abbreviation')): +# location['b'] = _LOCATION( +# equipment=s['other_end_equipment'], +# name=s['other_end_pop_name'], +# abbreviation=s['other_end_pop_abbreviation']) +# +# return location class ClassifierRequestError(Exception): @@ -115,23 +119,28 @@ def related_interfaces(hostname, interface): def get_top_level_services(circuit_id, r): - tls = [] - results = r.get("ims:services:children:{}".format(circuit_id)) + tls = {} + key = "ims:circuit_hierarchy:{}".format(circuit_id) + results = r.get(key) if results: results = json.loads(results.decode('utf-8')) + # should only ever be one, may refactor this for c in results: - temp_parents = \ - get_top_level_services(c['parent_circuit_id'], r) - if not temp_parents: - tls.append( - {'name': c['parent_circuit'], - 'status': c['parent_circuit_status'], - 'circuit_type': c['parent_circuit_type'].lower(), - 'project': c['parent_project'] - }) - tls.extend(temp_parents) - return tls + if c['sub-circuits']: + for sub in c['sub-circuits']: + temp_parents = \ + get_top_level_services(sub, r) + tls.update({t['id']: t for t in temp_parents}) + else: + tls[c['id']] = { + 'id': c['id'], + 'name': c['name'], + 'status': c['status'], + 'circuit_type': c['product'].lower(), + 'project': c['project'] + } + return list(tls.values()) def get_ims_equipment_name(equipment_name: str) -> str: @@ -176,8 +185,10 @@ def get_juniper_link_info(source_equipment: str, interface: str): result['services'] = json.loads(services.decode('utf=8')) for s in result['services']: top_level_services.extend(get_top_level_services(s['id'], r)) - result['locations'] += [ - _location_from_service_dict(s) for s in result['services']] + + result['locations'] += _location_from_equipment(s['equipment']) + result['locations'] += \ + _location_from_equipment(s['other_end_equipment']) ifc_info = r.get(f'netconf-interfaces:{source_equipment}:{interface}') if ifc_info: @@ -342,7 +353,7 @@ def peer_info(address): r = common.get_current_redis() - cache_key = f'classifier-cache:peer:{address}' + cache_key = f'ims-classifier-cache:peer:{address}' # result = r.get(cache_key) result = False @@ -360,21 +371,24 @@ def peer_info(address): info = info.decode('utf-8') info = json.loads(info) result['ix-public-peer-info'] = ix_peering_info(info) - result['locations'] += _locations_from_router(info['router']) + result['locations'] += _location_from_equipment(info['router']) info = r.get('vpn_rr_peer:%s' % address) if info: info = info.decode('utf-8') info = json.loads(info) result['vpn-rr-peer-info'] = info - result['locations'] += _locations_from_router(info['router']) + result['locations'] += _location_from_equipment(info['router']) interfaces = list(find_interfaces_and_services(address)) if interfaces: result['interfaces'] = interfaces for i in interfaces: - result['locations'] += [ - _location_from_service_dict(s) for s in i['services']] + for s in i['services']: + result['locations'] += \ + _location_from_equipment(s['equipment']) + result['locations'] += \ + _location_from_equipment(s['other_end_equipment']) result['locations'] = _remove_duplicates_from_list(result['locations']) result = json.dumps(result) @@ -382,3 +396,97 @@ def peer_info(address): r.set(cache_key, result.encode('utf-8')) return Response(result, mimetype="application/json") + + +@routes.route("/infinera-fiberlink-info/<ne_name_str>/<object_name_str>", + methods=['GET', 'POST']) +@common.require_accepts_json +def get_fiberlink_trap_metadata(ne_name_str, object_name_str): + objects = object_name_str.split('_') + shelves = [x.split('-')[0] for x in objects] + 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: + raise ClassifierProcessingError( + f'unable to parse {ne_name_str} {object_name_str } ' + 'into two elements') + + r = common.get_current_redis() + + # double check that we only need to check the two nodes and not the objects + cache_key = f'ims-classifier-cache:fiberlink:{ne_name_str}:{object_name_str}' + + # result = r.get(cache_key) + result = False + + if result: + result = result.decode('utf-8') + 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] + locations_a = _location_from_equipment(equipment_a) + locations_b = _location_from_equipment(equipment_b) + if locations_a: + loc_a = locations_a[0]['a'] + else: + loc_a = _LOCATION(equipment_a, '', '') + if locations_b: + loc_b = locations_b[0]['a'] + 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': [ + { + 'a': loc_a, + 'b': 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'] + }, + }, + 'df_route': { + 'id': match[0]['df_route_id'], + 'name': match[0]['df_route'], + 'status': match[0]['df_status'], + }, + 'related-services': + get_top_level_services(match[0]['df_route_id'], r) + } + result = json.dumps(result) + r.set(cache_key, result) + if not result: + return Response( + response="no available info for " + f"{ne_name_str} {object_name_str}", + status=404, + mimetype="text/html") + + return Response(result, mimetype="application/json")