Skip to content
Snippets Groups Projects
classifier.py 4.30 KiB
import ipaddress
import json

from flask import Blueprint, Response

from inventory_provider.routes import common

routes = Blueprint("inventory-data-classifier-support-routes", __name__)


@routes.route("/trap-metadata/<source_equipment>/<path:interface>",
              methods=['GET', 'POST'])
@common.require_accepts_json
def get_trap_metadata(source_equipment, interface):
    r = common.get_redis()

    cache_key = 'classifier:cache:%s:%s' % (source_equipment, interface)
    result = r.get(cache_key)

    if result:
        result = result.decode('utf-8')
    else:
        result = {}

        services = r.get(
            'opsdb:interface_services:%s:%s' % (source_equipment, interface))
        if services:
            result['services'] = json.loads(services.decode('utf=8'))

        ifc_info = r.get(
            'netconf-interfaces:%s:%s' % (source_equipment, interface))
        if ifc_info:
            result['interface'] = json.loads(ifc_info.decode('utf-8'))

        if not result:
            return Response(
                response="no available info for {} {}".format(
                    source_equipment, interface),
                status=404,
                mimetype="text/html")

        result = json.dumps(result)
        # cache this data for the next call
        r.set(cache_key, result.encode('utf-8'))

    return Response(result, mimetype="application/json")


def ix_peering_group(address, description):
    """
    TODO: this is probably the least efficient way of doing this
          (if it's a problem, pre-compute these lists)

    :param ix_public_peer_info: ix public peer info loaded for address
    :return:
    """

    protocol = type(address).__name__
    keyword = description.split(' ')[0]  # regex needed??? (e.g. tabs???)

    r = common.get_redis()

    for k in r.keys('ix_public_peer:*'):
        peer = r.get(k.decode('utf-8')).decode('utf-8')
        peer = json.loads(peer)
        assert peer['description'] is not None  # sanity: as above...
        if not peer['description'].startswith(keyword):
            continue
        peer_address = ipaddress.ip_address(peer['name'])
        if protocol == type(peer_address).__name__:
            yield peer['name']


def find_interfaces(address):
    """
    TODO: this is probably the least efficient way of doing this
          (if it's a problem, pre-compute these lists)

    :param address: an ipaddress object
    :return:
    """
    r = common.get_redis()
    for k in r.keys('reverse_interface_addresses:*'):
        info = r.get(k.decode('utf-8')).decode('utf-8')
        info = json.loads(info)
        interface = ipaddress.ip_interface(info['interface address'])
        if address in interface.network:
            yield info


@routes.route("/peer-info/<address>", methods=['GET', 'POST'])
@common.require_accepts_json
def peer_info(address):

    r = common.get_redis()

    cache_key = 'classifier:peer-cache:%s' % address

    result = r.get(cache_key)
    if result:
        result = result.decode('utf-8')
    else:
        try:
            address_obj = ipaddress.ip_address(address)
        except ValueError:
            return Response(
                response='unable to parse %r as an ip address' % address,
                status=422,
                mimetype="text/html")

        result = {}

        info = r.get('ix_public_peer:%s' % address)
        if info:
            info = info.decode('utf-8')
            result['ix-public-peer-info'] = json.loads(info)
            description = result['ix-public-peer-info']['description']
            assert description is not None  # sanity
            result['ix-public-peer-group'] = list(
                ix_peering_group(address_obj, description))

        info = r.get('vpn_rr_peer:%s' % address)
        if info:
            info = info.decode('utf-8')
            result['vpn-rr-peer-info'] = json.loads(info)

        interfaces = list(find_interfaces(address_obj))
        if interfaces:
            result['interfaces'] = interfaces

        if not result:
            return Response(
                response='no peering info found for %s' % address,
                status=404,
                mimetype="text/html")

        result = json.dumps(result)
        # cache this data for the next call
        r.set(cache_key, result.encode('utf-8'))

    return Response(result, mimetype="application/json")