diff --git a/inventory_provider/routes/classifier.py b/inventory_provider/routes/classifier.py index b2e96b6522f9ca56bbb2015633178f087863fd0f..bcf80690874f15a21a40766d162f3f4e10997a5a 100644 --- a/inventory_provider/routes/classifier.py +++ b/inventory_provider/routes/classifier.py @@ -1,5 +1,6 @@ import ipaddress import json +import re from flask import Blueprint, Response @@ -33,6 +34,22 @@ def handle_request_error(error): status=error.status_code) +def base_interface_name(interface): + m = re.match(r'(.*?)(\.\d+)?$', interface) + assert m # sanity: anything should match + return m.group(1) + + +def related_interfaces(hostname, interface): + r = common.get_redis() + prefix = 'netconf-interfaces:%s:' % hostname + for k in r.keys(prefix + base_interface_name(interface) + '*'): + k = k.decode('utf-8') + assert k.startswith(prefix) # sanity + assert len(k) > len(prefix) # sanity (contains at least an interface) + yield k[len(prefix):] + + @routes.route("/trap-metadata/<source_equipment>/<path:interface>", methods=['GET', 'POST']) @common.require_accepts_json @@ -57,6 +74,17 @@ def get_trap_metadata(source_equipment, interface): if ifc_info: result['interface'] = json.loads(ifc_info.decode('utf-8')) + def _related_services(): + for related in related_interfaces(source_equipment, interface): + s = r.get('opsdb:interface_services:%s:%s' + % (source_equipment, related)) + if s: + yield json.loads(s.decode('utf-8')) + + related_services = list(_related_services()) + if related_services: + result['related-services'] = related_services + if not result: return Response( response="no available info for {} {}".format( diff --git a/test/test_classifier_routes.py b/test/test_classifier_routes.py index 308a725762c796fb715ddaadb7644f736dfb69b7..f44770dccb53f7716de92e4d5a9ace03880e3bee 100644 --- a/test/test_classifier_routes.py +++ b/test/test_classifier_routes.py @@ -7,6 +7,57 @@ DEFAULT_REQUEST_HEADERS = { "Accept": ["application/json"] } +JUNIPER_LINK_METADATA = { + "$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}$'} + ] + }, + "ipv4-interface-address": { + "type": "string", + "pattern": r'^(\d+\.){3}\d+/\d+$' + }, + "ipv6-interface-address": { + "type": "string", + "pattern": r'^[a-f\d:]+/\d+$' + }, + "interface-info": { + "type": "object", + "properties": { + "name": {"type": "string"}, + "description": {"type": "string"}, + "ipv4": {"$ref": "#/definitions/ipv4-interface-addres"}, + "ipv4": {"$ref": "#/definitions/ipv6-interface-addres"}, + }, + "required": ["name", "description", "ipv4", "ipv6"], + "additionalProperties": False + }, + "service-info": { + "type": "object" + } + }, + + "type": "object", + "properties": { + "services": { + "type": "array", + "items": {"$ref": "#/definitions/service-info"} + }, + "interface": {"$ref": "#/definitions/interface-info"}, + "related-services": { + "type": "array", + "items": {"$ref": "#/definitions/service-info"} + } + }, + "additionalProperties": False +} + def test_trap_metadata(client_with_mocked_data): response_schema = {