diff --git a/changelog b/changelog index 6fe9ef386da6a0410c22b0f11afc08cf79039948..fafaca0c0a61d2306743a7d6c708d20bbb36375c 100644 --- a/changelog +++ b/changelog @@ -1,2 +1,3 @@ 0.1: initial skeleton -0.2: use celery for task management \ No newline at end of file +0.2: use celery for task management +0.3: basic opsdb, alarmsdb coms & test api \ No newline at end of file diff --git a/inventory_provider/__init__.py b/inventory_provider/__init__.py index 3eb4592480884d54a4bde47e961fea4ec900c2e3..a8e55e8edb0d75ffbbbcae07669b9d63450a064f 100644 --- a/inventory_provider/__init__.py +++ b/inventory_provider/__init__.py @@ -17,11 +17,17 @@ def create_app(): app = Flask(__name__) app.secret_key = "super secret session key" - from inventory_provider import data_routes - app.register_blueprint(data_routes.routes, url_prefix='/data') + from inventory_provider.routes import data + app.register_blueprint(data.routes, url_prefix='/data') - from inventory_provider import job_routes - app.register_blueprint(job_routes.routes, url_prefix='/jobs') + from inventory_provider.routes import jobs + app.register_blueprint(jobs.routes, url_prefix='/jobs') + + from inventory_provider.routes import opsdb + app.register_blueprint(opsdb.routes, url_prefix='/opsdb') + + from inventory_provider.routes import alarmsdb + app.register_blueprint(alarmsdb.routes, url_prefix='/alarmsdb') if "SETTINGS_FILENAME" not in os.environ: assert False, \ diff --git a/inventory_provider/alarmsdb.py b/inventory_provider/alarmsdb.py index 01a4390b2ba53bf112185a508da8b760365ef42b..c3e7d5bb57d843e1134a0a7c9bb2e7eb61bf4092 100644 --- a/inventory_provider/alarmsdb.py +++ b/inventory_provider/alarmsdb.py @@ -40,3 +40,4 @@ def _db_test(db, router): crs.execute(query, (router['hostname'],)) for (absid,) in crs: database_logger.debug("absid: %r" % absid) + yield absid diff --git a/inventory_provider/config.py b/inventory_provider/config.py index 36d814633db7b53fdc9038b5c373f173bf4e6326..101f60e3688b37bdbee0eeeb1d2f55844bd736dc 100644 --- a/inventory_provider/config.py +++ b/inventory_provider/config.py @@ -6,9 +6,9 @@ import jsonschema CONFIG_SCHEMA = { "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "alarms-db": { + + "definitions": { + "database_credentials": { "type": "object", "properties": { "hostname": {"type": "string"}, @@ -18,7 +18,14 @@ CONFIG_SCHEMA = { }, "required": ["hostname", "dbname", "username", "password"], "additionalProperties": False - }, + + } + }, + + "type": "object", + "properties": { + "alarms-db": {"$ref": "#/definitions/database_credentials"}, + "ops-db": {"$ref": "#/definitions/database_credentials"}, "oid_list.conf": {"type": "string"}, "routers_community.conf": {"type": "string"}, "ssh": { @@ -42,6 +49,7 @@ CONFIG_SCHEMA = { }, "required": [ "alarms-db", + "ops-db", "oid_list.conf", "routers_community.conf", "ssh", diff --git a/inventory_provider/opsdb.py b/inventory_provider/opsdb.py new file mode 100644 index 0000000000000000000000000000000000000000..6457745ad9f6ecd26645f6d9304d0cf9ac4f04ee --- /dev/null +++ b/inventory_provider/opsdb.py @@ -0,0 +1,43 @@ +import contextlib +import logging + +import mysql.connector + +from inventory_provider.constants import DATABASE_LOGGER_NAME + + +@contextlib.contextmanager +def connection(opsdb): + cx = None + try: + cx = mysql.connector.connect( + host=opsdb["hostname"], + user=opsdb["username"], + passwd=opsdb["password"], + db=opsdb["dbname"]) + yield cx + finally: + if cx: + cx.close() + + +@contextlib.contextmanager +def cursor(cnx): + csr = None + try: + csr = cnx.cursor() + yield csr + finally: + if csr: + csr.close() + + +def _db_test(db, router): + database_logger = logging.getLogger(DATABASE_LOGGER_NAME) + with cursor(db) as crs: + query = "select model, manufacturer from equipment where name = %s" + crs.execute(query, (router['hostname'],)) + for (model, manufacturer) in crs: + database_logger.debug("%s: %s %s" % ( + router['hostname'], model, manufacturer)) + yield {"model": model, "manufacturer": manufacturer} diff --git a/inventory_provider/routes/__init__.py b/inventory_provider/routes/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/inventory_provider/routes/alarmsdb.py b/inventory_provider/routes/alarmsdb.py new file mode 100644 index 0000000000000000000000000000000000000000..ce06752987a037d8958f227a4b32268805c4b5c9 --- /dev/null +++ b/inventory_provider/routes/alarmsdb.py @@ -0,0 +1,41 @@ +import functools +import json + +from flask import Blueprint, request, Response, current_app +from inventory_provider import alarmsdb + +routes = Blueprint("inventory-alarmsdb-query-routes", __name__) + + +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("/test", methods=['GET', 'POST']) +@require_accepts_json +def alarmsdb_test(): + config = current_app.config['INVENTORY_PROVIDER_CONFIG'] + + result = {} + with alarmsdb.connection(config['alarms-db']) as db: + for r in config['routers']: + result[r['hostname']] = list(alarmsdb._db_test(db, r)) + + return Response( + json.dumps(result), + mimetype="application/json") diff --git a/inventory_provider/data_routes.py b/inventory_provider/routes/data.py similarity index 100% rename from inventory_provider/data_routes.py rename to inventory_provider/routes/data.py diff --git a/inventory_provider/job_routes.py b/inventory_provider/routes/jobs.py similarity index 100% rename from inventory_provider/job_routes.py rename to inventory_provider/routes/jobs.py diff --git a/inventory_provider/routes/opsdb.py b/inventory_provider/routes/opsdb.py new file mode 100644 index 0000000000000000000000000000000000000000..1b3195ecec04c04d87598918bc03d4862e07739a --- /dev/null +++ b/inventory_provider/routes/opsdb.py @@ -0,0 +1,41 @@ +import functools +import json + +from flask import Blueprint, request, Response, current_app +from inventory_provider import opsdb + +routes = Blueprint("inventory-opsdb-query-routes", __name__) + + +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("/test", methods=['GET', 'POST']) +@require_accepts_json +def opsdb_test(): + config = current_app.config['INVENTORY_PROVIDER_CONFIG'] + + result = {} + with opsdb.connection(config['ops-db']) as db: + for r in config['routers']: + result[r['hostname']] = list(opsdb._db_test(db, r)) + + return Response( + json.dumps(result), + mimetype="application/json") diff --git a/setup.py b/setup.py index 7bd7ee5d2dc6631242a2e80c7ba373fe9655adb1..478b13ec8fadcc982eb73c70d9c350f046c691a7 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name='inventory-provider', - version="0.2", + version="0.3", author='GEANT', author_email='swd@geant.org', description='Dashboard inventory provider', diff --git a/test/test_data_routes.py b/test/test_data_routes.py index 42878e10b79bddc64d4c509ed24f8fbfb7cf9385..03642b09a4e6dbf1fb60cb43835e7e399191072e 100644 --- a/test/test_data_routes.py +++ b/test/test_data_routes.py @@ -90,6 +90,12 @@ def data_config_filename(tmp_dir_name): "username": "xxxxxx", "password": "xxxxxxxx" }, + "ops-db": { + "hostname": "xxxxxxx.yyyyy.zzz", + "dbname": "xxxxxx", + "username": "xxxxxx", + "password": "xxxxxxxx" + }, "oid_list.conf": os.path.join( tmp_dir_name, "oid_list.conf"), @@ -199,7 +205,7 @@ def test_routers_list(mocker, client): 'inventory_provider.router_details.redis.StrictRedis', MockedRedis) mocker.patch( - 'inventory_provider.data_routes.redis.StrictRedis', + 'inventory_provider.routes.data.redis.StrictRedis', MockedRedis) rv = client.post( "data/routers",