diff --git a/inventory_provider/routes/neteng.py b/inventory_provider/routes/neteng.py index 29f8abcd9c963c06ac40fed5063d32f401435a28..f171fa9d4d340f084817c46c1cc2619d78896d7b 100644 --- a/inventory_provider/routes/neteng.py +++ b/inventory_provider/routes/neteng.py @@ -12,9 +12,20 @@ These endpoints are intended for use by neteng tools. .. autofunction:: inventory_provider.routes.neteng.get_location +/neteng/pops +--------------------------------- + +.. autofunction:: inventory_provider.routes.neteng.get_pop_names + +/neteng/pop/name +--------------------------------- + +.. autofunction:: inventory_provider.routes.neteng.get_pop_location + """ import json import logging +import re import threading from flask import Blueprint, Response, jsonify @@ -25,6 +36,12 @@ routes = Blueprint('neteng-query-routes', __name__) logger = logging.getLogger(__name__) _subnet_lookup_semaphore = threading.Semaphore() +STRING_LIST_SCHEMA = { + '$schema': 'http://json-schema.org/draft-07/schema#', + 'type': 'array', + 'items': {'type': 'string'} +} + @routes.after_request def after_request(resp): @@ -35,9 +52,9 @@ def after_request(resp): @common.require_accepts_json def get_location(equipment): """ - Handler for `/neteng/location/equipment-name` + Handler for `/neteng/location/<equipment-name>` - This method will pop location information for the IMS node + This method will return pop location information for the IMS node with name = `equipment-name`. 404 is returned if the IMS node name is not known. @@ -66,3 +83,66 @@ def get_location(equipment): mimetype='text/html') return jsonify(value[0]) + + +@routes.route('/pops', methods=['GET', 'POST']) +@common.require_accepts_json +def get_pop_names(): + """ + Handler for `/neteng/pops` + + This method will return a list of defined pop + names. Elements from this list can be used + with `/neteng/pop`. + + .. asjson:: + inventory_provider.routes.neteng.STRING_LIST_SCHEMA + + :return: as above + """ + + def _pops(): + r = common.get_current_redis() + for k in r.scan_iter('ims:pop:*', count=1000): + k = k.decode('utf-8') + m = re.match('^ims:pop:(.+)$', k) + yield m.group(1) + + return jsonify(sorted(list(_pops()))) + + +@routes.route('/pop/<name>', methods=['GET', 'POST']) +@common.require_accepts_json +def get_pop_location(name): + """ + Handler for `/neteng/pop/<name>` + + This method will return location information for the POP + with name = `name` in IMS. + + 404 is returned if the POP name is not known. + Otherwise the return value will be formatted as: + + .. asjson:: + inventory_provider.db.ims_data.POP_LOCATION_SCHEMA + + :return: as above + """ + + r = common.get_current_redis() + + value = r.get(f'ims:pop:{name}') + if not value: + return Response( + response='no location information available for "{pop}"', + status=404, + mimetype='text/html') + + value = json.loads(value.decode('utf-8')) + if not value: + return Response( + response='unexpected empty cached data for "{name}"', + status=500, + mimetype='text/html') + + return jsonify(value) diff --git a/test/data/router-info.json b/test/data/router-info.json index 11cf50f26a517892c3177aaebce341a8dd8207d0..2f118d9e38d0562b5521cf06e4673610dd114d00 100644 Binary files a/test/data/router-info.json and b/test/data/router-info.json differ diff --git a/test/test_neteng_routes.py b/test/test_neteng_routes.py index 1e95322a3be4cd43a83caeea12c2a9f6d35e034e..ceaa92c7735f6c9393563b0cc52d63db3a5ce5e9 100644 --- a/test/test_neteng_routes.py +++ b/test/test_neteng_routes.py @@ -2,7 +2,9 @@ import json import jsonschema import pytest -from inventory_provider.db.ims_data import NODE_LOCATION_SCHEMA +from inventory_provider.db.ims_data \ + import NODE_LOCATION_SCHEMA, POP_LOCATION_SCHEMA +from inventory_provider.routes.neteng import STRING_LIST_SCHEMA @pytest.mark.parametrize('equipment_name', [ @@ -25,3 +27,35 @@ def test_location_not_found(client, mocked_redis): '/neteng/location/BOGUS.EQUIPMENT.NAME', headers={'Accept': ['application/json']}) assert rv.status_code == 404 + + +def test_get_pops(client, mocked_redis): + rv = client.get( + '/neteng/pops', + headers={'Accept': ['application/json']}) + assert rv.status_code == 200 + jsonschema.validate( + json.loads(rv.data.decode('utf-8')), + STRING_LIST_SCHEMA) + + +@pytest.mark.parametrize('pop_name', [ + 'AMSTERDAM', 'VIENNA', 'LONDON', 'LONDON 2' +]) +def test_pop_location(client, mocked_redis, pop_name): + rv = client.post( + f'/neteng/pop/{pop_name}', + headers={'Accept': ['application/json']}) + assert rv.status_code == 200 + jsonschema.validate( + json.loads(rv.data.decode('utf-8')), + POP_LOCATION_SCHEMA) + s = json.loads(rv.data.decode('utf-8')) + print(s) + + +def test_pop_not_found(client, mocked_redis): + rv = client.post( + '/neteng/pop/BOGUS.POP.NAME', + headers={'Accept': ['application/json']}) + assert rv.status_code == 404