Skip to content
Snippets Groups Projects
Commit e6fb8ab2 authored by Erik Reid's avatar Erik Reid
Browse files

Finished feature DBOARD3-493-neteng-ims-location-api.

parents 5c2400ac c582c440
Branches
Tags
No related merge requests found
...@@ -39,3 +39,5 @@ API modules ...@@ -39,3 +39,5 @@ API modules
.. automodule:: inventory_provider.routes.data .. automodule:: inventory_provider.routes.data
.. automodule:: inventory_provider.routes.jobs .. automodule:: inventory_provider.routes.jobs
.. automodule:: inventory_provider.routes.neteng
...@@ -74,6 +74,9 @@ def create_app(): ...@@ -74,6 +74,9 @@ def create_app():
from inventory_provider.routes import lnetd from inventory_provider.routes import lnetd
app.register_blueprint(lnetd.routes, url_prefix='/LnetD') app.register_blueprint(lnetd.routes, url_prefix='/LnetD')
from inventory_provider.routes import neteng
app.register_blueprint(neteng.routes, url_prefix='/neteng')
if app.config.get('ENABLE_TESTING_ROUTES', False): if app.config.get('ENABLE_TESTING_ROUTES', False):
from inventory_provider.routes import testing from inventory_provider.routes import testing
app.register_blueprint(testing.routes, url_prefix='/testing') app.register_blueprint(testing.routes, url_prefix='/testing')
......
...@@ -26,6 +26,40 @@ IMS_OPSDB_STATUS_MAP = { ...@@ -26,6 +26,40 @@ IMS_OPSDB_STATUS_MAP = {
STATUSES_TO_IGNORE = \ STATUSES_TO_IGNORE = \
[InventoryStatus.OUT_OF_SERVICE.value] [InventoryStatus.OUT_OF_SERVICE.value]
NODE_LOCATION_SCHEMA = {
'$schema': 'http://json-schema.org/draft-07/schema#',
'definitions': {
'pop-location': {
'type': 'object',
'properties': {
'name': {'type': 'string'},
'city': {'type': 'string'},
'country': {'type': 'string'},
'abbreviation': {'type': 'string'},
'longitude': {'type': 'number'},
'latitude': {'type': 'number'}
},
'required': [
'name',
'city',
'country',
'abbreviation',
'longitude',
'latitude'],
'additionalProperties': False
}
},
'type': 'object',
'properties': {
'equipment-name': {'type': 'string'},
'status': {'type': 'string'},
'pop': {'$ref': '#/definitions/pop-location'}
},
'required': ['equipment-name', 'status', 'pop'],
'additionalProperties': False
}
def get_non_monitored_circuit_ids(ds: IMS): def get_non_monitored_circuit_ids(ds: IMS):
# note the id for the relevant field is hard-coded. I didn't want to use # note the id for the relevant field is hard-coded. I didn't want to use
...@@ -300,6 +334,17 @@ def get_circuit_hierarchy(ds: IMS): ...@@ -300,6 +334,17 @@ def get_circuit_hierarchy(ds: IMS):
def get_node_locations(ds: IMS): def get_node_locations(ds: IMS):
"""
return location info for all Site nodes
yields dictionaries formatted as:
.. as_json::
inventory_provider.db.ims_data.NODE_LOCATION_SCHEMA
:param ds:
:return: yields dicts as above
"""
site_nav_props = [ site_nav_props = [
ims.SITE_PROPERTIES['City'], ims.SITE_PROPERTIES['City'],
ims.SITE_PROPERTIES['SiteAliases'], ims.SITE_PROPERTIES['SiteAliases'],
......
"""
Neteng Support Endpoints
=========================
These endpoints are intended for use by neteng tools.
.. contents:: :local:
/neteng/location/equipment-name
---------------------------------
.. autofunction:: inventory_provider.routes.neteng.get_location
"""
import json
import logging
import threading
from flask import Blueprint, Response, jsonify
from inventory_provider.routes import common
routes = Blueprint('neteng-query-routes', __name__)
logger = logging.getLogger(__name__)
_subnet_lookup_semaphore = threading.Semaphore()
@routes.after_request
def after_request(resp):
return common.after_request(resp)
@routes.route('/location/<equipment>', methods=['GET', 'POST'])
@common.require_accepts_json
def get_location(equipment):
"""
Handler for `/neteng/location/equipment-name`
This method will pop location information for the IMS node
with name = `equipment-name`.
404 is returned if the IMS node name is not known.
Otherwise the return value will be formatted as:
.. asjson::
inventory_provider.db.ims_data.NODE_LOCATION_SCHEMA
:return: as above
"""
r = common.get_current_redis()
value = r.get(f'ims:location:{equipment}')
if not value:
return Response(
response='no location information available for "{equipment}"',
status=404,
mimetype='text/html')
value = json.loads(value.decode('utf-8'))
if not value:
return Response(
response='unexpected empty cached data for "{equipment}"',
status=500,
mimetype='text/html')
return jsonify(value[0])
import json import json
import os
import jsonschema
import inventory_provider import inventory_provider
from inventory_provider.db.ims import InventoryStatus from inventory_provider.db.ims import InventoryStatus
from inventory_provider.db.ims_data import lookup_lg_routers, \ from inventory_provider.db.ims_data import lookup_lg_routers, \
get_node_locations, IMS_OPSDB_STATUS_MAP, \ get_node_locations, IMS_OPSDB_STATUS_MAP, \
get_port_id_services, get_port_details, \ get_port_id_services, get_port_details, \
get_circuit_hierarchy get_circuit_hierarchy, NODE_LOCATION_SCHEMA
def _json_test_data(filename):
abs_filename = os.path.join(
os.path.dirname(__file__),
'data',
filename)
with open(abs_filename) as data:
return json.load(data)
def test_get_circuit_hierarchy(mocker): def test_get_circuit_hierarchy(mocker):
ds = inventory_provider.db.ims.IMS( ds = inventory_provider.db.ims.IMS(
'http://dummy_base', 'dummy_username', 'dummy_password') 'http://dummy_base', 'dummy_username', 'dummy_password')
with open('test/data/ims_circuit_hierarchy_data.json') as data: se_data = _json_test_data('ims_circuit_hierarchy_data.json')
se_data = json.load(data)
mocker.patch.object( mocker.patch.object(
inventory_provider.db.ims.IMS, inventory_provider.db.ims.IMS,
'get_filtered_entities', 'get_filtered_entities',
...@@ -55,8 +66,7 @@ def test_get_circuit_hierarchy(mocker): ...@@ -55,8 +66,7 @@ def test_get_circuit_hierarchy(mocker):
def test_get_port_details(mocker): def test_get_port_details(mocker):
def _se(entity, y, step_count): def _se(entity, y, step_count):
with open(f'test/data/ims_{entity}_details_data.json') as data: return _json_test_data(f'ims_{entity}_details_data.json')
return json.load(data)
mocker.patch.object( mocker.patch.object(
inventory_provider.db.ims.IMS, inventory_provider.db.ims.IMS,
...@@ -105,8 +115,7 @@ def test_get_port_details(mocker): ...@@ -105,8 +115,7 @@ def test_get_port_details(mocker):
def test_get_port_id_services(mocker): def test_get_port_id_services(mocker):
with open('test/data/ims_port_id_services_data.json') as data: d = _json_test_data('ims_port_id_services_data.json')
d = json.load(data)
mocker.patch.object( mocker.patch.object(
inventory_provider.db.ims.IMS, inventory_provider.db.ims.IMS,
...@@ -215,8 +224,8 @@ def test_get_port_id_services(mocker): ...@@ -215,8 +224,8 @@ def test_get_port_id_services(mocker):
def test_lookup_lg_routers(mocker): def test_lookup_lg_routers(mocker):
ims = mocker.patch('inventory_provider.db.ims.IMS') ims = mocker.patch('inventory_provider.db.ims.IMS')
with open('test/data/ims_lg_data.json') as data: ims.return_value.get_filtered_entities.return_value \
ims.return_value.get_filtered_entities.return_value = json.load(data) = _json_test_data('ims_lg_data.json')
ims.return_value.get_entity_by_id.return_value = { ims.return_value.get_entity_by_id.return_value = {
'name': 'pop name', 'name': 'pop name',
'longitude': 'long', 'longitude': 'long',
...@@ -266,13 +275,16 @@ def test_lookup_lg_routers(mocker): ...@@ -266,13 +275,16 @@ def test_lookup_lg_routers(mocker):
def test_get_node_location(mocker): def test_get_node_location(mocker):
ims = mocker.patch('inventory_provider.db.ims.IMS') ims = mocker.patch('inventory_provider.db.ims.IMS')
with open('test/data/ims_nodes_data.json') as data: resp_data = _json_test_data('ims_nodes_data.json')
resp_data = json.load(data)
ims.return_value.get_all_entities.return_value = resp_data ims.return_value.get_all_entities.return_value = resp_data
ds = inventory_provider.db.ims.IMS( ds = inventory_provider.db.ims.IMS(
'dummy_base', 'dummy_username', 'dummy_password') 'dummy_base', 'dummy_username', 'dummy_password')
res = list(get_node_locations(ds)) res = list(get_node_locations(ds))
for name, node in res:
assert isinstance(name, str)
jsonschema.validate(node, NODE_LOCATION_SCHEMA)
assert len(res) == 36 assert len(res) == 36
assert res[0] == ('LON3_CX_01', { assert res[0] == ('LON3_CX_01', {
'equipment-name': 'LON3_CX_01', 'equipment-name': 'LON3_CX_01',
......
import json
import jsonschema
import pytest
from inventory_provider.db.ims_data import NODE_LOCATION_SCHEMA
@pytest.mark.parametrize('equipment_name', [
'MX1.AMS.NL',
'MIL-OLA1',
'LON02-GRV1'
])
def test_location(client, mocked_redis, equipment_name):
rv = client.post(
f'/neteng/location/{equipment_name}',
headers={'Accept': ['application/json']})
assert rv.status_code == 200
jsonschema.validate(
json.loads(rv.data.decode('utf-8')),
NODE_LOCATION_SCHEMA)
def test_location_not_found(client, mocked_redis):
rv = client.post(
'/neteng/location/BOGUS.EQUIPMENT.NAME',
headers={'Accept': ['application/json']})
assert rv.status_code == 404
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment