WORKDIR /tmp
# copy NNG out of the CI builder nng
-COPY --from=nexus3.o-ran-sc.org:10004/bldr-debian-python-nng:2-py3.7-nng1.1.1 /usr/local/lib/libnng.so /usr/local/lib/libnng.so
+COPY --from=nexus3.o-ran-sc.org:10004/bldr-debian-python:3-py3.7-nng1.1.1 /usr/local/lib/libnng.so /usr/local/lib/libnng.so
# Installs RMr using debian package hosted at packagecloud.io
RUN wget --content-disposition https://packagecloud.io/o-ran-sc/master/packages/debian/stretch/rmr_1.0.36_amd64.deb/download.deb
logger = get_module_logger(__name__)
-def _get_needed_policy_info(policyname):
- """
- Get the needed info for a policy
- """
+def _get_policy_definition(policyname):
# Currently we read the manifest on each call, which would seem to allow updating A1 in place. Revisit this?
manifest = utils.get_ric_manifest()
for m in manifest["controls"]:
if m["name"] == policyname:
- schema = m["message_receives_payload_schema"] if "message_receives_payload_schema" in m else None
- return (
- utils.rmr_string_to_int(m["message_receives_rmr_type"]),
- schema,
- utils.rmr_string_to_int(m["message_sends_rmr_type"]),
- )
+ return m
raise exceptions.PolicyNotFound()
+def _get_needed_policy_info(policyname):
+ """
+ Get the needed info for a policy
+ """
+ m = _get_policy_definition(policyname)
+ return (
+ utils.rmr_string_to_int(m["message_receives_rmr_type"]),
+ m["message_receives_payload_schema"] if "message_receives_payload_schema" in m else None,
+ utils.rmr_string_to_int(m["message_sends_rmr_type"]),
+ )
+
+
+def _get_needed_policy_fetch_info(policyname):
+ """
+ Get the needed info for fetching a policy state
+ """
+ m = _get_policy_definition(policyname)
+ req_k = "control_state_request_rmr_type"
+ ack_k = "control_state_request_reply_rmr_type"
+ return (
+ utils.rmr_string_to_int(m[req_k]) if req_k in m else None,
+ utils.rmr_string_to_int(m[ack_k]) if ack_k in m else None,
+ )
+
+
def _try_func_return(func):
"""
generic caller that returns the apporp http response if exceptions are raised
return {"reason": "NO STATUS", "return_payload": rpj}, 502
+def _get_handler(policyname):
+ """
+ Handles policy GET
+ """
+ mtype_send, mtype_return = _get_needed_policy_fetch_info(policyname)
+
+ if not (mtype_send and mtype_return):
+ return "POLICY DOES NOT SUPPORT FETCHING", 400
+
+ # send rmr, wait for ACK
+ return_payload = a1rmr.send_ack_retry("", message_type=mtype_send, expected_ack_message_type=mtype_return)
+
+ # right now it is assumed that xapps respond with JSON payloads
+ try:
+ return (json.loads(return_payload), 200)
+ except json.decoder.JSONDecodeError:
+ return {"reason": "NOT JSON", "return_payload": return_payload}, 502
+
+
# Public
"""
Handles policy GET
"""
- return "", 501
+ return _try_func_return(lambda: _get_handler(policyname))
+
+
+def healthcheck_handler():
+ """
+ Handles healthcheck GET
+ Currently, this basically checks the server is alive.a1rmr
+ """
+ return "", 200
# ==================================================================================
openapi: 3.0.0
info:
- version: 0.8.0
+ version: 0.9.0
title: RIC A1
paths:
+ '/a1-p/healthcheck':
+ get:
+ description: >
+ perform a healthcheck on a1
+ tags:
+ - A1 Mediator
+ operationId: a1.controller.healthcheck_handler
+ responses:
+ 200:
+ description: >
+ a1 is healthy. Anything other than a 200 should be considered a1 as failing
+
'/ric/policies/{policyname}':
parameters:
- name: policyname
- A1 Mediator
operationId: a1.controller.get_handler
responses:
- '501':
+ '200':
description: >
- "future GET support has been pondered, but this is not currently implemented"U
+ The downstream component responsible for implementing this policy replied with a good response. Check the manifest for response details.
+ '400':
+ description: >
+ The downstream component for implementing this policy does not support policy fetching.
+ '404':
+ description: >
+ there is no policy with this name
+ '504':
+ description: >
+ the downstream component responsible for handling this policy did not respond (in time)
+
+
# 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: 0.8.4
+tag: 0.9.0
4. Barrages the server with apache bench
5. Tears everything down
+Unless you're a core A1 developer, you should probably stop here. The below instructions
+are for running A1 locally, without docker, and is much more involved (however useful when developing a1).
+
Running locally
===============
does this)
3. Create a ``local.rt`` file and copy it into ``/opt/route/local.rt``.
- Note, the example one in ``local_tests`` will need to be modified for
+ Note, the example one in ``integration_tests`` will need to be modified for
your scenario and machine.
4. Copy a ric manifest into ``/opt/ricmanifest.json`` and an rmr mapping
::
- cp tests/fixtures/ricmanifest.json /opt/ricmanifest.json cp
- tests/fixtures/rmr_string_int_mapping.txt
- /opt/rmr_string_int_mapping.txt
+ cp tests/fixtures/ricmanifest.json /opt/ricmanifest.json
+ cp tests/fixtures/rmr_string_int_mapping.txt /opt/rmr_string_int_mapping.txt
5. Then:
- sudo pip install –ignore-installed .; set -x LD_LIBRARY_PATH
- /usr/local/lib/; set -x RMR_SEED_RT /opt/route/local.rt ; set -x
- RMR_RCV_RETRY_INTERVAL 500; set -x RMR_RETRY_TIMES 10;
+ ::
+
+ sudo pip install -e .
+ set -x LD_LIBRARY_PATH /usr/local/lib/; set -x RMR_SEED_RT /opt/route/local.rt ; set -x RMR_RCV_RETRY_INTERVAL 500; set -x RMR_RETRY_TIMES 10;
/usr/bin/run.py
-Testing locally
-===============
There are also two test receivers in ``integration_tests`` you can run locally.
The first is meant to be used with the ``control_admission`` policy
curl -v -X PUT -H "Content-Type: application/json" -d '{}' localhost:10000/ric/policies/test_policy
curl -v -X PUT -H "Content-Type: application/json" -d '{ "enforce":true, "window_length":10, "blocking_rate":20, "trigger_threshold":10 }' localhost:10000/ric/policies/admission_control_policy
+ curl -v localhost:10000/ric/policies/admission_control_policy
+ curl -v localhost:10000/a1-p/healthcheck
Finally, there is a test “bombarder” that will flood A1 with messages
with good message types but bad transaction IDs, to test A1’s resilience
and this project adheres to `Semantic
Versioning <http://semver.org/>`__.
+[0.9.0] - 7/22/2019
+-------------------
+
+::
+
+ * Implement the GET on policies
+ * Add a new endpoint for healthcheck. NOTE, it has been decided by oran architecture documents that this policy interface should be named a1-p in all URLS. In a future release the existing URLs will be renamed (existing URLs were not changed in this release).
+
+
[0.8.4] - 7/16/2019
-------------------
apiVersion: v1
description: A1 Helm chart for Kubernetes
name: a1mediator
-version: 0.8.4
+version: 0.9.0
DC_ADM_INT_CONTROL:20000
DC_ADM_INT_CONTROL_ACK:20001
+DC_ADM_GET_POLICY: 20002
+DC_ADM_GET_POLICY_ACK: 20003
TEST_REQ:10000
TEST_ACK:10001
local.rt: |
newrt|start
rte|20000|testreceiverrmrservice:4560
+ rte|20001|{{ .Values.rmrservice.name }}:{{ .Values.rmrservice.port }}
+ rte|20002|testreceiverrmrservice:4560
+ rte|20003|{{ .Values.rmrservice.name }}:{{ .Values.rmrservice.port }}
rte|10000|delayreceiverrmrservice:4563
rte|10001|{{ .Values.rmrservice.name }}:{{ .Values.rmrservice.port }}
- rte|20001|{{ .Values.rmrservice.name }}:{{ .Values.rmrservice.port }}
newrt|end
rmr_string_int_mapping.txt: {{ tpl (.Files.Get "files/rmr_string_int_mapping.txt") . | quote }}
ricmanifest.json: {{ tpl (.Files.Get "files/ricmanifest.json") . | quote }}
livenessProbe:
httpGet:
- path: /ui
+ path: /healthcheck
port: http
readinessProbe:
httpGet:
- path: /ui
+ path: /healthcheck
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
"""
import time
-from rmr import rmr
import json
import os
+from rmr import rmr
PORT = os.environ.get("TEST_RCV_PORT", "4560")
RETURN_MINT = int(os.environ.get("TEST_RCV_RETURN_MINT", 20001))
+RETURN_MINT_FETCH = int(os.environ.get("TEST_RCV_RETURN_MINT", 20003))
DELAY = int(os.environ.get("TEST_RCV_SEC_DELAY", 0))
PAYLOAD_RETURNED = json.loads(
os.environ.get("TEST_RCV_RETURN_PAYLOAD", '{"ACK_FROM": "ADMISSION_CONTROL", "status": "SUCCESS"}')
else:
print("Message received!: {}".format(summary))
+ # if this was a policy fetch (request int =20002), override the payload and return int
+ if summary["message type"] == 20002:
+ PAYLOAD_RETURNED = {"mock return from FETCH": "pretend policy is here"}
+ RETURN_MINT = 20003
+
val = json.dumps(PAYLOAD_RETURNED).encode("utf-8")
rmr.set_payload_and_length(val, sbuf)
sbuf.contents.mtype = RETURN_MINT
# test_a1.tavern.yaml
+test_name: test healthcheck
+
+stages:
+ - name: test the a1 healthcheck
+ request:
+ url: http://localhost:10000/a1-p/healthcheck
+ method: GET
+ response:
+ status_code: 200
+
+
---
test_name: test delayed policy
ACK_FROM: ADMISSION_CONTROL
status: SUCCESS
+ - name: test the admission control policy get
+ request:
+ url: http://localhost:10000/ric/policies/admission_control_policy
+ method: GET
+ response:
+ status_code: 200
+ body:
+ mock return from FETCH: pretend policy is here
+
---
test_name: bad_requests
not: "welcome"
response:
status_code: 400
+
+ - name: test policy doesnt support fetch
+ request:
+ url: http://localhost:10000/ric/policies/test_policy
+ method: GET
+ response:
+ status_code: 400
newrt|start
rte|10000|devarchwork:4563
-rte|20000|devarchwork:4560
rte|10001|devarchwork:4562
+rte|20000|devarchwork:4560
+rte|20002|devarchwork:4560
rte|20001|devarchwork:4562
+rte|20003|devarchwork:4562
newrt|end
newrt|start
rte|20000|{{ .Values.testrmrservice.name }}:{{ .Values.testrmrservice.port }}
rte|20001|a1rmrservice:4562
+ rte|20002|{{ .Values.testrmrservice.name }}:{{ .Values.testrmrservice.port }}
+ rte|20003|a1rmrservice:4562
newrt|end
---
setup(
name="a1",
- version="0.8.4",
+ version="0.9.0",
packages=find_packages(exclude=["tests.*", "tests"]),
author="Tommy Carpenter",
description="RIC A1 Mediator for policy/intent changes",
- url="",
+ url="https://gerrit.o-ran-sc.org/r/admin/repos/ric-plt/a1",
entry_points={"console_scripts": ["run.py=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", "rmr>=0.10.0"],
DC_ADM_INT_CONTROL:20000
DC_ADM_INT_CONTROL_ACK:20001
+DC_ADM_GET_POLICY: 20002
+DC_ADM_GET_POLICY_ACK: 20003
TEST_REQ:10000
TEST_ACK:10001
return f
-# Actual Tests
-
-
def _test_put_patch(monkeypatch):
testing_helpers.patch_all(monkeypatch)
monkeypatch.setattr("rmr.rmr.rmr_send_msg", rmr_mocks.send_mock_generator(0)) # good sends for this whole batch
monkeypatch.setattr("rmr.rmr.generate_and_set_transaction_id", fake_set_transactionid)
+# Actual Tests
+
+
+def test_policy_get(client, monkeypatch):
+ """
+ test policy GET
+ """
+ _test_put_patch(monkeypatch)
+ monkeypatch.setattr(
+ "a1.a1rmr._dequeue_all_waiting_messages",
+ _fake_dequeue(monkeypatch, msg_payload={"GET ack": "pretend policy is here"}, msg_type=20003),
+ )
+ res = client.get("/ric/policies/admission_control_policy")
+ assert res.status_code == 200
+ assert res.json == {"GET ack": "pretend policy is here"}
+
+
+def test_policy_get_unsupported(client, monkeypatch):
+ """
+ test policy GET
+ """
+ testing_helpers.patch_all(monkeypatch, nofetch=True)
+ res = client.get("/ric/policies/admission_control_policy")
+ assert res.status_code == 400
+ assert res.data == b'"POLICY DOES NOT SUPPORT FETCHING"\n'
+
+
def test_xapp_put_good(client, monkeypatch):
""" test policy put good"""
_test_put_patch(monkeypatch)
res = client.put("/ric/policies/admission_control_policy", json=testing_helpers.good_payload())
assert res.status_code == 500
assert res.data == b'"A1 does not have a mapping for the desired rmr string. report this!"\n'
+
+
+def test_healthcheck(client):
+ """
+ test healthcheck
+ """
+ res = client.get("/a1-p/healthcheck")
+ assert res.status_code == 200
return "{0}/fixtures/{1}".format(cur_dir, name)
-def patch_all(monkeypatch, nonexisting_rmr=False):
+def patch_all(monkeypatch, nonexisting_rmr=False, nofetch=False):
rmr_mocks.patch_rmr(monkeypatch)
# patch manifest
man = json.loads(open(_get_fixture_path("ricmanifest.json"), "r").read())
if nonexisting_rmr:
man["controls"][0]["message_receives_rmr_type"] = "DARKNESS"
+
+ if nofetch:
+ del man["controls"][0]["control_state_request_rmr_type"]
+
monkeypatch.setattr("a1.utils.get_ric_manifest", lambda: man)
# patch rmr mapping