diff --git a/inventory_provider/routes/classifier_schema.py b/inventory_provider/routes/classifier_schema.py index 9c8b0ffe9bbc4a1541d6b008db2c76a9af7a758d..4259d5fe39e925a7e79596c6991778867659c547 100644 --- a/inventory_provider/routes/classifier_schema.py +++ b/inventory_provider/routes/classifier_schema.py @@ -53,7 +53,8 @@ _common_schema_definitions = { }, "service_type": {"type": "string"}, "project": {"type": "string"}, - "sid": {"type": "string"} + "sid": {"type": "string"}, + "contacts": {"type": "array", "items": {"type": "string"}} }, "additionalProperties": False }, @@ -254,10 +255,17 @@ _juniper_link_response_schema_definitions = { }, "service_type": {"type": "string"}, "project": {"type": "string"}, - "sid": {"type": "string"} + "sid": {"type": "string"}, + "contacts": {"type": "array", "items": {"type": "string"}} }, "required": [ - "name", "status", "circuit_type", "service_type", "project"], + "name", + "status", + "circuit_type", + "service_type", + "project", + "contacts" + ], "additionalProperties": False } } @@ -508,10 +516,17 @@ _infinera_lambda_response_schema_definitions = { }, "service_type": {"type": "string"}, "project": {"type": "string"}, - "sid": {"type": "string"} + "sid": {"type": "string"}, + "contacts": {"type": "array", "items": {"type": "string"}} }, "required": [ - "name", "status", "circuit_type", "service_type", "project"], + "name", + "status", + "circuit_type", + "service_type", + "project", + "contacts" + ], "additionalProperties": False }, "geant-lambda": { diff --git a/inventory_provider/routes/mic.py b/inventory_provider/routes/mic.py index 83858f6e66637ce8dac8c5071f044bb5d3b21842..7315674b7c2e44fa5d5433bee42af94806165034 100644 --- a/inventory_provider/routes/mic.py +++ b/inventory_provider/routes/mic.py @@ -13,8 +13,10 @@ These endpoints are intended for use by the Maintenance Impact Calculator tool """ +import itertools import json import logging +from collections import defaultdict from flask import Blueprint, request, current_app, Response @@ -52,6 +54,32 @@ INTERFACES_LIST_SCHEMA = { "additionalProperties": False } +IMPACT_SCHEMA = { + "schema": "http:json-schema.org/draft-07/schema#", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": {"type": "integer"}, + "sid": {"type": "string"}, + "name": {"type": "string"}, + "service_type": {"type": "string"}, + "contacts": { + "type": "array", + "items": {"type": "string"} + } + }, + "required": ["id", "sid", "name", "service_type", "contacts"], + "additionalProperties": False + } + } + }, + "additionalProperties": False +} + @routes.route('/sites') def get_sites(): @@ -105,3 +133,46 @@ def get_interfaces(node): result = json.dumps(interfaces_) r.set(cache_key, result.encode('utf-8')) return Response(result, mimetype='application/json') + + +@routes.route('/impact/<site>') +@routes.route('/impact/<site>/<node>') +@routes.route('/impact/<site>/<node>/<interface_>') +def get_impact(site, node=None, interface_=None): + cache_key_end = ':'.join(filter(None, (site, node, interface_))) + cache_key = f"classifier-cache:mic:impact:{cache_key_end}" + r = common.get_current_redis() + result = _ignore_cache_or_retrieve(request, cache_key, r) + if not result: + if interface_: + key_pattern = f"ims:interface_services:{node}:{interface_}" + data = [r.get(key_pattern)] + elif node: + data = [] + else: + data = [] + data = (json.loads(all_data.decode('utf-8')) for all_data in data) + # convert list of lists to single dimensional list + data = itertools.chain(*data) + # extract all related services from each list element and join into a + # single list + data = itertools.chain(*(d.get('related-services', []) for d in data)) + data = filter( + lambda x: x['circuit_type'] == 'service' + and x['status'] == 'operational', + data) + unique_services = sorted({d['id']: d for d in data}.values(), + key=lambda x: x['name']) + services_by_type = defaultdict(list) + for service in unique_services: + services_by_type[service['service_type']].append({ + 'id': service['id'], + 'sid': service['sid'], + 'name': service['name'], + 'service_type': service['service_type'], + 'contacts': service['contacts'] + }) + result = json.dumps(services_by_type) + + r.set(cache_key, result.encode('utf-8')) + return Response(result, mimetype='application/json') diff --git a/inventory_provider/tasks/worker.py b/inventory_provider/tasks/worker.py index 53b9454365bf37b18fb726fc635aeea4fb716f01..3517bb0377fa4b6acf5e1310223de180fd01bba6 100644 --- a/inventory_provider/tasks/worker.py +++ b/inventory_provider/tasks/worker.py @@ -1022,7 +1022,9 @@ def transform_ims_data(data): _get_related_services(circ['id']) for tlc in circ['related-services']: - contacts.update(tlc.pop('contacts')) + # why were these removed? + # contacts.update(tlc.pop('contacts')) + contacts.update(tlc.get('contacts')) circ['contacts'] = sorted(list(contacts)) circ['calculated-speed'] = _get_speed(circ['id']) diff --git a/test/data/router-info.json b/test/data/router-info.json index f1d75202a5ed84d46f7354bcb8672f298f2d1a9f..cb5ddc3b55b0e47a07d09dc100afbea0e6caba11 100644 Binary files a/test/data/router-info.json and b/test/data/router-info.json differ diff --git a/test/test_mic_routes.py b/test/test_mic_routes.py index d744c1955542b548ab8ef979d3bdef26edc1e194..cb43ae49d7f69cbab5cbd2fdf3da175e987f9f85 100644 --- a/test/test_mic_routes.py +++ b/test/test_mic_routes.py @@ -3,7 +3,7 @@ import json import jsonschema from inventory_provider.routes.mic import SITES_LIST_SCHEMA, \ - NODES_LIST_SCHEMA, INTERFACES_LIST_SCHEMA + NODES_LIST_SCHEMA, INTERFACES_LIST_SCHEMA, IMPACT_SCHEMA DEFAULT_REQUEST_HEADERS = { "Content-type": "application/json", @@ -39,3 +39,13 @@ def test_get_interfaces(client, mocked_redis): assert rv.is_json response_data = json.loads(rv.data.decode('utf-8')) jsonschema.validate(response_data, INTERFACES_LIST_SCHEMA) + + +def test_get_impact(client, mocked_redis): + rv = client.get( + '/mic/impact/AMSTERDAM/MX1.AMS.NL/AE21', + 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, IMPACT_SCHEMA)