import functools import json import pkg_resources from flask import Blueprint, jsonify, request, Response, current_app from lxml import etree import redis from inventory_provider import db, juniper routes = Blueprint("inventory-data-query-routes", __name__) API_VERSION = '0.1' def require_accepts_json(f): """ used as a route handler decorator to return an error unless the request allows responses with type "application/json" :param f: the function to be decorated :return: the decorated function """ @functools.wraps(f) def decorated_function(*args, **kwargs): # TODO: use best_match to disallow */* ...? if not request.accept_mimetypes.accept_json: return Response( response="response will be json", status=406, mimetype="text/html") return f(*args, **kwargs) return decorated_function @routes.route("/version", methods=['GET', 'POST']) @require_accepts_json def version(): return Response( json.dumps({ 'api': API_VERSION, 'module': pkg_resources.get_distribution('inventory_provider').version }), mimetype="application/json" ) @routes.route("/routers", methods=['GET', 'POST']) @require_accepts_json def routers(): redis_config = current_app.config["INVENTORY_PROVIDER_CONFIG"]["redis"] r = redis.StrictRedis( host=redis_config["hostname"], port=redis_config["port"]) return Response( json.dumps(list([k.decode("utf-8") for k in r.keys("*")])), mimetype="application/json") @routes.route("/interfaces/<hostname>", methods=['GET', 'POST']) @require_accepts_json def router_interfaces(hostname): redis_config = current_app.config["INVENTORY_PROVIDER_CONFIG"]["redis"] r = redis.StrictRedis( host=redis_config["hostname"], port=redis_config["port"]) netconf_string = r.hget(hostname, 'netconf') if not netconf_string: return Response( response="no available info for '%s'" % hostname, status=404, mimetype="text/html") interfaces = list(juniper.list_interfaces( etree.XML(netconf_string.decode('utf-8')))) if not interfaces: return Response( response="no interfaces found for '%s'" % hostname, status=404, mimetype="text/html") return Response( json.dumps(interfaces), mimetype="application/json") @routes.route("/snmp/<hostname>", methods=['GET', 'POST']) @require_accepts_json def snmp_ids(hostname): redis_config = current_app.config["INVENTORY_PROVIDER_CONFIG"]["redis"] r = redis.StrictRedis( host=redis_config["hostname"], port=redis_config["port"]) ifc_data_string = r.hget(hostname, 'snmp-interfaces') if not ifc_data_string: return Response( response="no available info for '%s'" % hostname, status=404, mimetype="text/html") def _ifc_name(ifc): if 'v4InterfaceName' in ifc: return ifc['v4InterfaceName'] if 'v6InterfaceName' in ifc: return ifc['v6InterfaceName'] assert False, "sanity failure: no interface name found" ifc_data = json.loads(ifc_data_string.decode('utf-8')) result = [ {'index': i['index'], 'name': _ifc_name(i)} for i in ifc_data] return Response( json.dumps(result), mimetype="application/json") @routes.route("/debug-dump/<hostname>", methods=['GET', 'POST']) @require_accepts_json def debug_dump_router_info(hostname): redis_config = current_app.config["INVENTORY_PROVIDER_CONFIG"]["redis"] r = redis.StrictRedis( host=redis_config["hostname"], port=redis_config["port"]) info = dict([(k.decode('utf-8'), json.loads(v.decode('utf-8'))) for k, v in r.hgetall(hostname).items()]) if not info: return Response( response="no info found for router '%s'" % hostname, status=404, mimetype="text/html") return Response( json.dumps(info), mimetype="application/json") @routes.route("/bgp/<hostname>", methods=['GET', 'POST']) @require_accepts_json def bgp_configs(hostname): redis_config = current_app.config["INVENTORY_PROVIDER_CONFIG"]["redis"] r = redis.StrictRedis( host=redis_config["hostname"], port=redis_config["port"]) netconf_string = r.hget(hostname, 'netconf') if not netconf_string: return Response( response="no available info for '%s'" % hostname, status=404, mimetype="text/html") routes = list(juniper.list_bgp_routes( etree.XML(netconf_string.decode('utf-8')))) if not routes: return Response( response="no interfaces found for '%s'" % hostname, status=404, mimetype="text/html") return Response( json.dumps(routes), mimetype="application/json") @routes.route("/interfaces/status/<hostname>/<path:interface>", methods=['GET', 'POST']) @require_accepts_json def interface_statuses(hostname, interface): r = db.get_redis() result = r.hget("interface_statuses", "{}::{}".format(hostname, interface)) if not result: return Response( response="no available info for {} {}".format(hostname, interface), status=404, mimetype="text/html") return jsonify({"status": result.decode('utf-8')}) @routes.route("/services/<hostname>/<path:interface>", methods=['GET', 'POST']) def services_for_interface(hostname, interface): r = db.get_redis() result = r.hget("interface_services", "{}::{}".format(hostname, interface)) if not result: return Response( response="no available info for {} {}".format(hostname, interface), status=404, mimetype="text/html") return jsonify(json.loads(result.decode('utf-8')))