Skip to content
Snippets Groups Projects

Add classifier endpoint for router info

Merged Pelle Koster requested to merge router-info-endpoint into develop
Files
5
@@ -38,19 +38,34 @@ These endpoints are intended for use by Dashboard V3.
@@ -38,19 +38,34 @@ These endpoints are intended for use by Dashboard V3.
--------------------------------
--------------------------------
.. autofunction:: inventory_provider.routes.classifier.get_mtc_interface_info
.. autofunction:: inventory_provider.routes.classifier.get_mtc_interface_info
 
 
 
/classifier/router-info
 
--------------------------------
 
 
.. autofunction:: inventory_provider.routes.classifier.get_all_routers
 
 
 
/classifier/router-info/<equimpent-name>
 
--------------------------------
 
 
.. autofunction:: inventory_provider.routes.classifier.get_router_info
 
"""
"""
 
 
import functools
import ipaddress
import ipaddress
import json
import json
import logging
import logging
import re
import re
from functools import lru_cache
from functools import lru_cache
from typing import Optional
from typing import Iterable, List, Optional
from flask import Blueprint, Response, request
from flask import Blueprint, Response, jsonify, request
from redis import Redis
from redis import Redis
from inventory_provider.routes import common
from inventory_provider.routes import common
from inventory_provider.routes.common import _ignore_cache_or_retrieve
from inventory_provider.routes.common import _ignore_cache_or_retrieve, cache_result
routes = Blueprint("inventory-data-classifier-support-routes", __name__)
routes = Blueprint("inventory-data-classifier-support-routes", __name__)
@@ -137,22 +152,39 @@ def after_request(resp):
@@ -137,22 +152,39 @@ def after_request(resp):
@lru_cache(256)
@lru_cache(256)
def get_ims_equipment_name(equipment_name: str, r: Redis = None) -> str:
def get_ims_equipment_name(equipment_name: str, redis: Redis = None) -> str:
if not r:
redis = redis or common.get_current_redis()
r = common.get_current_redis()
candidates = _get_equipment_names_candidates(equipment_name)
 
result = _get_candidate_or_none(candidates, redis)
 
return result or candidates[0]
 
 
 
# We can't lru_cache this function like the above one, since if new equipment is
 
# installed and queried before the inventory is updated, it may return None. Then that
 
# result would be cached, and it would still not recognize that equipment, even after
 
# the inventory is updated
 
def get_ims_equipment_name_or_none(
 
equipment_name: str, redis: Redis = None
 
) -> Optional[str]:
 
redis = redis or common.get_current_redis()
 
candidates = _get_equipment_names_candidates(equipment_name)
 
return _get_candidate_or_none(candidates, redis)
 
 
 
def _get_candidate_or_none(candidates: Iterable[str], r: Redis) -> Optional[str]:
 
for c in candidates:
 
if r.exists(f"ims:location:{c}"):
 
return c
 
return None
 
 
 
def _get_equipment_names_candidates(equipment_name) -> List[str]:
ims_equipment_name = equipment_name.upper()
ims_equipment_name = equipment_name.upper()
candidates = [
return [
ims_equipment_name.split('.GEANT.')[0],
ims_equipment_name.split(".GEANT.")[0],
ims_equipment_name,
ims_equipment_name,
ims_equipment_name.split('.OFFICE.')[0]
ims_equipment_name.split(".OFFICE.")[0],
]
]
return_value = candidates[0]
loc_key = 'ims:location:{}'
for c in candidates:
if r.exists(loc_key.format(c)):
return_value = c
break
return return_value
def get_ims_interface(interface: str) -> str:
def get_ims_interface(interface: str) -> str:
@@ -1120,3 +1152,75 @@ def _get_coriant_info(
@@ -1120,3 +1152,75 @@ def _get_coriant_info(
r.set(cache_key, result.encode('utf-8'))
r.set(cache_key, result.encode('utf-8'))
return Response(result, mimetype="application/json")
return Response(result, mimetype="application/json")
 
 
 
@routes.route("/router-info", methods=["GET"])
 
@common.require_accepts_json
 
def get_all_routers() -> Response:
 
redis = common.get_current_redis()
 
all_routers_raw = redis.get("netdash")
 
all_routers = json.loads(all_routers_raw) if all_routers_raw else {}
 
return jsonify(
 
[
 
{"hostname": hostname, "vendor": vendor}
 
for hostname, vendor in all_routers.items()
 
]
 
)
 
 
 
@routes.route("/router-info/<equipment_name>", methods=["GET"])
 
@common.require_accepts_json
 
def get_router_info(equipment_name: str) -> Response:
 
redis = common.get_current_redis()
 
ims_equipment_name = get_ims_equipment_name_or_none(equipment_name, redis)
 
if not ims_equipment_name:
 
return Response(
 
response=f"no router info available for {equipment_name}",
 
status=404,
 
mimetype="text/plain",
 
)
 
 
cache_key = f"classifier-cache:router:{ims_equipment_name}"
 
 
result = cache_result(
 
cache_key,
 
func=functools.partial(_get_router_info, ims_equipment_name, redis),
 
redis=redis,
 
)
 
return Response(result, mimetype="application/json")
 
 
 
def _get_router_info(ims_source_equipment, redis):
 
 
def _format_service(service: dict):
 
keys = {"name", "status", "service_type", "sid"}
 
return {k: v for k, v in service.items() if k in keys}
 
 
related_services = {}
 
contacts = set()
 
 
vendor = "unknown"
 
all_routers = redis.get("netdash")
 
 
for name, v in (json.loads(all_routers) if all_routers else {}).items():
 
if name.startswith(ims_source_equipment.lower()):
 
vendor = v
 
break
 
 
for key in redis.scan_iter(f"ims:interface_services:{ims_source_equipment}:*"):
 
raw_services = redis.get(key.decode())
 
if not raw_services:
 
continue
 
 
services = json.loads(raw_services)
 
for service in services:
 
related_services.update({r["id"]: r for r in service["related-services"]})
 
contacts.update(service.get("contacts", []))
 
return {
 
"related-services": sorted(
 
map(_format_service, related_services.values()), key=lambda s: s["name"]
 
),
 
"contacts": sorted(contacts),
 
"location": _location_from_equipment(ims_source_equipment, redis),
 
"vendor": vendor,
 
}
Loading