diff --git a/README.md b/README.md index 774e59d40a7f1d8830855cb1547a71fdfa01a6a3..57ff6af4366489b876c8b3a9b12934216047245f 100644 --- a/README.md +++ b/README.md @@ -355,7 +355,7 @@ Any non-empty responses are JSON formatted messages. alarms database -* /classifier/*`type`*/*`source-equipment`*/*`source-interface`* +* /classifier/trap-metadata/*`source-equipment`*/*`source-interface`* The source-equipment is the equipment that causes the trap, not the NMS that sends it. @@ -369,6 +369,71 @@ Any non-empty responses are JSON formatted messages. } ``` +* /classifier/peer-info/*`address`* + + The `address` parameter should be the ip address of + a remote peer. If this address is found in the system + then information about the interface is returned, otherwise + 404 is returned. + + The resp onse will be formatted according to the following syntax: + + ```json + { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + + "definitions": { + "ip-address": { + "type": "string", + "oneOf": [ + {"pattern": r'^(\d+\.){3}\d+$'}, + {"pattern": r'^([a-f\d]{4}:){7}[a-f\d]{4}$'} + ] + }, + "vpn-rr-peer": { + "type": "object", + "properties": { + "name": {"$ref": "#/definitions/ip-address"}, + "description": {"type": "string"}, + "peer-as": {"type": "integer"}, + "router": {"type": "string"} + }, + "required": ["name", "description"], + "additionalProperties": False + }, + + "ix-public-peer": { + "type": "object", + "properties": { + "name": {"$ref": "#/definitions/ip-address"}, + "description": {"type": "string"}, + "router": {"type": "string"}, + "as": { + "type": "object", + "properties": { + "local": {"type": "integer"}, + "peer": {"type": "integer"}, + }, + "required": ["local", "peer"], + "additionalProperties": False + } + }, + "required": ["name", "description", "as"], + "additionalProperties": False + } + }, + + "type": "object", + "properties": { + "ix-public-peer-info": {"$ref": "#/definitions/ix-public-peer"}, + "vpn-rr-peer-info": {"$ref": "#/definitions/vpn-rr-peer"} + }, + "additionalProperties": False + } + ``` + + * /poller/interfaces/*`hostname`* The response will be the list of active interfaces on the @@ -724,7 +789,7 @@ was started with the `ENABLE_TESTING_ROUTES` flag. * `vpn_rr_peers/<address>` * key examples - * `ix_public_peer:193.203.0.203` + * `vpn_rr_peers:193.203.0.203` * valid values: ```json { diff --git a/inventory_provider/routes/classifier.py b/inventory_provider/routes/classifier.py index 5f984cec14036cbb2a7ec13d464c3388a6d8bc56..0d66e89e4b3da1af595967f812a3d0026ebee137 100644 --- a/inventory_provider/routes/classifier.py +++ b/inventory_provider/routes/classifier.py @@ -1,6 +1,6 @@ import json -from flask import Blueprint, Response +from flask import Blueprint, Response, jsonify from inventory_provider.routes import common @@ -43,3 +43,30 @@ def get_trap_metadata(source_equipment, interface): r.set(cache_key, result.encode('utf-8')) return Response(result, mimetype="application/json") + + +@routes.route("/peer-info/<address>", methods=['GET', 'POST']) +@common.require_accepts_json +def peer_info(address): + + r = common.get_redis() + + result = {} + + info = r.get('ix_public_peer:%s' % address) + if info: + info = info.decode('utf-8') + result['ix-public-peer-info'] = json.loads(info) + + info = r.get('vpn_rr_peer:%s' % address) + if info: + info = info.decode('utf-8') + result['vpn-rr-peer-info'] = json.loads(info) + + if not result: + return Response( + response='no peering info found for %s' % address, + status=404, + mimetype="text/html") + + return jsonify(result) diff --git a/test/test_classifier_routes.py b/test/test_classifier_routes.py index 6b68221d42e1805c373990ea12cbdc8950bb57db..bd783d52c388d2a18caa352ed4b697ec50da014e 100644 --- a/test/test_classifier_routes.py +++ b/test/test_classifier_routes.py @@ -1,5 +1,6 @@ import json import jsonschema +import pytest DEFAULT_REQUEST_HEADERS = { "Content-type": "application/json", @@ -19,3 +20,86 @@ def test_trap_metadata(client_with_mocked_data): assert rv.is_json response_data = json.loads(rv.data.decode('utf-8')) jsonschema.validate(response_data, response_schema) + + +@pytest.mark.parametrize("peer_address,peer_type", [ + ('109.105.110.54', 'vpn-rr-peer-info'), + ('2001:07f8:001c:024a:0000:0000:316e:0001', 'ix-public-peer-info'), + ('2001:07f8:000b:0100:01d1:a5d1:0310:0029', 'ix-public-peer-info'), + ('195.66.224.238', 'ix-public-peer-info'), +] +) +def test_peer_info(client_with_mocked_data, peer_address, peer_type): + response_schema = { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + + "definitions": { + "ip-address": { + "type": "string", + "oneOf": [ + {"pattern": r'^(\d+\.){3}\d+$'}, + {"pattern": r'^([a-f\d]{4}:){7}[a-f\d]{4}$'} + ] + }, + "vpn-rr-peer": { + "type": "object", + "properties": { + "name": {"$ref": "#/definitions/ip-address"}, + "description": {"type": "string"}, + "peer-as": {"type": "integer"}, + "router": {"type": "string"} + }, + "required": ["name", "description"], + "additionalProperties": False + }, + + "ix-public-peer": { + "type": "object", + "properties": { + "name": {"$ref": "#/definitions/ip-address"}, + "description": {"type": "string"}, + "router": {"type": "string"}, + "as": { + "type": "object", + "properties": { + "local": {"type": "integer"}, + "peer": {"type": "integer"}, + }, + "required": ["local", "peer"], + "additionalProperties": False + } + }, + "required": ["name", "description", "as"], + "additionalProperties": False + } + }, + + "type": "object", + "properties": { + "ix-public-peer-info": {"$ref": "#/definitions/ix-public-peer"}, + "vpn-rr-peer-info": {"$ref": "#/definitions/vpn-rr-peer"} + }, + "additionalProperties": False + } + + rv = client_with_mocked_data.get( + '/classifier/peer-info/%s' % peer_address, + 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, response_schema) + + assert len(response_data) == 1, \ + "peer should be only vpn-rr or ix-public, not both" + + assert peer_type in response_data + + +def test_peer_not_found(client_with_mocked_data): + + rv = client_with_mocked_data.get( + '/classifier/peer-info/1.2.3.4.5', + headers=DEFAULT_REQUEST_HEADERS) + assert rv.status_code == 404