From 57cffe85c4dd5d1ae934a63c61857bab28d54667 Mon Sep 17 00:00:00 2001
From: Erik Reid <erik.reid@geant.org>
Date: Mon, 28 Jan 2019 14:38:04 +0100
Subject: [PATCH] skeleton for poller api

---
 inventory_provider/__init__.py      |  3 ++
 inventory_provider/routes/common.py | 34 +++++++++++++
 inventory_provider/routes/poller.py | 74 +++++++++++++++++++++++++++++
 3 files changed, 111 insertions(+)
 create mode 100644 inventory_provider/routes/common.py
 create mode 100644 inventory_provider/routes/poller.py

diff --git a/inventory_provider/__init__.py b/inventory_provider/__init__.py
index a8adb17f..5de6a311 100644
--- a/inventory_provider/__init__.py
+++ b/inventory_provider/__init__.py
@@ -29,6 +29,9 @@ def create_app():
     from inventory_provider.routes import classifier
     app.register_blueprint(classifier.routes, url_prefix='/classifier')
 
+    from inventory_provider.routes import poller
+    app.register_blueprint(poller.routes, url_prefix='/poller')
+
     if "SETTINGS_FILENAME" not in os.environ:
         assert False, \
             "environment variable SETTINGS_FILENAME' must be defined"
diff --git a/inventory_provider/routes/common.py b/inventory_provider/routes/common.py
new file mode 100644
index 00000000..857d644e
--- /dev/null
+++ b/inventory_provider/routes/common.py
@@ -0,0 +1,34 @@
+import functools
+
+from flask import request, Response, current_app
+import redis
+
+
+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
+
+
+def redis_connection():
+    """
+    just a common place for acquiring a redis connection
+    :return: a redis connection
+    """
+    redis_config = current_app.config["INVENTORY_PROVIDER_CONFIG"]["redis"]
+    return redis.StrictRedis(
+        host=redis_config["hostname"],
+        port=redis_config["port"])
diff --git a/inventory_provider/routes/poller.py b/inventory_provider/routes/poller.py
new file mode 100644
index 00000000..962660e6
--- /dev/null
+++ b/inventory_provider/routes/poller.py
@@ -0,0 +1,74 @@
+import json
+
+from flask import Blueprint, Response, jsonify
+from lxml import etree
+from inventory_provider import juniper
+from inventory_provider.routes import common
+
+routes = Blueprint('poller-support-routes', __name__)
+
+
+@routes.route('/interfaces/<hostname>', methods=['GET', 'POST'])
+@common.require_accepts_json
+def poller_interface_oids(hostname):
+    r = common.redis_connection()
+
+    netconf_string = r.hget(hostname, 'netconf')
+    if not netconf_string:
+        return Response(
+            response='no netconf available info for %r' % hostname,
+            status=404,
+            mimetype='text/html')
+
+    snmp_data_string = r.hget(hostname, 'snmp-interfaces')
+    if not snmp_data_string:
+        return Response(
+            response='no snmp available info for '%r % 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'
+
+    snmp_indexes = dict([
+        (_ifc_name(ifc), int(ifc['index'])) for ifc in
+        json.loads(snmp_data_string.decode('utf-8'))
+    ])
+
+    interfaces = list(juniper.list_interfaces(
+        etree.XML(netconf_string.decode('utf-8'))))
+
+    if not interfaces:
+        return Response(
+            response='no interfaces found for %r' % hostname,
+            status=404,
+            mimetype='text/html')
+
+    result = []
+    for ifc in interfaces:
+        if not ifc['description']:
+            continue
+
+        ifc_data = {
+            'name': ifc['name'],
+            'snmp-index': snmp_indexes.get(ifc['name'], None),
+            'description': ifc['description'],
+            'circuits': []
+        }
+
+        circuits = r.hget(
+            'interface_services', '%s::%s' % (hostname, ifc['name']))
+        if circuits:
+            ifc_data['circuits'] = [
+                {'type': c['circuit_type'], 'id': c['id']}
+                for c in json.loads(circuits.decode('utf-8'))
+
+            ]
+
+        result.append(ifc_data)
+
+    return jsonify(result)
-- 
GitLab