From: Lott, Christopher (cl778h) Date: Wed, 27 May 2020 19:56:20 +0000 (-0400) Subject: Add Prometheus metrics and query endpoint X-Git-Tag: 2.4.0~5 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;h=c91a4a172446229f4b11e76547ffdf454528b30d;p=ric-plt%2Fa1.git Add Prometheus metrics and query endpoint Extend controller with counters to measure activity Add /a1-p/metrics endpoint to expose measures in Prometheus format Document new environment variable prometheus_multiproc_dir Issue-ID: RIC-353 Signed-off-by: Lott, Christopher (cl778h) Change-Id: I44bc5346b627b27ba5b344187223dd0323b32a2c --- diff --git a/.bumpversion.cfg b/.bumpversion.cfg index c5783f8..c4d0a5f 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 2.1.9 +current_version = 2.2.0 commit = False tag = False diff --git a/Dockerfile b/Dockerfile index a3afffe..cf79e17 100644 --- a/Dockerfile +++ b/Dockerfile @@ -60,7 +60,9 @@ ENV RMR_SEED_RT /opt/route/local.rt ENV USE_FAKE_SDL False ENV PYTHONUNBUFFERED 1 # pip installs console script to ~/.local/bin so PATH is critical -ENV PATH=/home/a1user/.local/bin:$PATH +ENV PATH /home/a1user/.local/bin:$PATH +# prometheus client gathers data here +ENV prometheus_multiproc_dir /tmp # Run! CMD run-a1 diff --git a/a1/__init__.py b/a1/__init__.py index 76a9dac..f7155a0 100644 --- a/a1/__init__.py +++ b/a1/__init__.py @@ -18,7 +18,19 @@ contains the app; broken out here for ease of unit testing """ import connexion +from prometheus_client import CollectorRegistry, generate_latest, multiprocess app = connexion.App(__name__, specification_dir=".") app.add_api("openapi.yaml", arguments={"title": "My Title"}) + + +# python decorators feel like black magic to me +@app.app.route('/a1-p/metrics', methods=['GET']) +def metrics(): # pylint: disable=unused-variable + # /metrics API shouldn't be visible in the API documentation, + # hence it's added here in the create_app step + # requires environment variable prometheus_multiproc_dir + registry = CollectorRegistry() + multiprocess.MultiProcessCollector(registry) + return generate_latest(registry) diff --git a/a1/controller.py b/a1/controller.py index 118a67e..9de9c00 100644 --- a/a1/controller.py +++ b/a1/controller.py @@ -20,12 +20,14 @@ Main a1 controller from jsonschema import validate from jsonschema.exceptions import ValidationError import connexion +from prometheus_client import Counter from mdclogpy import Logger from ricsdl.exceptions import RejectedByBackend, NotConnected, BackendError from a1 import a1rmr, exceptions, data mdc_logger = Logger(name=__name__) +request_counter = Counter('policy_requests', 'Policy type and instance requests', ['action', 'target']) def _log_build_http_resp(exception, http_resp_code): @@ -95,6 +97,7 @@ def create_policy_type(policy_type_id): """ Handles PUT /a1-p/policytypes/policy_type_id """ + request_counter.labels(action='create', target='policy_type').inc() def put_type_handler(): data.store_policy_type(policy_type_id, body) @@ -116,6 +119,7 @@ def delete_policy_type(policy_type_id): """ Handles DELETE /a1-p/policytypes/policy_type_id """ + request_counter.labels(action='delete', target='policy_type').inc() def delete_policy_type_handler(): data.delete_policy_type(policy_type_id) @@ -158,6 +162,7 @@ def create_or_replace_policy_instance(policy_type_id, policy_instance_id): """ Handles PUT /a1-p/policytypes/polidyid/policies/policy_instance_id """ + request_counter.labels(action='create', target='policy_inst').inc() instance = connexion.request.json def put_instance_handler(): @@ -185,6 +190,7 @@ def delete_policy_instance(policy_type_id, policy_instance_id): """ Handles DELETE /a1-p/policytypes/polidyid/policies/policy_instance_id """ + request_counter.labels(action='delete', target='policy_inst').inc() def delete_instance_handler(): data.delete_policy_instance(policy_type_id, policy_instance_id) diff --git a/container-tag.yaml b/container-tag.yaml index fe94776..5781531 100644 --- a/container-tag.yaml +++ b/container-tag.yaml @@ -1,4 +1,4 @@ # The Jenkins job uses this string for the tag in the image name # for example nexus3.o-ran-sc.org:10004/my-image-name:my-tag --- -tag: 2.1.9 +tag: 2.2.0 diff --git a/docs/developer-guide.rst b/docs/developer-guide.rst index 2f2447c..9c0132c 100644 --- a/docs/developer-guide.rst +++ b/docs/developer-guide.rst @@ -18,6 +18,8 @@ depends on these third-party packages and technologies: - Connexion - Flask with Gevent serving - Swagger +- Prometheus + Version bumping A1 ------------------ @@ -61,6 +63,7 @@ In addition these items in this repo must be kept in sync: #. ``integration_tests/install_rmr.sh`` is a useful script for a variety of local testing. + Version bumping Python ---------------------- @@ -71,6 +74,24 @@ recently done to move from 3.7 to 3.8, update these files: #. ``Dockerfile-Unit-Test`` #. ``tox.ini`` + +Running A1 Standalone +--------------------- + +The A1 container can be run standalone, which means using an in-memory mock +version of SDL and a static route table. The host machine must have the RMR +library and the environment must define the variable `prometheus_multiproc_dir` +with a value like /tmp. Alternately, use the following command to run A1 as +a Docker container, using a route table mounted as a file from this git +repository and exposing the server's HTTP port on the Docker host:: + + docker run -e USE_FAKE_SDL=True -p 10000:10000 -v `pwd`:/opt/route [DOCKER_IMAGE_ID_HERE] + +Then test the server with an invocation such as this:: + + curl localhost:10000/a1-p/healthcheck + + Unit Testing ------------ @@ -94,6 +115,7 @@ less nice because you don't get the pretty HTML) docker build --no-cache -f Dockerfile-Unit-Test . + Integration testing ------------------- diff --git a/docs/installation-guide.rst b/docs/installation-guide.rst index 0b1de6a..e04fdf2 100644 --- a/docs/installation-guide.rst +++ b/docs/installation-guide.rst @@ -9,8 +9,8 @@ Installation Guide :depth: 3 :local: -Optional ENV Variables ----------------------- +Environment Variables +--------------------- You can set the following environment variables when launching a container to change the A1 behavior: @@ -22,12 +22,19 @@ You can set the following environment variables when launching a container to ch 4. ``USE_FAKE_SDL``: This allows testing of the A1 feature without a DBaaS SDL container. The default is False. -K8S ---- -The "real" helm chart for A1 is in the LF it/dep repo. That repo holds all of the helm charts for the RIC platform. There is a helm chart in `integration_tests` here for running the integration tests as discussed above. +5. ``prometheus_multiproc_dir``: The directory where Prometheus gathers metrics. The default is /tmp. -Local Docker -------------- + +Kubernetes Deployment +--------------------- +The official Helm chart for the A1 Mediator is in a deployment repository, which holds all of the Helm charts +for the RIC platform. There is a helm chart in `integration_tests` here for running the integration tests as +discussed above. + +Local Deployment +---------------- + +Build and run the A1 mediator locally using the docker CLI as follows. Build the image ~~~~~~~~~~~~~~~ diff --git a/docs/overview.rst b/docs/overview.rst index 46ea3ff..450e92f 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -4,6 +4,10 @@ Overview ======== +.. contents:: + :depth: 3 + :local: + The RAN Intelligent Controller (RIC) Platform's A1 Mediator component listens for policy type and policy instance requests sent via HTTP (the "northbound" interface), and publishes those requests to running diff --git a/docs/release-notes.rst b/docs/release-notes.rst index babfbef..1ac7348 100644 --- a/docs/release-notes.rst +++ b/docs/release-notes.rst @@ -11,6 +11,13 @@ The format is based on `Keep a Changelog `__ and this project adheres to `Semantic Versioning `__. +[2.2.0] - 2020-05-28 +-------------------- + +* Add counters of create/update/delete actions on policy types and instances +* Add Prometheus /metrics endpoint to report counter data + + [2.1.9] - 2020-05-26 -------------------- diff --git a/docs/user-guide-api.rst b/docs/user-guide-api.rst index 2dcd31e..5136d06 100644 --- a/docs/user-guide-api.rst +++ b/docs/user-guide-api.rst @@ -4,6 +4,10 @@ User Guide and APIs =================== +.. contents:: + :depth: 3 + :local: + This document explains how to communicate with the A1 Mediator. Information for maintainers of this platform component is in the Developer Guide. @@ -33,6 +37,12 @@ a single integer value: } +For example, if you put the JSON above into a file called "create.json" you can use +the curl command-line tool to send the request:: + + curl -X PUT --header "Content-Type: application/json" --data-raw @create.json http://localhost/a1-p/policytypes/20008 + + Send the following JSON to create an instance of policy type 20008: .. code-block:: yaml @@ -42,6 +52,11 @@ Send the following JSON to create an instance of policy type 20008: } +For example, you can use the curl command-line tool to send this request:: + + curl -X PUT --header "Content-Type: application/json" --data '{"threshold" : 5}' http://localhost/a1-p/policytypes/20008/policies/tsapolicy145 + + Integrating Xapps with A1 ------------------------- diff --git a/integration_tests/a1mediator/Chart.yaml b/integration_tests/a1mediator/Chart.yaml index 805e8cb..1a4eb5b 100644 --- a/integration_tests/a1mediator/Chart.yaml +++ b/integration_tests/a1mediator/Chart.yaml @@ -1,4 +1,4 @@ apiVersion: v1 description: A1 Helm chart for Kubernetes name: a1mediator -version: 2.1.9 +version: 2.2.0 diff --git a/setup.py b/setup.py index f934d9d..5c44b0c 100644 --- a/setup.py +++ b/setup.py @@ -18,13 +18,13 @@ from setuptools import setup, find_packages setup( name="a1", - version="2.1.9", + version="2.2.0", packages=find_packages(exclude=["tests.*", "tests"]), author="Tommy Carpenter", description="RIC A1 Mediator for policy/intent changes", url="https://gerrit.o-ran-sc.org/r/admin/repos/ric-plt/a1", entry_points={"console_scripts": ["run-a1=a1.run:main"]}, # we require jsonschema, should be in that list, but connexion already requires a specific version of it - install_requires=["requests", "Flask", "connexion[swagger-ui]", "gevent", "mdclogpy", "ricxappframe>=1.0.0,<2.0.0"], + install_requires=["requests", "Flask", "connexion[swagger-ui]", "gevent", "prometheus-client", "mdclogpy", "ricxappframe>=1.0.0,<2.0.0"], package_data={"a1": ["openapi.yaml"]}, ) diff --git a/tests/test_controller.py b/tests/test_controller.py index f243bf7..9a73ea8 100644 --- a/tests/test_controller.py +++ b/tests/test_controller.py @@ -400,6 +400,14 @@ def test_healthcheck(client): assert res.status_code == 200 +def test_metrics(client): + """ + test Prometheus metrics + """ + res = client.get("/a1-p/metrics") + assert res.status_code == 200 + + def teardown_module(): """module teardown""" a1rmr.stop_rmr_thread() diff --git a/tox.ini b/tox.ini index 77613b8..56d7d4b 100644 --- a/tox.ini +++ b/tox.ini @@ -35,6 +35,7 @@ setenv = A1_RMR_RETRY_TIMES = 2 INSTANCE_DELETE_NO_RESP_TTL = 3 INSTANCE_DELETE_RESP_TTL = 3 + prometheus_multiproc_dir = /tmp # Note, before this will work, for the first time on that machine, run ./install_deps.sh commands =