From 24514469850dc9a543869f132385633039c5f5a6 Mon Sep 17 00:00:00 2001 From: Tommy Carpenter Date: Tue, 16 Jul 2019 11:25:52 -0400 Subject: [PATCH] Fix missing 400 impl, update test manifest Issue-ID: RICPLT-1638 Change-Id: Iff6de7b64a0dd94c6653eff5312ec84c1cf2bced Signed-off-by: Tommy Carpenter --- a1/controller.py | 4 ++ container-tag.yaml | 2 +- docs/developer-guide.rst | 8 ++-- docs/release-notes.rst | 10 +++++ install_deps.sh | 6 +++ integration_tests/a1mediator/Chart.yaml | 2 +- .../a1mediator/files/ricmanifest.json | 49 +++++++++++++++------- .../a1mediator/files/rmr_string_int_mapping.txt | 4 +- integration_tests/putdata | 7 +++- integration_tests/test_a1.tavern.yaml | 23 +++++++--- setup.py | 2 +- tests/fixtures/ricmanifest.json | 49 +++++++++++++++------- tests/fixtures/rmr_string_int_mapping.txt | 4 +- tests/test_controller.py | 48 ++++++++++----------- tests/testing_helpers.py | 2 +- tox-integration.ini | 2 +- tox.ini | 4 +- 17 files changed, 151 insertions(+), 75 deletions(-) create mode 100755 install_deps.sh diff --git a/a1/controller.py b/a1/controller.py index ca9618b..b3bbcdb 100644 --- a/a1/controller.py +++ b/a1/controller.py @@ -17,6 +17,7 @@ from flask import Response import connexion import json +from jsonschema.exceptions import ValidationError from a1 import get_module_logger from a1 import a1rmr, exceptions, utils @@ -47,6 +48,9 @@ def _try_func_return(func): """ try: return func() + except ValidationError as exc: + logger.exception(exc) + return "", 400 except exceptions.PolicyNotFound as exc: logger.exception(exc) return "", 404 diff --git a/container-tag.yaml b/container-tag.yaml index 623599d..d3e22b4 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: 0.8.3 +tag: 0.8.4 diff --git a/docs/developer-guide.rst b/docs/developer-guide.rst index 60c1d9a..5209cd3 100644 --- a/docs/developer-guide.rst +++ b/docs/developer-guide.rst @@ -45,7 +45,8 @@ This project follows semver. When changes are made, the versions are in: Unit Testing ============ -Note, this requires rmr to be installed on the system executing the tests. Also, this requires the python packages ``tox`` and ``pytest``. +Note, before this will work, for the first time on the machine running the tests, run ``./install_deps.sh``. This is only needed once on the machine. +Also, this requires the python packages ``tox`` and ``pytest``. :: @@ -89,8 +90,7 @@ This script: Running locally =============== -1. This requires that RMR is installed on the base system. (the - Dockerfile does this when running in Docker) +1. Before this will work, for the first time on that machine, run ``./install_deps.sh`` 2. It also requires rmr-python >= 0.10.1 installed. (The dockerfile also does this) @@ -145,7 +145,7 @@ while it is sleeping, and both responses should be correct. :: 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 '{"dc_admission_start_time": "10:00:00", "dc_admission_end_time": "11:00:00"}' localhost:10000/ric/policies/control_admission_time + 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 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 diff --git a/docs/release-notes.rst b/docs/release-notes.rst index 8cfeee6..3008efb 100644 --- a/docs/release-notes.rst +++ b/docs/release-notes.rst @@ -24,6 +24,16 @@ The format is based on `Keep a Changelog `__ and this project adheres to `Semantic Versioning `__. +[0.8.4] - 7/16/2019 +------------------- + +:: + + * Fix the 400, which was in the API, but wasn't actually implemented + * Update the test fixture manifests to reflect the latest adm control, paves way for next feature coming which is a policy GET + + + [0.8.3] - 6/18/2019 ------------------- diff --git a/install_deps.sh b/install_deps.sh new file mode 100755 index 0000000..f7b3660 --- /dev/null +++ b/install_deps.sh @@ -0,0 +1,6 @@ +#!/bin/sh +git clone https://gerrit.oran-osc.org/r/ric-plt/lib/rmr \ + && cd rmr \ + && mkdir .build; cd .build; cmake .. -DPACK_EXTERNALS=1; sudo make install \ + && cd ../.. \ + && rm -rf rmr diff --git a/integration_tests/a1mediator/Chart.yaml b/integration_tests/a1mediator/Chart.yaml index ab5ee57..17253ae 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: 0.8.3 +version: 0.8.4 diff --git a/integration_tests/a1mediator/files/ricmanifest.json b/integration_tests/a1mediator/files/ricmanifest.json index 211a115..300d7e5 100644 --- a/integration_tests/a1mediator/files/ricmanifest.json +++ b/integration_tests/a1mediator/files/ricmanifest.json @@ -1,28 +1,43 @@ { "controls":[ { - "name":"control_admission_time", - "description":"time period to allow dual connection", - "message_receives_rmr_type":"DC_ADMISSION_INTERVAL_CONTROL", + "name":"admission_control_policy", + "description":"various parameters to control admission of dual connection", + "control_state_request_rmr_type":"DC_ADM_GET_POLICY", + "control_state_request_reply_rmr_type":"DC_ADM_GET_POLICY_ACK", + "message_receives_rmr_type":"DC_ADM_INT_CONTROL", "message_receives_payload_schema":{ "$schema":"http://json-schema.org/draft-07/schema#", "type":"object", "properties":{ - "dc_admission_start_time":{ - "type":"string", - "pattern":"^[0-9]{2}:[0-9]{2}:[0-9]{2}$" + "enforce":{ + "type":"boolean", + "default":true }, - "dc_admission_end_time":{ - "type":"string", - "pattern":"^[0-9]{2}:[0-9]{2}:[0-9]{2}$" + "window_length":{ + "type":"integer", + "default":1, + "minimum":1, + "maximum":60, + "description":"Sliding window length (in minutes)" + }, + "blocking_rate":{ + "type":"number", + "default":10, + "minimum":1, + "maximum":100, + "description":"% Connections to block" + }, + "trigger_threshold":{ + "type":"integer", + "default":10, + "minimum":1, + "description":"Minimum number of events in window to trigger blocking" } }, - "required":[ - "dc_admission_start_time", - "dc_admission_end_time" - ] + "additionalProperties":false }, - "message_sends_rmr_type":"DC_ADMISSION_INTERVAL_CONTROL_ACK", + "message_sends_rmr_type":"DC_ADM_INT_CONTROL_ACK", "message_sends_payload_schema":{ "$schema":"http://json-schema.org/draft-07/schema#", "type":"object", @@ -37,7 +52,11 @@ "message":{ "type":"string" } - } + }, + "required":[ + "status" + ], + "additionalProperties":false } }, { diff --git a/integration_tests/a1mediator/files/rmr_string_int_mapping.txt b/integration_tests/a1mediator/files/rmr_string_int_mapping.txt index ff1479d..1f194b8 100644 --- a/integration_tests/a1mediator/files/rmr_string_int_mapping.txt +++ b/integration_tests/a1mediator/files/rmr_string_int_mapping.txt @@ -1,4 +1,4 @@ -DC_ADMISSION_INTERVAL_CONTROL:20000 -DC_ADMISSION_INTERVAL_CONTROL_ACK:20001 +DC_ADM_INT_CONTROL:20000 +DC_ADM_INT_CONTROL_ACK:20001 TEST_REQ:10000 TEST_ACK:10001 diff --git a/integration_tests/putdata b/integration_tests/putdata index 329851c..a2f8d11 100644 --- a/integration_tests/putdata +++ b/integration_tests/putdata @@ -1 +1,6 @@ -{"dc_admission_start_time": "10:00:00", "dc_admission_end_time": "11:00:00"} +{ + "enforce":true, + "window_length":10, + "blocking_rate":20, + "trigger_threshold":10 +} diff --git a/integration_tests/test_a1.tavern.yaml b/integration_tests/test_a1.tavern.yaml index 5ecb69a..8409056 100644 --- a/integration_tests/test_a1.tavern.yaml +++ b/integration_tests/test_a1.tavern.yaml @@ -27,11 +27,13 @@ test_name: test admission control stages: - name: test the admission control policy request: - url: http://localhost:10000/ric/policies/control_admission_time + url: http://localhost:10000/ric/policies/admission_control_policy method: PUT json: - dc_admission_start_time: "10:00:00" - dc_admission_end_time: "11:00:00" + enforce: true + window_length: 10 + blocking_rate: 20 + trigger_threshold: 10 headers: content-type: application/json response: @@ -56,15 +58,26 @@ stages: response: status_code: 404 + - name: bad body for admission control policy + request: + url: http://localhost:10000/ric/policies/admission_control_policy + method: PUT + json: + not: "expected" + headers: + content-type: application/json + response: + status_code: 400 + - name: not a json request: - url: http://localhost:10000/ric/policies/control_admission_time + url: http://localhost:10000/ric/policies/admission_control_policy method: PUT data: "asdf" response: status_code: 415 - - name: body not expected + - name: bad body for test policy request: url: http://localhost:10000/ric/policies/test_policy method: PUT diff --git a/setup.py b/setup.py index 66be986..b55ab6c 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ from setuptools import setup, find_packages setup( name="a1", - version="0.8.3", + version="0.8.4", packages=find_packages(exclude=["tests.*", "tests"]), author="Tommy Carpenter", description="RIC A1 Mediator for policy/intent changes", diff --git a/tests/fixtures/ricmanifest.json b/tests/fixtures/ricmanifest.json index 211a115..300d7e5 100644 --- a/tests/fixtures/ricmanifest.json +++ b/tests/fixtures/ricmanifest.json @@ -1,28 +1,43 @@ { "controls":[ { - "name":"control_admission_time", - "description":"time period to allow dual connection", - "message_receives_rmr_type":"DC_ADMISSION_INTERVAL_CONTROL", + "name":"admission_control_policy", + "description":"various parameters to control admission of dual connection", + "control_state_request_rmr_type":"DC_ADM_GET_POLICY", + "control_state_request_reply_rmr_type":"DC_ADM_GET_POLICY_ACK", + "message_receives_rmr_type":"DC_ADM_INT_CONTROL", "message_receives_payload_schema":{ "$schema":"http://json-schema.org/draft-07/schema#", "type":"object", "properties":{ - "dc_admission_start_time":{ - "type":"string", - "pattern":"^[0-9]{2}:[0-9]{2}:[0-9]{2}$" + "enforce":{ + "type":"boolean", + "default":true }, - "dc_admission_end_time":{ - "type":"string", - "pattern":"^[0-9]{2}:[0-9]{2}:[0-9]{2}$" + "window_length":{ + "type":"integer", + "default":1, + "minimum":1, + "maximum":60, + "description":"Sliding window length (in minutes)" + }, + "blocking_rate":{ + "type":"number", + "default":10, + "minimum":1, + "maximum":100, + "description":"% Connections to block" + }, + "trigger_threshold":{ + "type":"integer", + "default":10, + "minimum":1, + "description":"Minimum number of events in window to trigger blocking" } }, - "required":[ - "dc_admission_start_time", - "dc_admission_end_time" - ] + "additionalProperties":false }, - "message_sends_rmr_type":"DC_ADMISSION_INTERVAL_CONTROL_ACK", + "message_sends_rmr_type":"DC_ADM_INT_CONTROL_ACK", "message_sends_payload_schema":{ "$schema":"http://json-schema.org/draft-07/schema#", "type":"object", @@ -37,7 +52,11 @@ "message":{ "type":"string" } - } + }, + "required":[ + "status" + ], + "additionalProperties":false } }, { diff --git a/tests/fixtures/rmr_string_int_mapping.txt b/tests/fixtures/rmr_string_int_mapping.txt index ff1479d..1f194b8 100644 --- a/tests/fixtures/rmr_string_int_mapping.txt +++ b/tests/fixtures/rmr_string_int_mapping.txt @@ -1,4 +1,4 @@ -DC_ADMISSION_INTERVAL_CONTROL:20000 -DC_ADMISSION_INTERVAL_CONTROL_ACK:20001 +DC_ADM_INT_CONTROL:20000 +DC_ADM_INT_CONTROL_ACK:20001 TEST_REQ:10000 TEST_ACK:10001 diff --git a/tests/test_controller.py b/tests/test_controller.py index 680f586..5370207 100644 --- a/tests/test_controller.py +++ b/tests/test_controller.py @@ -90,6 +90,15 @@ def _test_put_patch(monkeypatch): monkeypatch.setattr("rmr.rmr.generate_and_set_transaction_id", fake_set_transactionid) +def test_xapp_put_good(client, monkeypatch): + """ test policy put good""" + _test_put_patch(monkeypatch) + monkeypatch.setattr("a1.a1rmr._dequeue_all_waiting_messages", _fake_dequeue(monkeypatch)) + res = client.put("/ric/policies/admission_control_policy", json=testing_helpers.good_payload()) + assert res.status_code == 200 + assert res.json == {"status": "SUCCESS", "foo": "bar"} + + def test_xapp_put_bad(client, monkeypatch): """Test policy put fails""" _test_put_patch(monkeypatch) @@ -97,14 +106,14 @@ def test_xapp_put_bad(client, monkeypatch): monkeypatch.setattr( "a1.a1rmr._dequeue_all_waiting_messages", _fake_dequeue(monkeypatch, msg_payload={"status": "FAIL", "foo": "bar"}) ) - res = client.put("/ric/policies/control_admission_time", json=testing_helpers.good_payload()) + res = client.put("/ric/policies/admission_control_policy", json=testing_helpers.good_payload()) assert res.status_code == 502 assert res.json["reason"] == "BAD STATUS" assert res.json["return_payload"] == {"status": "FAIL", "foo": "bar"} # return from policy handler has no status field monkeypatch.setattr("a1.a1rmr._dequeue_all_waiting_messages", _fake_dequeue(monkeypatch, msg_payload={"foo": "bar"})) - res = client.put("/ric/policies/control_admission_time", json=testing_helpers.good_payload()) + res = client.put("/ric/policies/admission_control_policy", json=testing_helpers.good_payload()) assert res.status_code == 502 assert res.json["reason"] == "NO STATUS" assert res.json["return_payload"] == {"foo": "bar"} @@ -113,48 +122,41 @@ def test_xapp_put_bad(client, monkeypatch): monkeypatch.setattr( "a1.a1rmr._dequeue_all_waiting_messages", _fake_dequeue(monkeypatch, msg_payload="booger", jsonb=False) ) - res = client.put("/ric/policies/control_admission_time", json=testing_helpers.good_payload()) + res = client.put("/ric/policies/admission_control_policy", json=testing_helpers.good_payload()) assert res.status_code == 502 assert res.json["reason"] == "NOT JSON" assert res.json["return_payload"] == "booger" # bad type monkeypatch.setattr("a1.a1rmr._dequeue_all_waiting_messages", _fake_dequeue(monkeypatch, msg_type=666)) - res = client.put("/ric/policies/control_admission_time", json=testing_helpers.good_payload()) + res = client.put("/ric/policies/admission_control_policy", json=testing_helpers.good_payload()) assert res.status_code == 504 assert res.data == b"\"A1 was expecting an ACK back but it didn't receive one or didn't recieve the expected ACK\"\n" # bad state monkeypatch.setattr("a1.a1rmr._dequeue_all_waiting_messages", _fake_dequeue(monkeypatch, msg_state=666)) - res = client.put("/ric/policies/control_admission_time", json=testing_helpers.good_payload()) + res = client.put("/ric/policies/admission_control_policy", json=testing_helpers.good_payload()) assert res.status_code == 504 assert res.data == b"\"A1 was expecting an ACK back but it didn't receive one or didn't recieve the expected ACK\"\n" -def test_xapp_put_good(client, monkeypatch): - """ test policy put good""" - _test_put_patch(monkeypatch) - - # do a good one - monkeypatch.setattr("a1.a1rmr._dequeue_all_waiting_messages", _fake_dequeue(monkeypatch)) - res = client.put("/ric/policies/control_admission_time", json=testing_helpers.good_payload()) - assert res.status_code == 200 - assert res.json == {"status": "SUCCESS", "foo": "bar"} - - def test_xapp_put_bad_send(client, monkeypatch): """ Test bad send failures """ testing_helpers.patch_all(monkeypatch) + monkeypatch.setattr("a1.a1rmr._dequeue_all_waiting_messages", _fake_dequeue(monkeypatch)) + res = client.put("/ric/policies/admission_control_policy", json={"not": "expected"}) + assert res.status_code == 400 + monkeypatch.setattr("rmr.rmr.rmr_send_msg", rmr_mocks.send_mock_generator(10)) - res = client.put("/ric/policies/control_admission_time", json=testing_helpers.good_payload()) + res = client.put("/ric/policies/admission_control_policy", json=testing_helpers.good_payload()) assert res.status_code == 504 assert res.data == b'"A1 was unable to send a needed message to a downstream subscriber"\n' monkeypatch.setattr("rmr.rmr.rmr_send_msg", rmr_mocks.send_mock_generator(5)) - res = client.put("/ric/policies/control_admission_time", json=testing_helpers.good_payload()) + res = client.put("/ric/policies/admission_control_policy", json=testing_helpers.good_payload()) assert res.status_code == 504 assert res.data == b'"A1 was unable to send a needed message to a downstream subscriber"\n' @@ -168,7 +170,7 @@ def test_bad_requests(client, monkeypatch): assert res.status_code == 404 # bad media type - res = client.put("/ric/policies/control_admission_time", data="notajson") + res = client.put("/ric/policies/admission_control_policy", data="notajson") assert res.status_code == 415 # test a PUT body against a poliucy not expecting one @@ -187,9 +189,7 @@ def test_missing_manifest(client, monkeypatch): monkeypatch.setattr("a1.utils.get_ric_manifest", f) - res = client.put( - "/ric/policies/control_admission_time", json=testing_helpers.good_payload(), headers={"Content-Type": "text/plain"} - ) + res = client.put("/ric/policies/admission_control_policy", json=testing_helpers.good_payload()) assert res.status_code == 500 assert res.data == b'"A1 was unable to find the required RIC manifest. report this!"\n' @@ -199,8 +199,6 @@ def test_missing_rmr(client, monkeypatch): test that we get a 500 with an approrpiate message on a missing rmr rmr_string """ testing_helpers.patch_all(monkeypatch, nonexisting_rmr=True) - res = client.put( - "/ric/policies/control_admission_time", json=testing_helpers.good_payload(), headers={"Content-Type": "text/plain"} - ) + 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' diff --git a/tests/testing_helpers.py b/tests/testing_helpers.py index 3cf94e2..b258a85 100644 --- a/tests/testing_helpers.py +++ b/tests/testing_helpers.py @@ -39,4 +39,4 @@ def patch_all(monkeypatch, nonexisting_rmr=False): def good_payload(): - return {"dc_admission_start_time": "10:00:00", "dc_admission_end_time": "11:00:00"} + return {"enforce": True, "window_length": 10, "blocking_rate": 20, "trigger_threshold": 10} diff --git a/tox-integration.ini b/tox-integration.ini index b3a9183..aa2ce36 100644 --- a/tox-integration.ini +++ b/tox-integration.ini @@ -40,7 +40,7 @@ commands= echo "running tavern" pytest echo "running ab" - ab -n 100 -c 10 -u putdata -T application/json http://localhost:10000/ric/policies/control_admission_time + ab -n 100 -c 10 -u putdata -T application/json http://localhost:10000/ric/policies/admission_control_policy commands_post= helm delete testreceiver helm del --purge testreceiver diff --git a/tox.ini b/tox.ini index a89f0a6..976ddc7 100644 --- a/tox.ini +++ b/tox.ini @@ -26,7 +26,9 @@ setenv = LD_LIBRARY_PATH = /usr/local/lib/ RMR_RCV_RETRY_INTERVAL = 1 RMR_RETRY_TIMES = 2 -commands=pytest --verbose --cov {envsitepackagesdir}/a1 --cov-report html +commands= +# Note, before this will work, for the first time on that machine, run ./install_deps.sh + pytest --verbose --cov {envsitepackagesdir}/a1 --cov-report html [testenv:flake8] basepython = python3.7 -- 2.16.6