diff --git a/inventory_provider/routes/classifier.py b/inventory_provider/routes/classifier.py index 16cab7001476c1fef49b742c59c10d32133681c4..7ae881e1a9bc4559e93b395a40de1f59a502ce88 100644 --- a/inventory_provider/routes/classifier.py +++ b/inventory_provider/routes/classifier.py @@ -13,6 +13,65 @@ routes = Blueprint("inventory-data-classifier-support-routes", __name__) logger = logging.getLogger(__name__) +def _LOCATION(equipment, name, abbreviation): + return { + 'equipment': equipment, + 'name': name, + 'abbreviation': abbreviation + } + + +def _remove_duplicates_from_list(l): + """ + removes duplicates from the input list + the list items must be encodable as json + :param l: + :return: a new list with unique elements + """ + tmp_dict = dict([(json.dumps(item, sort_keys=True), item) for item in l]) + return list(tmp_dict.values()) + + +def _location_from_router(router_name): + r = common.get_current_redis() + result = r.get(f'opsdb:location:{router_name}') + if not result: + logger.error(f'error looking up location for {router_name}') + return None + + result = json.loads(result.decode('utf-8')) + if not result: + logger.error(f'sanity failure: empty list for location {router_name}') + return None + + return { + 'a': _LOCATION( + equipment=result[0]['equipment-name'], + name=result[0]['pop']['name'], + abbreviation=result[0]['pop']['abbreviation']) + } + + +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): status_code = 500 @@ -86,7 +145,11 @@ def get_juniper_link_info(source_equipment, interface): if result: result = result.decode('utf-8') else: - result = {} + + result = { + 'locations': [] + } + top_level_services = [] services = r.get( @@ -95,6 +158,8 @@ def get_juniper_link_info(source_equipment, interface): 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']] ifc_info = r.get( 'netconf-interfaces:%s:%s' % (source_equipment, interface)) @@ -132,6 +197,10 @@ def get_juniper_link_info(source_equipment, interface): if top_level_services: result['related-services'] = top_level_services + if not result['locations']: + result['locations'] = [_location_from_router(source_equipment)] + + result['locations'] = _remove_duplicates_from_list(result['locations']) result = json.dumps(result) # cache this data for the next call r.set(cache_key, result.encode('utf-8')) @@ -243,33 +312,45 @@ def peer_info(address): address = obj.exploded except ValueError: raise ClassifierProcessingError( - 'unable to parse %r as an ip address' % address) + f'unable to parse {address} as an ip address') r = common.get_current_redis() - cache_key = 'classifier-cache:peer:%s' % address + cache_key = f'classifier-cache:peer:{address}' result = r.get(cache_key) if result: result = result.decode('utf-8') else: - result = {} + result = { + 'locations': [] + } info = r.get('ix_public_peer:%s' % address) if info: info = info.decode('utf-8') - result['ix-public-peer-info'] = ix_peering_info(json.loads(info)) + info = json.loads(info) + result['ix-public-peer-info'] = ix_peering_info(info) + router_location = _location_from_router(info['router']) + result['locations'] += [router_location] info = r.get('vpn_rr_peer:%s' % address) if info: info = info.decode('utf-8') - result['vpn-rr-peer-info'] = json.loads(info) + info = json.loads(info) + result['vpn-rr-peer-info'] = info + router_location = _location_from_router(info['router']) + result['locations'] += [router_location] 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']] + result['locations'] = _remove_duplicates_from_list(result['locations']) result = json.dumps(result) # cache this data for the next call r.set(cache_key, result.encode('utf-8')) @@ -293,7 +374,9 @@ def get_trap_metadata(source_equipment, interface, circuit_id): if result: result = result.decode('utf-8') else: - result = {} + result = { + 'locations': [] + } top_level_services = [] services = r.get( @@ -306,6 +389,8 @@ def get_trap_metadata(source_equipment, interface, circuit_id): top_level_services.extend(tls) if top_level_services: result['related-services'] = top_level_services + result['locations'] += [ + _location_from_service_dict(s) for s in result['services']] gl = r.get('opsdb:geant_lambdas:%s' % circuit_id.lower()) if gl: @@ -337,9 +422,6 @@ def get_coriant_info(equipment_name, entity_string): equipment_name, entity_string) result = r.get(cache_key) - # this is just for development to save deleting the cache every time - # result = False - if result: result = result.decode('utf-8') else: @@ -356,7 +438,8 @@ def get_coriant_info(equipment_name, entity_string): result = { 'equipment name': equipment_name, 'card id': m.group(1), - 'port number': m.group(2) + 'port number': m.group(2), + 'locations': [] } config = current_app.config['INVENTORY_PROVIDER_CONFIG'] @@ -367,6 +450,16 @@ def get_coriant_info(equipment_name, entity_string): if path: result['path'] = path + result['locations'] += [{ + 'a': _LOCATION( + equipment=path['a']['equipment name'], + name=path['a']['pop']['name'], + abbreviation=path['a']['pop']['abbreviation']), + 'b': _LOCATION( + equipment=path['b']['equipment name'], + name=path['b']['pop']['name'], + abbreviation=path['b']['pop']['abbreviation']), + }] top_level_services = get_top_level_services(path['id'], r) if top_level_services: diff --git a/test/conftest.py b/test/conftest.py index 0726f549347bcef6c443393c82eaa634281ea8dc..7ed4586b067d4144eb9c6a30d6790a02da415f8e 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -143,6 +143,7 @@ def flask_config_filename(): @pytest.fixture def mocked_redis(mocker): + MockedRedis.db = None # force data to be reloaded mocker.patch( 'inventory_provider.tasks.common.redis.StrictRedis', MockedRedis) diff --git a/test/test_celery_worker_global.py b/test/test_celery_worker_global.py index 1d9a0b9c126bf077ddca48542b01c45d00ff9e34..da36007024692deaed25f5f2853442e4dec64ce8 100644 --- a/test/test_celery_worker_global.py +++ b/test/test_celery_worker_global.py @@ -2,13 +2,10 @@ just checks that the worker methods call the right functions and some data ends up in the right place ... otherwise not very detailed """ -import contextlib import os from inventory_provider.tasks import worker from inventory_provider.tasks import common - - from inventory_provider.tasks.common import _get_redis @@ -22,19 +19,11 @@ def backend_db(): }).db -@contextlib.contextmanager -def _mocked_connection(x): - yield x - - def test_update_locations(mocker, mocked_worker_module, mocked_redis): mocker.patch( 'inventory_provider.db.opsdb.lookup_pop_info', lambda c, h: [{'C': c, 'H': h}]) - mocker.patch( - 'inventory_provider.db.db.connection', - _mocked_connection) def _cached_locations(): db = backend_db() diff --git a/test/test_classifier_routes.py b/test/test_classifier_routes.py index 81fa8e87c906dea82d927129bc595c7e8e71eced..56821fcb4056b1107ad9f76fe5d103560525d309 100644 --- a/test/test_classifier_routes.py +++ b/test/test_classifier_routes.py @@ -8,109 +8,139 @@ 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+$' +LOCATIONS_DEFINITIONS = { + "location-endpoint": { + "type": "object", + "properties": { + "equipment": {"type": "string"}, + "name": {"type": "string"}, + "abbreviation": {"type": "string"} }, - "ipv6-interface-address": { - "type": "string", - "pattern": r'^[a-f\d:]+/\d+$' + "required": ["equipment", "name", "abbreviation"], + "additionalProperties": False + }, + "location": { + "type": "object", + "properties": { + "a": {"$ref": "#/definitions/location-endpoint"}, + "b": {"$ref": "#/definitions/location-endpoint"} }, - "interface-info": { - "type": "object", - "properties": { - "name": {"type": "string"}, - "description": {"type": "string"}, - "ipv4": { - "type": "array", - "items": {"$ref": "#/definitions/ipv4-interface-address"} - }, - "ipv6": { - "type": "array", - "items": {"$ref": "#/definitions/ipv6-interface-address"} - }, + "required": ["a"], + "additionalProperties": False + }, + "locations-list": { + "type": "array", + "items": {"$ref": "#/definitions/location"} + } +} - # TODO: check what's changed: added to make tests pass - 'bundle': {"type": "array"} +JUNIPER_LINK_METADATA_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": { + "type": "array", + "items": {"$ref": "#/definitions/ipv4-interface-address"} }, - "required": ["name", "description", "ipv4", "ipv6"], - "additionalProperties": False + "ipv6": { + "type": "array", + "items": {"$ref": "#/definitions/ipv6-interface-address"} + }, + + # TODO: check what's changed: added to make tests pass + 'bundle': {"type": "array"} }, - "service-info": { - "type": "object", - "properties": { - "id": {"type": "integer"}, - "name": {"type": "string"}, - "status": { - "type": "string", - "enum": ["operational", "installed", "planned", "ordered"] - }, - "circuit_type": { - "type": "string", - "enum": ["path", "service", "l2circuit"] - }, - "service_type": {"type": "string"}, - "project": {"type": "string"}, - "equipment": {"type": "string"}, - "pop": {"type": "string"}, - "pop_abbreviation": {"type": "string"}, - - "other_end_pop": {"type": "string"}, - "other_end_pop_abbreviation": {"type": "string"}, - "other_end_equipment": {"type": "string"}, - "port": {"type": "string"}, - "other_end_port": {"type": "string"}, - "logical_unit": { - "oneOf": [ - {"type": "integer"}, - {"type": "string", "maxLength": 0} - ] - }, - "other_end_logical_unit": { - "oneOf": [ - {"type": "integer"}, - {"type": "string", "maxLength": 0} - ] - }, - "manufacturer": { - "type": "string", - "enum": ["juniper", "coriant", "infinera", - "cisco", "hewlett packard", - "corsa", "graham smith uk ltd", - "unknown", ""] - }, - "card_id": {"type": "string"}, - "other_end_card_id": {"type": "string"}, - "interface_name": {"type": "string"}, - "other_end_interface_name": {"type": "string"}, - - # TODO: check what's changed: added to make tests pass - 'other_end_pop_name': {"type": "string"}, - 'pop_name': {"type": "string"} + "required": ["name", "description", "ipv4", "ipv6"], + "additionalProperties": False + }, + "service-info": { + "type": "object", + "properties": { + "id": {"type": "integer"}, + "name": {"type": "string"}, + "status": { + "type": "string", + "enum": ["operational", "installed", "planned", "ordered"] }, - # TODO: modify service-info so that "" entries are just omitted - # (... rather than requiring 'oneOf') - # TODO: put 'other_end_*' params in a sub dictionary - # "required": [ - # "id", "name", "status", - # "circuit_type", "service_type", - # "project", "port", "manufacturer", - # "equipment", "logical_unit", "card_id", "interface_name" - # ], - "additionalProperties": False - } + "circuit_type": { + "type": "string", + "enum": ["path", "service", "l2circuit"] + }, + "service_type": {"type": "string"}, + "project": {"type": "string"}, + "equipment": {"type": "string"}, + "pop": {"type": "string"}, + "pop_abbreviation": {"type": "string"}, + + "other_end_pop": {"type": "string"}, + "other_end_pop_abbreviation": {"type": "string"}, + "other_end_equipment": {"type": "string"}, + "port": {"type": "string"}, + "other_end_port": {"type": "string"}, + "logical_unit": { + "oneOf": [ + {"type": "integer"}, + {"type": "string", "maxLength": 0} + ] + }, + "other_end_logical_unit": { + "oneOf": [ + {"type": "integer"}, + {"type": "string", "maxLength": 0} + ] + }, + "manufacturer": { + "type": "string", + "enum": ["juniper", "coriant", "infinera", + "cisco", "hewlett packard", + "corsa", "graham smith uk ltd", + "unknown", ""] + }, + "card_id": {"type": "string"}, + "other_end_card_id": {"type": "string"}, + "interface_name": {"type": "string"}, + "other_end_interface_name": {"type": "string"}, + + # TODO: check what's changed: added to make tests pass + 'other_end_pop_name': {"type": "string"}, + 'pop_name': {"type": "string"} + }, + # TODO: modify service-info so that "" entries are just omitted + # (... rather than requiring 'oneOf') + # TODO: put 'other_end_*' params in a sub dictionary + # "required": [ + # "id", "name", "status", + # "circuit_type", "service_type", + # "project", "port", "manufacturer", + # "equipment", "logical_unit", "card_id", "interface_name" + # ], + "additionalProperties": False + } +} + +JUNIPER_LINK_METADATA = { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + + "definitions": { + **JUNIPER_LINK_METADATA_DEFINITIONS, **LOCATIONS_DEFINITIONS }, "type": "object", @@ -123,7 +153,8 @@ JUNIPER_LINK_METADATA = { "related-services": { "type": "array", "items": {"$ref": "#/definitions/service-info"} - } + }, + "locations": {"$ref": "#/definitions/locations-list"} }, # "required": ["interface"], "additionalProperties": False @@ -156,12 +187,18 @@ def test_juniper_link_info_not_found(client): 'ipv4': [], 'ipv6': [], 'bundle': [] - } + }, + 'locations': [{ + 'a': { + 'equipment': 'mx1.ams.nl.geant.net', + 'name': 'Amsterdam', + 'abbreviation': 'ams'} + }] } -VPN_RR_PEER_INFO_KEYS = {'vpn-rr-peer-info'} -IX_PUBLIC_PEER_INFO_KEYS = {'ix-public-peer-info', 'interfaces'} +VPN_RR_PEER_INFO_KEYS = {'vpn-rr-peer-info', 'locations'} +IX_PUBLIC_PEER_INFO_KEYS = {'ix-public-peer-info', 'interfaces', 'locations'} @pytest.mark.parametrize('peer_address,expected_response_keys', [ @@ -173,95 +210,98 @@ IX_PUBLIC_PEER_INFO_KEYS = {'ix-public-peer-info', 'interfaces'} ) def test_peer_info( client, peer_address, expected_response_keys): - 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}$'} - ] - }, - "interface-address": { - "type": "string", - "oneOf": [ - {"pattern": r'^(\d+\.){3}\d+/\d+$'}, - {"pattern": r'^[a-f\d:]+/\d+$'} - ] - }, - "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 - }, - "ix-public-peer-list": { - "type": "array", - "items": {"$ref": "#/definitions/ip-address"} + response_schema_definitions = { + "ip-address": { + "type": "string", + "oneOf": [ + {"pattern": r'^(\d+\.){3}\d+$'}, + {"pattern": r'^([a-f\d]{4}:){7}[a-f\d]{4}$'} + ] + }, + "interface-address": { + "type": "string", + "oneOf": [ + {"pattern": r'^(\d+\.){3}\d+/\d+$'}, + {"pattern": r'^[a-f\d:]+/\d+$'} + ] + }, + "vpn-rr-peer": { + "type": "object", + "properties": { + "name": {"$ref": "#/definitions/ip-address"}, + "description": {"type": "string"}, + "peer-as": {"type": "integer"}, + "router": {"type": "string"} }, - "ix-public-peer-info": { - "type": "object", - "properties": { - "peer": {"$ref": "#/definitions/ix-public-peer"}, - "group": {"$ref": "#/definitions/ix-public-peer-list"}, - "router": {"$ref": "#/definitions/ix-public-peer-list"} - }, - "required": ["peer", "group", "router"], - "additionalProperties": False + "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 + } }, - "interface-info": { - "type": "object", - "properties": { - "name": {"$ref": "#/definitions/ip-address"}, - "interface address": { - "$ref": "#/definitions/interface-address"}, - "interface name": {"type": "string"}, - "router": {"type": "string"} - }, - "required": [ - "name", "interface address", "interface name", "router"], - "additionalProperties": False + "required": ["name", "description", "as"], + "additionalProperties": False + }, + "ix-public-peer-list": { + "type": "array", + "items": {"$ref": "#/definitions/ip-address"} + }, + "ix-public-peer-info": { + "type": "object", + "properties": { + "peer": {"$ref": "#/definitions/ix-public-peer"}, + "group": {"$ref": "#/definitions/ix-public-peer-list"}, + "router": {"$ref": "#/definitions/ix-public-peer-list"} }, - "service-info": { - "type": "object" + "required": ["peer", "group", "router"], + "additionalProperties": False + }, + "interface-info": { + "type": "object", + "properties": { + "name": {"$ref": "#/definitions/ip-address"}, + "interface address": { + "$ref": "#/definitions/interface-address"}, + "interface name": {"type": "string"}, + "router": {"type": "string"} }, - "interface-lookup-info": { - "type": "object", - "properties": { - "interface": {"$ref": "#/definitions/interface-info"}, - "services": { - "type": "array", - "items": {"$ref": "#/definitions/service-info"} - } + "required": [ + "name", "interface address", "interface name", "router"], + "additionalProperties": False + }, + "service-info": { + "type": "object" + }, + "interface-lookup-info": { + "type": "object", + "properties": { + "interface": {"$ref": "#/definitions/interface-info"}, + "services": { + "type": "array", + "items": {"$ref": "#/definitions/service-info"} } } + } + } + response_schema = { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + + "definitions": { + **response_schema_definitions, **LOCATIONS_DEFINITIONS }, "type": "object", @@ -272,7 +312,8 @@ def test_peer_info( "interfaces": { "type": "array", "items": {"$ref": "#/definitions/interface-lookup-info"} - } + }, + "locations": {"$ref": "#/definitions/locations-list"} }, "additionalProperties": False } @@ -301,7 +342,7 @@ def test_peer_not_found(client): headers=DEFAULT_REQUEST_HEADERS) assert rv.status_code == 200 response_data = json.loads(rv.data.decode('utf-8')) - assert response_data == {} + assert response_data == {'locations': []} @pytest.mark.parametrize('id_,equipment,entity_name,card_id,port_number', [ @@ -325,7 +366,27 @@ def test_coriant_info( 'inventory_provider.db.db.connection', mocked_connection) mocker.patch( 'inventory_provider.db.opsdb.lookup_coriant_path', - lambda a, b, c, d: {'id': '123', 'C': a, 'E': b, 'CID': c, 'P': d}) + lambda a, b, c, d: { + 'id': '123', + 'C': a, + 'E': b, + 'CID': c, + 'P': d, + 'a': { + 'equipment name': 'abc', + 'pop': { + 'name': 'zzz', + 'abbreviation': '123' + } + }, + 'b': { + 'equipment name': 'ddd', + 'pop': { + 'name': 'aaa', + 'abbreviation': '999' + } + } + }) rv = client.get( '/classifier/coriant-info/{equipment}/{entity}'.format( @@ -346,8 +407,18 @@ def test_coriant_info( 'C': CONNECTION, 'E': equipment, 'CID': card_id, - 'P': port_number - } + 'P': port_number, + 'a': { + 'equipment name': 'abc', + 'pop': {'name': 'zzz', 'abbreviation': '123'}}, + 'b': { + 'equipment name': 'ddd', + 'pop': {'name': 'aaa', 'abbreviation': '999'}} + }, + 'locations': [{ + 'a': {'equipment': 'abc', 'name': 'zzz', 'abbreviation': '123'}, + 'b': {'equipment': 'ddd', 'name': 'aaa', 'abbreviation': '999'} + }] } assert response_data == expected_response diff --git a/test/test_infinera_classifier.py b/test/test_infinera_classifier.py index 28fe8464037870d6ed784e3fbdbde37928930c4c..d37940287e75cfc9b12c6c723c3438a267a0dcb4 100644 --- a/test/test_infinera_classifier.py +++ b/test/test_infinera_classifier.py @@ -6,74 +6,103 @@ DEFAULT_REQUEST_HEADERS = { "Accept": ["application/json"] } +LOCATIONS_DEFINITIONS = { + "location-endpoint": { + "type": "object", + "properties": { + "equipment": {"type": "string"}, + "name": {"type": "string"}, + "abbreviation": {"type": "string"} + }, + "required": ["equipment", "name", "abbreviation"], + "additionalProperties": False + }, + "location": { + "type": "object", + "properties": { + "a": {"$ref": "#/definitions/location-endpoint"}, + "b": {"$ref": "#/definitions/location-endpoint"} + }, + "required": ["a"], + "additionalProperties": False + }, + "locations-list": { + "type": "array", + "items": {"$ref": "#/definitions/location"} + } +} + +INFINERA_LINK_METADATA_DEFINITIONS = { + "service-info": { + "type": "object", + "properties": { + "id": {"type": "integer"}, + "name": {"type": "string"}, + "status": { + "type": "string", + "enum": ["operational", "installed", "planned", "ordered"] + }, + "circuit_type": { + "type": "string", + "enum": ["path", "service", "l2circuit"] + }, + "service_type": {"type": "string"}, + "project": {"type": "string"}, + "pop_name": {"type": "string"}, + "pop_abbreviation": {"type": "string"}, + "other_end_pop_name": {"type": "string"}, + "other_end_pop_abbreviation": {"type": "string"}, + "equipment": {"type": "string"}, + "other_end_equipment": {"type": "string"}, + "port": {"type": "string"}, + "other_end_port": {"type": "string"}, + "logical_unit": { + "oneOf": [ + {"type": "integer"}, + {"type": "string", "maxLength": 0} + ] + }, + "other_end_logical_unit": { + "oneOf": [ + {"type": "integer"}, + {"type": "string", "maxLength": 0} + ] + }, + "manufacturer": { + "type": "string", + "enum": ["juniper", "coriant", "infinera", + "cisco", "hewlett packard", + "corsa", "graham smith uk ltd", + "unknown", ""] + }, + "card_id": {"type": "string"}, + "other_end_card_id": {"type": "string"}, + "interface_name": {"type": "string"}, + "other_end_interface_name": {"type": "string"} + }, + "additionalProperties": False + }, + "geant-lambda": { + "type": "object", + "properties": { + "id": {"type": "integer"}, + "name": {"type": "string"}, + "project": {"type": "string"}, + "status": { + "type": "string", + "enum": ["operational", "installed", "planned", "ordered"] + }, + }, + "additionalProperties": False + } +} INFINERA_LINK_METADATA = { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "definitions": { - "service-info": { - "type": "object", - "properties": { - "id": {"type": "integer"}, - "name": {"type": "string"}, - "status": { - "type": "string", - "enum": ["operational", "installed", "planned", "ordered"] - }, - "circuit_type": { - "type": "string", - "enum": ["path", "service", "l2circuit"] - }, - "service_type": {"type": "string"}, - "project": {"type": "string"}, - "pop_name": {"type": "string"}, - "pop_abbreviation": {"type": "string"}, - "other_end_pop_name": {"type": "string"}, - "other_end_pop_abbreviation": {"type": "string"}, - "equipment": {"type": "string"}, - "other_end_equipment": {"type": "string"}, - "port": {"type": "string"}, - "other_end_port": {"type": "string"}, - "logical_unit": { - "oneOf": [ - {"type": "integer"}, - {"type": "string", "maxLength": 0} - ] - }, - "other_end_logical_unit": { - "oneOf": [ - {"type": "integer"}, - {"type": "string", "maxLength": 0} - ] - }, - "manufacturer": { - "type": "string", - "enum": ["juniper", "coriant", "infinera", - "cisco", "hewlett packard", - "corsa", "graham smith uk ltd", - "unknown", ""] - }, - "card_id": {"type": "string"}, - "other_end_card_id": {"type": "string"}, - "interface_name": {"type": "string"}, - "other_end_interface_name": {"type": "string"} - }, - "additionalProperties": False - }, - "geant-lambda": { - "type": "object", - "properties": { - "id": {"type": "integer"}, - "name": {"type": "string"}, - "project": {"type": "string"}, - "status": { - "type": "string", - "enum": ["operational", "installed", "planned", "ordered"] - }, - }, - "additionalProperties": False - } + **INFINERA_LINK_METADATA_DEFINITIONS, **LOCATIONS_DEFINITIONS }, "type": "object", @@ -89,6 +118,7 @@ INFINERA_LINK_METADATA = { "geant-lambda": { "$ref": "#/definitions/geant-lambda" }, + "locations": {"$ref": "#/definitions/locations-list"} }, "additionalProperties": False }