Skip to content
Snippets Groups Projects
neteng.py 3.58 KiB
"""
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

/neteng/pops
---------------------------------

.. autofunction:: inventory_provider.routes.neteng.get_pop_names

/neteng/pop/name
---------------------------------

.. autofunction:: inventory_provider.routes.neteng.get_pop_location

"""
import json
import logging
import re
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()

STRING_LIST_SCHEMA = {
    '$schema': 'https://json-schema.org/draft-07/schema#',
    'type': 'array',
    'items': {'type': 'string'}
}


@routes.after_request
def after_request(resp):
    return common.after_request(resp)


@routes.route('/location/<equipment>', methods=['GET'])
@common.require_accepts_json
def get_location(equipment):
    """
    Handler for `/neteng/location/<equipment-name>`

    This method will return 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=f'no location information available for "{equipment}"',
            status=404,
            mimetype='text/html')

    value = json.loads(value.decode('utf-8'))
    if not value:
        return Response(
            response=f'unexpected empty cached data for "{equipment}"',
            status=500,
            mimetype='text/html')

    return jsonify(value[0])


@routes.route('/pops', methods=['GET'])
@common.require_accepts_json
def get_pop_names():
    """
    Handler for `/neteng/pops`

    This method will return a list of defined pop
    abbreviations.  Elements from this list can be used
    with `/neteng/pop`.

    .. asjson::
        inventory_provider.routes.neteng.STRING_LIST_SCHEMA

    :return: as above
    """

    def _pops():
        r = common.get_current_redis()
        for k in r.scan_iter('ims:site:*', count=1000):
            k = k.decode('utf-8')
            m = re.match('^ims:site:(.+)$', k)
            yield m.group(1)

    return jsonify(sorted(list(_pops())))


@routes.route('/pop/<abbreviation>', methods=['GET'])
@common.require_accepts_json
def get_pop_location(abbreviation):
    """
    Handler for `/neteng/pop/<name>`

    This method will return location information for the POP
    with abbreviation = `abbreviation` in IMS.

    404 is returned if the POP name is not known.
    Otherwise the return value will be formatted as:

    .. asjson::
        inventory_provider.db.ims_data.POP_LOCATION_SCHEMA

    :return: as above
    """

    r = common.get_current_redis()

    value = r.get(f'ims:site:{abbreviation}')
    if not value:
        return Response(
            response=f'no location information available for "{abbreviation}"',
            status=404,
            mimetype='text/html')

    value = json.loads(value.decode('utf-8'))
    if not value:
        return Response(
            response=f'unexpected empty cached data for "{abbreviation}"',
            status=500,
            mimetype='text/html')

    return jsonify(value)