Add Prometheus metrics and query endpoint 81/3881/5
authorLott, Christopher (cl778h) <cl778h@att.com>
Wed, 27 May 2020 19:56:20 +0000 (15:56 -0400)
committerLott, Christopher (cl778h) <cl778h@att.com>
Fri, 29 May 2020 13:12:01 +0000 (09:12 -0400)
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) <cl778h@att.com>
Change-Id: I44bc5346b627b27ba5b344187223dd0323b32a2c

14 files changed:
.bumpversion.cfg
Dockerfile
a1/__init__.py
a1/controller.py
container-tag.yaml
docs/developer-guide.rst
docs/installation-guide.rst
docs/overview.rst
docs/release-notes.rst
docs/user-guide-api.rst
integration_tests/a1mediator/Chart.yaml
setup.py
tests/test_controller.py
tox.ini

index c5783f8..c4d0a5f 100644 (file)
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 2.1.9
+current_version = 2.2.0
 commit = False
 tag = False
 
index a3afffe..cf79e17 100644 (file)
@@ -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
index 76a9dac..f7155a0 100644 (file)
 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)
index 118a67e..9de9c00 100644 (file)
@@ -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)
index fe94776..5781531 100644 (file)
@@ -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
index 2f2447c..9c0132c 100644 (file)
@@ -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
 -------------------
 
index 0b1de6a..e04fdf2 100644 (file)
@@ -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
 ~~~~~~~~~~~~~~~
index 46ea3ff..450e92f 100644 (file)
@@ -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
index babfbef..1ac7348 100644 (file)
@@ -11,6 +11,13 @@ The format is based on `Keep a Changelog <http://keepachangelog.com/>`__
 and this project adheres to `Semantic Versioning <http://semver.org/>`__.
 
 
+[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
 --------------------
 
index 2dcd31e..5136d06 100644 (file)
@@ -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
 -------------------------
 
index 805e8cb..1a4eb5b 100644 (file)
@@ -1,4 +1,4 @@
 apiVersion: v1
 description: A1 Helm chart for Kubernetes
 name: a1mediator
-version: 2.1.9
+version: 2.2.0
index f934d9d..5c44b0c 100644 (file)
--- 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"]},
 )
index f243bf7..9a73ea8 100644 (file)
@@ -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 (file)
--- 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 =