Skip to content
Snippets Groups Projects
Commit edda2250 authored by Release Webservice's avatar Release Webservice
Browse files

Finished release 0.77.

parents 2f1265c7 24b1628d
Branches
Tags 0.77
No related merge requests found
......@@ -2,7 +2,10 @@
All notable changes to this project will be documented in this file.
## [0/76] - 2021-11-24
## [0.77] - 2021-12-03
- DBOARD3-493: added /neteng/location/equipment-name
## [0.76] - 2021-11-24
- DBOARD3-486: added `updated-started` timestamp to /version response
## [0.75] - 2021-09-30
......
......@@ -39,3 +39,5 @@ API modules
.. automodule:: inventory_provider.routes.data
.. automodule:: inventory_provider.routes.jobs
.. automodule:: inventory_provider.routes.neteng
......@@ -74,6 +74,9 @@ def create_app():
from inventory_provider.routes import 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):
from inventory_provider.routes import testing
app.register_blueprint(testing.routes, url_prefix='/testing')
......
......@@ -26,6 +26,40 @@ IMS_OPSDB_STATUS_MAP = {
STATUSES_TO_IGNORE = \
[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):
# 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):
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 = [
ims.SITE_PROPERTIES['City'],
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])
......@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
setup(
name='inventory-provider',
version="0.76",
version="0.77",
author='GEANT',
author_email='swd@geant.org',
description='Dashboard inventory provider',
......
import json
import os
import jsonschema
import inventory_provider
from inventory_provider.db.ims import InventoryStatus
from inventory_provider.db.ims_data import lookup_lg_routers, \
get_node_locations, IMS_OPSDB_STATUS_MAP, \
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):
ds = inventory_provider.db.ims.IMS(
'http://dummy_base', 'dummy_username', 'dummy_password')
with open('test/data/ims_circuit_hierarchy_data.json') as data:
se_data = json.load(data)
se_data = _json_test_data('ims_circuit_hierarchy_data.json')
mocker.patch.object(
inventory_provider.db.ims.IMS,
'get_filtered_entities',
......@@ -55,8 +66,7 @@ def test_get_circuit_hierarchy(mocker):
def test_get_port_details(mocker):
def _se(entity, y, step_count):
with open(f'test/data/ims_{entity}_details_data.json') as data:
return json.load(data)
return _json_test_data(f'ims_{entity}_details_data.json')
mocker.patch.object(
inventory_provider.db.ims.IMS,
......@@ -105,8 +115,7 @@ def test_get_port_details(mocker):
def test_get_port_id_services(mocker):
with open('test/data/ims_port_id_services_data.json') as data:
d = json.load(data)
d = _json_test_data('ims_port_id_services_data.json')
mocker.patch.object(
inventory_provider.db.ims.IMS,
......@@ -215,8 +224,8 @@ def test_get_port_id_services(mocker):
def test_lookup_lg_routers(mocker):
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 = json.load(data)
ims.return_value.get_filtered_entities.return_value \
= _json_test_data('ims_lg_data.json')
ims.return_value.get_entity_by_id.return_value = {
'name': 'pop name',
'longitude': 'long',
......@@ -266,13 +275,16 @@ def test_lookup_lg_routers(mocker):
def test_get_node_location(mocker):
ims = mocker.patch('inventory_provider.db.ims.IMS')
with open('test/data/ims_nodes_data.json') as data:
resp_data = json.load(data)
resp_data = _json_test_data('ims_nodes_data.json')
ims.return_value.get_all_entities.return_value = resp_data
ds = inventory_provider.db.ims.IMS(
'dummy_base', 'dummy_username', 'dummy_password')
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 res[0] == ('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