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.
--------------------------------
.. 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 json
import logging
import re
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 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__)
@@ -137,22 +152,39 @@ def after_request(resp):
@lru_cache(256)
def get_ims_equipment_name(equipment_name: str, r: Redis = None) -> str:
if not r:
r = common.get_current_redis()
def get_ims_equipment_name(equipment_name: str, redis: Redis = None) -> str:
redis = redis or 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()
candidates = [
ims_equipment_name.split('.GEANT.')[0],
return [
ims_equipment_name.split(".GEANT.")[0],
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:
@@ -1120,3 +1152,75 @@ def _get_coriant_info(
r.set(cache_key, result.encode('utf-8'))
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