From b7a743adb64d9a737e505d54fbdbe46777551d06 Mon Sep 17 00:00:00 2001 From: "naman.gupta" Date: Wed, 7 Dec 2022 18:01:24 +0530 Subject: [PATCH] Replacing a1-python with a1-go implementation Replacing a1-python with a1-go implementation Signed-off-by: naman.gupta Change-Id: I42b5c87c41a2efc25ac1cd63bc53e969882de24a --- .bumpversion.cfg | 11 - .dockerignore | 1 - .gitattributes | 25 - .gitignore | 115 ---- .gitreview | 5 - .readthedocs.yaml | 16 - Dockerfile | 170 +++--- Dockerfile-Unit-Test | 35 -- Dockerfile_alpine | 68 --- INFO.yaml | 73 --- LICENSE.txt | 28 - README.md | 8 - a1-go/a1-entrypoint.sh => a1-entrypoint.sh | 0 a1-go/Dockerfile | 117 ---- a1/__init__.py | 36 -- a1/a1rmr.py | 341 ----------- a1/controller.py | 223 ------- a1/data.py | 291 --------- a1/exceptions.py | 47 -- a1/messages.py | 41 -- a1/openapi.yaml | 372 ------------ a1/run.py | 43 -- {a1-go/api => api}/swagger.yaml | 0 {a1-go/cmd => cmd}/a1.go | 0 {a1-go/config => config}/config.yaml | 0 {a1-go/config => config}/config_test.yaml | 0 container-tag.yaml | 4 - docs/_static/logo.png | Bin 43935 -> 0 bytes docs/a1_xapp_contract_openapi.yaml | 93 --- docs/conf.py | 3 - docs/conf.yaml | 3 - docs/deleted_flowchart.pdf | Bin 21180 -> 0 bytes docs/developer-guide.rst | 148 ----- docs/favicon.ico | Bin 15086 -> 0 bytes docs/index.rst | 19 - docs/installation-guide.rst | 75 --- docs/overview.rst | 133 ---- docs/policy_instance_state_diagram.pdf | Bin 17090 -> 0 bytes docs/release-notes.rst | 492 --------------- docs/requirements-docs.txt | 5 - docs/user-guide-api.rst | 115 ---- a1-go/go.mod => go.mod | 0 a1-go/go.sum => go.sum | 0 integration_tests/a1mediator/.helmignore | 22 - integration_tests/a1mediator/Chart.yaml | 4 - .../a1mediator/templates/_helpers.tpl | 45 -- integration_tests/a1mediator/templates/config.yaml | 12 - .../a1mediator/templates/deployment.yaml | 63 -- integration_tests/a1mediator/templates/helper.yaml | 3 - .../a1mediator/templates/service.yaml | 56 -- .../templates/tests/test-connection.yaml | 15 - integration_tests/a1mediator/values.yaml | 31 - integration_tests/dbaas-service/Chart.yaml | 25 - integration_tests/dbaas-service/README | 1 - .../dbaas-service/templates/deployment.yaml | 42 -- .../dbaas-service/templates/service.yaml | 30 - integration_tests/dbaas-service/values.yaml | 30 - integration_tests/getlogs.sh | 20 - integration_tests/install_rmr.sh | 11 - integration_tests/portforward.sh | 8 - integration_tests/test_a1.tavern.yaml | 670 --------------------- integration_tests/testreceiver/.helmignore | 22 - integration_tests/testreceiver/Chart.yaml | 5 - .../testreceiver/templates/_helpers.tpl | 45 -- .../testreceiver/templates/config.yaml | 40 -- .../testreceiver/templates/deployment.yaml | 93 --- .../testreceiver/templates/service.yaml | 51 -- integration_tests/testreceiver/values.yaml | 16 - .../testxappcode/Dockerfile-delay-receiver | 51 -- .../testxappcode/Dockerfile-query-receiver | 51 -- .../testxappcode/Dockerfile-test-receiver | 51 -- .../testxappcode/delay-config-file.yaml | 38 -- integration_tests/testxappcode/go.mod | 15 - .../testxappcode/query-config-file.yaml | 38 -- integration_tests/testxappcode/receiver.go | 227 ------- .../testxappcode/test-config-file.yaml | 38 -- local.rt | 4 - {a1-go/pkg => pkg}/a1/a1.go | 0 {a1-go/pkg => pkg}/a1/a1_test.go | 0 {a1-go/pkg => pkg}/models/policy_instance_id.go | 0 {a1-go/pkg => pkg}/models/policy_type_id.go | 0 {a1-go/pkg => pkg}/models/policy_type_schema.go | 0 {a1-go/pkg => pkg}/policy/policyManager.go | 0 {a1-go/pkg => pkg}/policy/policyManager_test.go | 0 {a1-go/pkg => pkg}/policy/types.go | 0 {a1-go/pkg => pkg}/restapi/configure_a1.go | 0 {a1-go/pkg => pkg}/restapi/doc.go | 0 {a1-go/pkg => pkg}/restapi/embedded_spec.go | 0 {a1-go/pkg => pkg}/restapi/operations/a1_api.go | 0 .../a1_controller_data_delivery.go | 0 .../a1_controller_data_delivery_parameters.go | 0 .../a1_controller_data_delivery_responses.go | 0 .../a1_controller_data_delivery_urlbuilder.go | 0 ...controller_create_or_replace_policy_instance.go | 0 ...create_or_replace_policy_instance_parameters.go | 0 ..._create_or_replace_policy_instance_responses.go | 0 ...create_or_replace_policy_instance_urlbuilder.go | 0 .../a1_controller_create_policy_type.go | 0 .../a1_controller_create_policy_type_parameters.go | 0 .../a1_controller_create_policy_type_responses.go | 0 .../a1_controller_create_policy_type_urlbuilder.go | 0 .../a1_controller_delete_policy_instance.go | 0 ...controller_delete_policy_instance_parameters.go | 0 ..._controller_delete_policy_instance_responses.go | 0 ...controller_delete_policy_instance_urlbuilder.go | 0 .../a1_controller_delete_policy_type.go | 0 .../a1_controller_delete_policy_type_parameters.go | 0 .../a1_controller_delete_policy_type_responses.go | 0 .../a1_controller_delete_policy_type_urlbuilder.go | 0 .../a1_controller_get_all_instances_for_type.go | 0 ...roller_get_all_instances_for_type_parameters.go | 0 ...troller_get_all_instances_for_type_responses.go | 0 ...roller_get_all_instances_for_type_urlbuilder.go | 0 .../a1_controller_get_all_policy_types.go | 0 ...1_controller_get_all_policy_types_parameters.go | 0 ...a1_controller_get_all_policy_types_responses.go | 0 ...1_controller_get_all_policy_types_urlbuilder.go | 0 .../a1_mediator/a1_controller_get_healthcheck.go | 0 .../a1_controller_get_healthcheck_parameters.go | 0 .../a1_controller_get_healthcheck_responses.go | 0 .../a1_controller_get_healthcheck_urlbuilder.go | 0 .../a1_controller_get_policy_instance.go | 0 ...a1_controller_get_policy_instance_parameters.go | 0 .../a1_controller_get_policy_instance_responses.go | 0 .../a1_controller_get_policy_instance_status.go | 0 ...roller_get_policy_instance_status_parameters.go | 0 ...troller_get_policy_instance_status_responses.go | 0 ...roller_get_policy_instance_status_urlbuilder.go | 0 ...a1_controller_get_policy_instance_urlbuilder.go | 0 .../a1_mediator/a1_controller_get_policy_type.go | 0 .../a1_controller_get_policy_type_parameters.go | 0 .../a1_controller_get_policy_type_responses.go | 0 .../a1_controller_get_policy_type_urlbuilder.go | 0 {a1-go/pkg => pkg}/restapi/server.go | 0 {a1-go/pkg => pkg}/restful/restful.go | 0 {a1-go/pkg => pkg}/restful/types.go | 0 {a1-go/pkg => pkg}/resthooks/resthooks.go | 0 {a1-go/pkg => pkg}/resthooks/resthooks_test.go | 0 {a1-go/pkg => pkg}/resthooks/types.go | 0 {a1-go/pkg => pkg}/rmr/messages.go | 0 {a1-go/pkg => pkg}/rmr/rmr.go | 0 releases/container-release-ric-plt-a1.yaml | 10 - rmr-version.yaml | 3 - setup.py | 30 - tests/__init__.py | 19 - tests/conftest.py | 85 --- tests/test_controller.py | 413 ------------- tox-integration.ini | 76 --- tox.ini | 84 --- 149 files changed, 82 insertions(+), 5667 deletions(-) delete mode 100644 .bumpversion.cfg delete mode 100644 .dockerignore delete mode 100644 .gitattributes delete mode 100644 .gitignore delete mode 100644 .gitreview delete mode 100644 .readthedocs.yaml delete mode 100644 Dockerfile-Unit-Test delete mode 100644 Dockerfile_alpine delete mode 100644 INFO.yaml delete mode 100644 LICENSE.txt delete mode 100644 README.md rename a1-go/a1-entrypoint.sh => a1-entrypoint.sh (100%) mode change 100755 => 100644 delete mode 100644 a1-go/Dockerfile delete mode 100644 a1/__init__.py delete mode 100644 a1/a1rmr.py delete mode 100644 a1/controller.py delete mode 100644 a1/data.py delete mode 100644 a1/exceptions.py delete mode 100644 a1/messages.py delete mode 100644 a1/openapi.yaml delete mode 100644 a1/run.py rename {a1-go/api => api}/swagger.yaml (100%) rename {a1-go/cmd => cmd}/a1.go (100%) rename {a1-go/config => config}/config.yaml (100%) rename {a1-go/config => config}/config_test.yaml (100%) delete mode 100644 container-tag.yaml delete mode 100644 docs/_static/logo.png delete mode 100644 docs/a1_xapp_contract_openapi.yaml delete mode 100644 docs/conf.py delete mode 100644 docs/conf.yaml delete mode 100644 docs/deleted_flowchart.pdf delete mode 100644 docs/developer-guide.rst delete mode 100644 docs/favicon.ico delete mode 100644 docs/index.rst delete mode 100644 docs/installation-guide.rst delete mode 100644 docs/overview.rst delete mode 100644 docs/policy_instance_state_diagram.pdf delete mode 100644 docs/release-notes.rst delete mode 100644 docs/requirements-docs.txt delete mode 100644 docs/user-guide-api.rst rename a1-go/go.mod => go.mod (100%) rename a1-go/go.sum => go.sum (100%) delete mode 100644 integration_tests/a1mediator/.helmignore delete mode 100644 integration_tests/a1mediator/Chart.yaml delete mode 100644 integration_tests/a1mediator/templates/_helpers.tpl delete mode 100644 integration_tests/a1mediator/templates/config.yaml delete mode 100644 integration_tests/a1mediator/templates/deployment.yaml delete mode 100644 integration_tests/a1mediator/templates/helper.yaml delete mode 100644 integration_tests/a1mediator/templates/service.yaml delete mode 100644 integration_tests/a1mediator/templates/tests/test-connection.yaml delete mode 100644 integration_tests/a1mediator/values.yaml delete mode 100644 integration_tests/dbaas-service/Chart.yaml delete mode 100644 integration_tests/dbaas-service/README delete mode 100644 integration_tests/dbaas-service/templates/deployment.yaml delete mode 100644 integration_tests/dbaas-service/templates/service.yaml delete mode 100644 integration_tests/dbaas-service/values.yaml delete mode 100755 integration_tests/getlogs.sh delete mode 100755 integration_tests/install_rmr.sh delete mode 100755 integration_tests/portforward.sh delete mode 100644 integration_tests/test_a1.tavern.yaml delete mode 100644 integration_tests/testreceiver/.helmignore delete mode 100644 integration_tests/testreceiver/Chart.yaml delete mode 100644 integration_tests/testreceiver/templates/_helpers.tpl delete mode 100644 integration_tests/testreceiver/templates/config.yaml delete mode 100644 integration_tests/testreceiver/templates/deployment.yaml delete mode 100644 integration_tests/testreceiver/templates/service.yaml delete mode 100644 integration_tests/testreceiver/values.yaml delete mode 100644 integration_tests/testxappcode/Dockerfile-delay-receiver delete mode 100644 integration_tests/testxappcode/Dockerfile-query-receiver delete mode 100644 integration_tests/testxappcode/Dockerfile-test-receiver delete mode 100644 integration_tests/testxappcode/delay-config-file.yaml delete mode 100644 integration_tests/testxappcode/go.mod delete mode 100644 integration_tests/testxappcode/query-config-file.yaml delete mode 100644 integration_tests/testxappcode/receiver.go delete mode 100644 integration_tests/testxappcode/test-config-file.yaml delete mode 100644 local.rt rename {a1-go/pkg => pkg}/a1/a1.go (100%) rename {a1-go/pkg => pkg}/a1/a1_test.go (100%) rename {a1-go/pkg => pkg}/models/policy_instance_id.go (100%) rename {a1-go/pkg => pkg}/models/policy_type_id.go (100%) rename {a1-go/pkg => pkg}/models/policy_type_schema.go (100%) rename {a1-go/pkg => pkg}/policy/policyManager.go (100%) rename {a1-go/pkg => pkg}/policy/policyManager_test.go (100%) rename {a1-go/pkg => pkg}/policy/types.go (100%) rename {a1-go/pkg => pkg}/restapi/configure_a1.go (100%) rename {a1-go/pkg => pkg}/restapi/doc.go (100%) rename {a1-go/pkg => pkg}/restapi/embedded_spec.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_api.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_e_i_data_delivery/a1_controller_data_delivery.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_e_i_data_delivery/a1_controller_data_delivery_parameters.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_e_i_data_delivery/a1_controller_data_delivery_responses.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_e_i_data_delivery/a1_controller_data_delivery_urlbuilder.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_create_or_replace_policy_instance.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_create_or_replace_policy_instance_parameters.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_create_or_replace_policy_instance_responses.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_create_or_replace_policy_instance_urlbuilder.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_create_policy_type.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_create_policy_type_parameters.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_create_policy_type_responses.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_create_policy_type_urlbuilder.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_delete_policy_instance.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_delete_policy_instance_parameters.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_delete_policy_instance_responses.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_delete_policy_instance_urlbuilder.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_delete_policy_type.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_delete_policy_type_parameters.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_delete_policy_type_responses.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_delete_policy_type_urlbuilder.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_all_instances_for_type.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_all_instances_for_type_parameters.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_all_instances_for_type_responses.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_all_instances_for_type_urlbuilder.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_all_policy_types.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_all_policy_types_parameters.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_all_policy_types_responses.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_all_policy_types_urlbuilder.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_healthcheck.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_healthcheck_parameters.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_healthcheck_responses.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_healthcheck_urlbuilder.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_policy_instance.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_policy_instance_parameters.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_policy_instance_responses.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_policy_instance_status.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_policy_instance_status_parameters.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_policy_instance_status_responses.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_policy_instance_status_urlbuilder.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_policy_instance_urlbuilder.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_policy_type.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_policy_type_parameters.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_policy_type_responses.go (100%) rename {a1-go/pkg => pkg}/restapi/operations/a1_mediator/a1_controller_get_policy_type_urlbuilder.go (100%) rename {a1-go/pkg => pkg}/restapi/server.go (100%) rename {a1-go/pkg => pkg}/restful/restful.go (100%) rename {a1-go/pkg => pkg}/restful/types.go (100%) rename {a1-go/pkg => pkg}/resthooks/resthooks.go (100%) rename {a1-go/pkg => pkg}/resthooks/resthooks_test.go (100%) rename {a1-go/pkg => pkg}/resthooks/types.go (100%) rename {a1-go/pkg => pkg}/rmr/messages.go (100%) rename {a1-go/pkg => pkg}/rmr/rmr.go (100%) delete mode 100644 releases/container-release-ric-plt-a1.yaml delete mode 100644 rmr-version.yaml delete mode 100644 setup.py delete mode 100644 tests/__init__.py delete mode 100644 tests/conftest.py delete mode 100644 tests/test_controller.py delete mode 100644 tox-integration.ini delete mode 100644 tox.ini diff --git a/.bumpversion.cfg b/.bumpversion.cfg deleted file mode 100644 index c4d0a5f..0000000 --- a/.bumpversion.cfg +++ /dev/null @@ -1,11 +0,0 @@ -[bumpversion] -current_version = 2.2.0 -commit = False -tag = False - -[bumpversion:file:setup.py] - -[bumpversion:file:container-tag.yaml] - -[bumpversion:file:integration_tests/a1mediator/Chart.yaml] - diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index eeb8a6e..0000000 --- a/.dockerignore +++ /dev/null @@ -1 +0,0 @@ -**/__pycache__ diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index d4628c1..0000000 --- a/.gitattributes +++ /dev/null @@ -1,25 +0,0 @@ -# https://help.github.com/articles/dealing-with-line-endings/ - -# Set the default behavior, in case people don't have core.autocrlf set. -* text=auto - -# Explicitly declare text files you want to always be normalized -# and converted to native line endings on checkout. -*.c text diff=cpp -*.cpp text diff=cpp -*.css text -*.go text diff=golang -*.htm text diff=html -*.html text diff=html -*.java text diff=java -*.js text -*.jsp text -*.less text -*.properties text -*.py text diff=python -*.sql text -*.xml text - -# Denote all files that are truly binary and should not be modified. -*.png binary -*.jpg binary diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 45f74f2..0000000 --- a/.gitignore +++ /dev/null @@ -1,115 +0,0 @@ -# misc cruft -*.log -log.txt -integration_tests/log.txt -NOTES.txt -rmr/* -docs_and_diagrams/ - -# documentation -.tox -docs/_build/ - -# standard python ignore template -.pytest_cache/ -xunit-results.xml -.DS_Store -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -venv-tox/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*,cover -.hypothesis/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# IPython Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# dotenv -.env - -# virtualenv -venv/ -ENV/ - -# Spyder project settings -.spyderproject - -# Rope project settings -.ropeproject - -# Test report -xunit-reports -coverage-reports - -# Eclipse -.project -.pydevproject -.settings/ diff --git a/.gitreview b/.gitreview deleted file mode 100644 index 91493c1..0000000 --- a/.gitreview +++ /dev/null @@ -1,5 +0,0 @@ -[gerrit] -host=gerrit.o-ran-sc.org -port=29418 -project=ric-plt/a1/ -defaultbranch=master diff --git a/.readthedocs.yaml b/.readthedocs.yaml deleted file mode 100644 index 095222a..0000000 --- a/.readthedocs.yaml +++ /dev/null @@ -1,16 +0,0 @@ ---- -version: 2 - -formats: - - htmlzip - -build: - image: latest - -python: - version: 3.7 - install: - - requirements: docs/requirements-docs.txt - -sphinx: - configuration: docs/conf.py diff --git a/Dockerfile b/Dockerfile index 2b56529..a9e6c15 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,131 +1,125 @@ -# ================================================================================== -# Copyright (c) 2019-2020 Nokia -# Copyright (c) 2018-2020 AT&T Intellectual Property. +# Copyright (c) 2021 Samsung. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# ================================================================================== -# This builds an image for A1 based on ubuntu. The build takes between three and four -# minutes depending on what was previously cached, and results in an image that is -# roughly 260 MiB in size (as of May 2021) -# +#----------------------------------------------------------- -FROM python:3.8 as build +FROM nexus3.o-ran-sc.org:10002/o-ran-sc/bldr-ubuntu20-c-go:1.1.0 AS a1-build -# upgrade pip as root -RUN pip install --upgrade pip -# pick up things for gevent build -# -RUN apt-get update -RUN apt-get install -y gcc musl-dev make file libffi-dev g++ +#TODO check why defualt golang is not working +ARG GOVERSION="1.18.5" +RUN wget -nv https://dl.google.com/go/go${GOVERSION}.linux-amd64.tar.gz \ + && tar -xf go${GOVERSION}.linux-amd64.tar.gz \ + && mv go /opt/go/${GOVERSION} \ + && rm -f go*.gz -# --- all root operations must be above this line ------------------------------------ +ENV DEFAULTPATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +ENV PATH=$DEFAULTPATH:/usr/local/go/bin:/opt/go/${GOVERSION}/bin:/root/go/bin -# create a simple user. This is only really needed in stage 2, -# however this makes the copying easier and straighttforward; -# the 'pip option --user' command doesn't do the same thing when -# run as root. -# -RUN addgroup a1user && adduser --ingroup a1user a1user +RUN apt-get update -y && apt-get install -y jq -# switch to the non-root user for installing python things -USER a1user +# Update CA certificates +RUN apt update && apt install --reinstall -y \ + ca-certificates \ + && \ + update-ca-certificates -# Speed hack; we install gevent before anything because when building repeatedly (eg during dev) -# and only changing a1 code, we do not need to keep compiling gevent which takes forever -RUN pip install --user gevent -RUN pip install --user requests +#Install RMR -COPY setup.py /home/a1user/ -COPY a1/ /home/a1user/a1 -RUN pip install --user /home/a1user +ARG RMR_VER=4.8.3 +ARG RMR_PKG_URL=https://packagecloud.io/o-ran-sc/release/packages/debian/stretch/ +RUN wget -nv --content-disposition ${RMR_PKG_URL}/rmr_${RMR_VER}_amd64.deb/download.deb +RUN wget -nv --content-disposition ${RMR_PKG_URL}/rmr-dev_${RMR_VER}_amd64.deb/download.deb +RUN dpkg -i rmr_${RMR_VER}_amd64.deb \ + && dpkg -i rmr-dev_${RMR_VER}_amd64.deb \ + && ldconfig -# ----- stage 2 --------------------------------------------------------------------------------- +ENV PATH="/usr/local/go/bin:${PATH}" -# It might be tempting to use python:3.8, but that image is more than -# 800 GiB to start, and the final image size if it is used is over -# 1 GiB!! Using the plain ubuntu image, then installing py3, and taking -# things built in the first stage, the final image size isn't tiny, but should -# be well under the 800GiB start for the python image. -# -FROM ubuntu:20.04 +ENV GOPATH="/go" -# pick up reference to python so that we can get 3.8 and not the really old default +RUN mkdir -p /go/bin +RUN mkdir -p /go/src/ws +WORKDIR "/go/src/ws" -RUN apt-get update \ - && apt install -y software-properties-common \ - && add-apt-repository ppa:deadsnakes/ppa \ - && apt-get install -y python3.8 python3-pip wget \ - && apt-get clean +# Module prepare (if go.mod/go.sum updated) +COPY go.mod /go/src/ws +COPY go.sum /go/src/ws +RUN GO111MODULE=on go mod download -# fetch and install RMR and any other needed libraries -# -ARG RMR_VER=4.8.0 +# build and test +COPY . /go/src/ws +COPY ./config/config_test.yaml /opt/a1-mediator/ +COPY ./config/config_rmr.json /opt/a1-mediator/ + +ENV CFG_FILE=/opt/a1-mediator/config-_rmr.json +ENV A1_CONFIG_FILE=/opt/a1-mediator/config_test.yaml + + +# Build the code +RUN GO111MODULE=on GO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o /go/src/ws/cache/go/cmd/a1 cmd/a1.go + +# Run unit tests +RUN GO111MODULE=on GO_ENABLED=0 GOOS=linux go test -p 1 -cover ./pkg/resthooks/ +RUN GO111MODULE=on GO_ENABLED=0 GOOS=linux go test -p 1 -cover ./pkg/a1/ +RUN GO111MODULE=on GO_ENABLED=0 GOOS=linux go test -p 1 -cover ./pkg/policy + +RUN gofmt -l $(find cmd/ pkg/ -name '*.go' -not -name '*_test.go') + +CMD ["/bin/bash"] + + +#---------------------------------------------------------- +FROM ubuntu:18.04 as a1-mediator + +RUN apt-get update -y \ + && apt-get install --reinstall -y sudo openssl ca-certificates ca-cacert wget\ + && apt-get clean && update-ca-certificates + +#Install RMR + +ARG RMR_VER=4.8.3 ARG RMR_PKG_URL=https://packagecloud.io/o-ran-sc/release/packages/debian/stretch/ RUN wget -nv --content-disposition ${RMR_PKG_URL}/rmr_${RMR_VER}_amd64.deb/download.deb RUN wget -nv --content-disposition ${RMR_PKG_URL}/rmr-dev_${RMR_VER}_amd64.deb/download.deb RUN dpkg -i rmr_${RMR_VER}_amd64.deb \ - && dpkg -i rmr-dev_${RMR_VER}_amd64.deb \ - && ldconfig + && dpkg -i rmr-dev_${RMR_VER}_amd64.deb \ + && ldconfig -# copy python modules; this makes the 2 stage python build work # -COPY --from=build /home/a1user/.local /home/a1user/.local - -# create mount point for dir with rmr routing file as named below +# a1-mediator # -RUN mkdir -p /opt/route/ +RUN mkdir -p /opt/a1-mediator \ + && chmod -R 755 /opt/a1-mediator -# create a non-root user, ensure it can access what it needs, and switch to it -# -RUN addgroup a1user \ - && adduser --disabled-password --disabled-login --gecos "image-user" --no-create-home --ingroup a1user a1user \ - && chown -R a1user:a1user /home/a1user/.local \ - && chown a1user:a1user /home/a1user +COPY --from=a1-build /go/src/ws/cache/go/cmd/a1 /opt/a1-mediator/a1 +COPY ./config/config.yaml /opt/a1-mediator/ +COPY ./config/config_rmr.json /opt/a1-mediator/ -# ------------------ no root commands after this point ------------------------------------- -USER a1user +WORKDIR /opt/a1-mediator -# the maddening onsey-twosey install of pything crud... -# -RUN pip3 install --user connexion +ARG RMR_CONFIG=/opt/a1-mediator/config-_rmr.json +ENV CFG_FILE=$RMR_CONFIG +ARG A1_CONFIG=/opt/a1-mediator/config.yaml +ENV A1_CONFIG_FILE=$A1_CONFIG -# misc -# -EXPOSE 10000 -ENV LD_LIBRARY_PATH /usr/local/lib/:/usr/local/lib64 -ENV RMR_SEED_RT /opt/route/local.rt - -# Set "fake" to True to run standalone -# -ENV USE_FAKE_SDL False -ENV PYTHONUNBUFFERED 1 -# pip installs executable script to $HOME/.local/bin so PATH vars are critical -# -ENV PATH /home/a1user/.local/bin:$PATH -ENV PYTHONPATH /home/a1user/.local/lib/python3.8/site-packages +COPY a1-entrypoint.sh /opt/a1-mediator/ +ENTRYPOINT ["/opt/a1-mediator/a1-entrypoint.sh"] -# prometheus client gathers data here -# -ENV prometheus_multiproc_dir /tmp - -# by defalt start the application -# -CMD [ "/usr/bin/python3.8", "/home/a1user/.local/bin/run-a1" ] diff --git a/Dockerfile-Unit-Test b/Dockerfile-Unit-Test deleted file mode 100644 index afbd61a..0000000 --- a/Dockerfile-Unit-Test +++ /dev/null @@ -1,35 +0,0 @@ -# ================================================================================== -# Copyright (c) 2019-2020 Nokia -# Copyright (c) 2018-2020 AT&T Intellectual Property. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ================================================================================== -FROM python:3.8-alpine - -# Gevent needs gcc, make, file, ffi -RUN apk update && apk add gcc musl-dev make file libffi-dev - -# Upgrade pip, install tox (gevent is installed as a speed hack in local dev where tox is run many times) -RUN pip install --upgrade pip && pip install tox gevent - -# copy rmr libraries from builder image in lieu of an Alpine package -COPY --from=nexus3.o-ran-sc.org:10002/o-ran-sc/bldr-alpine3-rmr:4.0.5 /usr/local/lib64/librmr* /usr/local/lib64/ - -# copies -COPY setup.py tox.ini /tmp/ -COPY a1/ /tmp/a1 -COPY tests/ /tmp/tests -WORKDIR /tmp - -# Run the unit tests but skip doc -RUN tox -e code,flake8 diff --git a/Dockerfile_alpine b/Dockerfile_alpine deleted file mode 100644 index 9d4b789..0000000 --- a/Dockerfile_alpine +++ /dev/null @@ -1,68 +0,0 @@ -# ================================================================================== -# Copyright (c) 2019-2020 Nokia -# Copyright (c) 2018-2020 AT&T Intellectual Property. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ================================================================================== - -# This container uses a 2 stage build! -# Tips and tricks were learned from: https://pythonspeed.com/articles/multi-stage-docker-python/ -FROM python:3.8-alpine AS compile-image -# upgrade pip as root -RUN pip install --upgrade pip -# Gevent needs gcc, make, file, ffi -RUN apk update && apk add gcc musl-dev make file libffi-dev g++ -# create a non-root user. Only really needed in stage 2, -# however this makes the copying easier and straighttforward; -# pip option --user doesn't do the same thing if run as root -RUN addgroup -S a1user && adduser -S -G a1user a1user -# switch to the non-root user for installing site packages -USER a1user -# Speed hack; we install gevent before a1 because when building repeatedly (eg during dev) -# and only changing a1 code, we do not need to keep compiling gevent which takes forever -RUN pip install --user gevent -COPY setup.py /home/a1user/ -COPY a1/ /home/a1user/a1 -RUN pip install --user /home/a1user - -########### -# 2nd stage -FROM python:3.8-alpine - -# copy rmr libraries from builder image in lieu of an Alpine package (10002 is the release portion of the repo) -COPY --from=nexus3.o-ran-sc.org:10002/o-ran-sc/bldr-alpine3-rmr:4.5.2 /usr/local/lib64/librmr* /usr/local/lib64/ - -# copy python modules; this makes the 2 stage python build work -COPY --from=compile-image /home/a1user/.local /home/a1user/.local -# create mount point for dir with rmr routing file as named below -RUN mkdir -p /opt/route/ -# create a non-root user -RUN addgroup -S a1user && adduser -S -G a1user a1user -# ensure the non-root user can read python files -RUN chown -R a1user:a1user /home/a1user/.local -# switch to the non-root user for security reasons -USER a1user -# misc setups -EXPOSE 10000 -ENV LD_LIBRARY_PATH /usr/local/lib/:/usr/local/lib64 -ENV RMR_SEED_RT /opt/route/local.rt -# Set to True to run standalone -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 -# prometheus client gathers data here -ENV prometheus_multiproc_dir /tmp - -# Run! -CMD run-a1 diff --git a/INFO.yaml b/INFO.yaml deleted file mode 100644 index f000431..0000000 --- a/INFO.yaml +++ /dev/null @@ -1,73 +0,0 @@ ---- -project: 'ric_plt_a1' -project_creation_date: '2019-11-08' -project_category: '' -lifecycle_state: 'Incubation' -project_lead: &oran_ric_plt_a1_ptl - name: 'Thoralf Czichy' - email: 'thoralf.czichy@nokia.com' - id: 'czichy' - company: 'Nokia' - timezone: 'America/New_York' -primary_contact: *oran_ric_plt_a1_ptl -issue_tracking: - type: 'jira' - url: 'https://jira.o-ran-sc.org/projects/' - key: 'ric_plt_a1' -mailing_list: - type: 'groups.io' - url: 'technical-discuss@lists.o-ran-sc.org' - tag: '[]' -realtime_discussion: - type: 'irc' - server: 'freenode.net' - channel: '#oran' -meetings: - - type: 'gotomeeting+irc' - agenda: 'https://wiki.o-ran-sc.org/display/' - url: '' - server: 'freenode.net' - channel: '#oran' - repeats: '' - time: '' -repositories: - - ric-plt/a1 -committers: - - <<: *oran_ric_plt_a1_ptl - - name: 'Scott Daniels' - email: 'daniels@research.att.com' - company: 'research.att' - id: 'EScottDaniels' - timezone: 'Unknown/Unknown' - - name: 'Naman Gupta' - email: 'naman.gupta@samsung.com' - company: 'samsung' - id: 'naman.gupta' - timezone: 'Unknown/Unknown' - - name: 'Subhash Kumar Singh' - email: 'subh.singh@samsung.com' - company: 'samsung' - id: 'subhash_singh' - timezone: 'Unknown/Unknown' -tsc: - # yamllint disable rule:line-length - approval: 'missing' - changes: - - type: 'Removal' - name: 'Tommy Carpenter' - link: 'https://lists.o-ran-sc.org/g/toc/topic/removal_of_committer_tommy_c/74795766?p=,,,100,0,0,0::recentpostdate%2Fsticky,,,100,2,0,74795766' - - type: 'Removal' - name: 'Chris Lott' - link: 'https://lists.o-ran-sc.org/g/toc/message/359' - - type: 'Promotion' - name: 'Subhash Kumar Singh' - link: 'https://lists.o-ran-sc.org/g/toc/message/359' - - type: 'Promotion' - name: 'Subhash Kumar Singh' - link: 'https://lists.o-ran-sc.org/g/toc/message/359' - - type: 'Removal' - name: 'Rahul Banerji' - link: 'https://lists.o-ran-sc.org/g/toc/message/435#Ritun#Subhash#Thoralf' - - type: 'Promotion' - name: 'Naman Gupta' - link: 'https://lists.o-ran-sc.org/g/toc/message/435#Ritun#Subhash#Thoralf' diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index f836918..0000000 --- a/LICENSE.txt +++ /dev/null @@ -1,28 +0,0 @@ -Unless otherwise specified, all software contained herein is licensed -under the Apache License, Version 2.0 (the "Software License"); -you may not use this software except in compliance with the Software -License. You may obtain a copy of the Software License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the Software License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the Software License for the specific language governing permissions -and limitations under the Software License. - - - -Unless otherwise specified, all documentation contained herein is licensed -under the Creative Commons License, Attribution 4.0 Intl. (the -"Documentation License"); you may not use this documentation except in -compliance with the Documentation License. You may obtain a copy of the -Documentation License at - - https://creativecommons.org/licenses/by/4.0/ - -Unless required by applicable law or agreed to in writing, documentation -distributed under the Documentation License is distributed on an "AS IS" -BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -implied. See the Documentation License for the specific language governing -permissions and limitations under the Documentation License. diff --git a/README.md b/README.md deleted file mode 100644 index 943c194..0000000 --- a/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# RIC A1 Mediator - -The xApp A1 mediator exposes a generic REST API by which xApps can -receive and send northbound messages. The A1 mediator will take -the payload from such generic REST messages, validate the payload, -and then communicate the payload to the xApp via RMR messaging. - -Please see documentation in the docs/ subdirectory. diff --git a/a1-go/a1-entrypoint.sh b/a1-entrypoint.sh old mode 100755 new mode 100644 similarity index 100% rename from a1-go/a1-entrypoint.sh rename to a1-entrypoint.sh diff --git a/a1-go/Dockerfile b/a1-go/Dockerfile deleted file mode 100644 index 1a4cfec..0000000 --- a/a1-go/Dockerfile +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (c) 2021 Samsung. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -#----------------------------------------------------------- - -FROM nexus3.o-ran-sc.org:10002/o-ran-sc/bldr-ubuntu20-c-go:1.1.0 AS a1-build - - -#TODO check why defualt golang is not working -ARG GOVERSION="1.18.5" -RUN wget -nv https://dl.google.com/go/go${GOVERSION}.linux-amd64.tar.gz \ - && tar -xf go${GOVERSION}.linux-amd64.tar.gz \ - && mv go /opt/go/${GOVERSION} \ - && rm -f go*.gz - - -ENV DEFAULTPATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin -ENV PATH=$DEFAULTPATH:/usr/local/go/bin:/opt/go/${GOVERSION}/bin:/root/go/bin - -RUN apt-get update -y && apt-get install -y jq - -# Update CA certificates -RUN apt update && apt install --reinstall -y \ - ca-certificates \ - && \ - update-ca-certificates - -#Install RMR - -ARG RMR_VER=4.8.3 -ARG RMR_PKG_URL=https://packagecloud.io/o-ran-sc/release/packages/debian/stretch/ - -RUN wget -nv --content-disposition ${RMR_PKG_URL}/rmr_${RMR_VER}_amd64.deb/download.deb -RUN wget -nv --content-disposition ${RMR_PKG_URL}/rmr-dev_${RMR_VER}_amd64.deb/download.deb -RUN dpkg -i rmr_${RMR_VER}_amd64.deb \ - && dpkg -i rmr-dev_${RMR_VER}_amd64.deb \ - && ldconfig - - -ENV PATH="/usr/local/go/bin:${PATH}" - -ENV GOPATH="/go" - -RUN mkdir -p /go/bin -RUN mkdir -p /go/src/ws -WORKDIR "/go/src/ws" - -# Module prepare (if go.mod/go.sum updated) -COPY go.mod /go/src/ws -COPY go.sum /go/src/ws -RUN GO111MODULE=on go mod download - -# build and test -COPY . /go/src/ws - -COPY ./config/uta_rtg.rt /opt/a1-mediator/ - -ENV RMR_RTG_SVC=-1 -ENV RMR_LOG_VLEVEL=5 -ENV CFG_FILE=/opt/a1-mediator/config-file.json -ENV RMR_SEED_RT=/opt/a1-mediator/uta_rtg.rt - -# Build the code -RUN GO111MODULE=on GO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o /go/src/ws/cache/go/cmd/a1 cmd/a1.go - -# Run unit tests -RUN GO111MODULE=on GO_ENABLED=0 GOOS=linux go test -p 1 -cover ./pkg/resthooks/ -RUN GO111MODULE=on GO_ENABLED=0 GOOS=linux go test -p 1 -cover ./pkg/a1/ -RUN GO111MODULE=on GO_ENABLED=0 GOOS=linux go test -p 1 -cover ./pkg/policyManager - -RUN gofmt -l $(find cmd/ pkg/ -name '*.go' -not -name '*_test.go') - -CMD ["/bin/bash"] - - -#---------------------------------------------------------- -FROM ubuntu:18.04 as a1-mediator - -RUN apt-get update -y \ - && apt-get install --reinstall -y sudo openssl ca-certificates ca-cacert wget\ - && apt-get clean && update-ca-certificates - -#Install RMR - -ARG RMR_VER=4.8.3 -ARG RMR_PKG_URL=https://packagecloud.io/o-ran-sc/release/packages/debian/stretch/ - -RUN wget -nv --content-disposition ${RMR_PKG_URL}/rmr_${RMR_VER}_amd64.deb/download.deb -RUN wget -nv --content-disposition ${RMR_PKG_URL}/rmr-dev_${RMR_VER}_amd64.deb/download.deb -RUN dpkg -i rmr_${RMR_VER}_amd64.deb \ - && dpkg -i rmr-dev_${RMR_VER}_amd64.deb \ - && ldconfig - -# -# a1-mediator -# -RUN mkdir -p /opt/a1-mediator \ - && chmod -R 755 /opt/a1-mediator - -COPY --from=a1-build /go/src/ws/cache/go/cmd/a1 /opt/a1-mediator/a1 - -WORKDIR /opt/a1-mediator - -COPY a1-entrypoint.sh /opt/a1-mediator/ -ENTRYPOINT ["/opt/a1-mediator/a1-entrypoint.sh"] - diff --git a/a1/__init__.py b/a1/__init__.py deleted file mode 100644 index f7155a0..0000000 --- a/a1/__init__.py +++ /dev/null @@ -1,36 +0,0 @@ -# ================================================================================== -# Copyright (c) 2019 Nokia -# Copyright (c) 2018-2019 AT&T Intellectual Property. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ================================================================================== -""" -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/a1rmr.py b/a1/a1rmr.py deleted file mode 100644 index 049ea2f..0000000 --- a/a1/a1rmr.py +++ /dev/null @@ -1,341 +0,0 @@ -# ================================================================================== -# Copyright (c) 2019-2020 Nokia -# Copyright (c) 2018-2020 AT&T Intellectual Property. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ================================================================================== -""" -A1 RMR functionality -""" -import os -import queue -import time -import json -import requests -from threading import Thread -from ricxappframe.rmr import rmr, helpers -from mdclogpy import Logger -from a1 import data, messages -from a1.exceptions import PolicyTypeNotFound, PolicyInstanceNotFound - -mdc_logger = Logger() -mdc_logger.mdclog_format_init(configmap_monitor=True) - - -# With Nanomsg and NNG it was possible for a send attempt to have a "soft" -# failure which did warrant some retries if the status of the send is RMR_ERR_RETRY. -# Because of the way NNG worked, it sometimes required many tens of retries, -# and a retry state happened often for even moderately "verbose" applications. -# With SI95 there is still a possibility that a retry is necessary, but it is very rare. -RETRY_TIMES = int(os.environ.get("A1_RMR_RETRY_TIMES", 4)) -A1_POLICY_REQUEST = 20010 -A1_POLICY_RESPONSE = 20011 -A1_POLICY_QUERY = 20012 -A1_EI_QUERY_ALL = 20013 -AI_EI_QUERY_ALL_RESP = 20014 -A1_EI_CREATE_JOB = 20015 -A1_EI_CREATE_JOB_RESP = 20016 -A1_EI_DATA_DELIVERY = 20017 -ECS_SERVICE_HOST = os.environ.get("ECS_SERVICE_HOST", "http://ecs-service:8083") -ESC_EI_TYPE_PATH = ECS_SERVICE_HOST + "/A1-EI/v1/eitypes" -ECS_EI_JOB_PATH = ECS_SERVICE_HOST + "/A1-EI/v1/eijobs/" - - -# Note; yes, globals are bad, but this is a private (to this module) global -# No other module can import/access this (well, python doesn't enforce this, but all linters will complain) -__RMR_LOOP__ = None - - -class _RmrLoop: - """ - Class represents an rmr loop that constantly reads from rmr and performs operations - based on waiting messages. This launches a thread, it should probably only be called - once; the public facing method to access these ensures this. - - TODO: the xapp frame has a version of this looping structure. See if A1 can switch to that. - """ - - def __init__(self, init_func_override=None, rcv_func_override=None): - """ - Init - - Parameters - ---------- - init_func_override: function (optional) - Function that initializes RMR and answers an RMR context. - Supply an empty function to skip initializing RMR. - - rcv_func_override: function (optional) - Function that receives messages from RMR and answers a list. - Supply a trivial function to skip reading from RMR. - """ - self.keep_going = True - self.rcv_func = None - self.last_ran = time.time() - - # see docs/overview#resiliency for a discussion of this - self.instance_send_queue = queue.Queue() # thread safe queue https://docs.python.org/3/library/queue.html - # queue for data delivery item - self.ei_job_result_queue = queue.Queue() - - # intialize rmr context - if init_func_override: - self.mrc = init_func_override() - else: - mdc_logger.debug("Waiting for rmr to initialize..") - # rmr.RMRFL_MTCALL puts RMR into a multithreaded mode, where a receiving thread - # populates an internal ring of messages, and receive calls read from that. - # currently the size is 2048 messages, so this is fine for the foreseeable future - self.mrc = rmr.rmr_init(b"4562", rmr.RMR_MAX_RCV_BYTES, rmr.RMRFL_MTCALL) - while rmr.rmr_ready(self.mrc) == 0: - time.sleep(0.5) - - # set the receive function - self.rcv_func = ( - rcv_func_override - if rcv_func_override - else lambda: helpers.rmr_rcvall_msgs_raw(self.mrc, [A1_POLICY_RESPONSE, A1_POLICY_QUERY, A1_EI_QUERY_ALL, A1_EI_CREATE_JOB]) - ) - - # start the work loop - self.thread = Thread(target=self.loop) - self.thread.start() - - def _assert_good_send(self, sbuf, pre_send_summary): - """ - Extracts the send result and logs a detailed warning if the send failed. - Returns the message state, an integer that indicates the result. - """ - post_send_summary = rmr.message_summary(sbuf) - if post_send_summary[rmr.RMR_MS_MSG_STATE] != rmr.RMR_OK: - mdc_logger.warning("RMR send failed; pre-send summary: {0}, post-send summary: {1}".format(pre_send_summary, post_send_summary)) - return post_send_summary[rmr.RMR_MS_MSG_STATE] - - def _send_msg(self, pay, mtype, subid): - """ - Creates and sends a message via RMR's send-message feature with the specified payload - using the specified message type and subscription ID. - """ - sbuf = rmr.rmr_alloc_msg(self.mrc, len(pay), payload=pay, gen_transaction_id=True, mtype=mtype, sub_id=subid) - sbuf.contents.sub_id = subid - pre_send_summary = rmr.message_summary(sbuf) - for _ in range(0, RETRY_TIMES): - mdc_logger.debug("_send_msg: sending: {}".format(pre_send_summary)) - sbuf = rmr.rmr_send_msg(self.mrc, sbuf) - msg_state = self._assert_good_send(sbuf, pre_send_summary) - mdc_logger.debug("_send_msg: result message state: {}".format(msg_state)) - if msg_state != rmr.RMR_ERR_RETRY: - break - - rmr.rmr_free_msg(sbuf) - if msg_state != rmr.RMR_OK: - mdc_logger.warning("_send_msg: failed after {} retries".format(RETRY_TIMES)) - - def _rts_msg(self, pay, sbuf_rts, mtype): - """ - Sends a message via RMR's return-to-sender feature. - This neither allocates nor frees a message buffer because we may rts many times. - Returns the message buffer from the RTS function, which may reallocate it. - """ - pre_send_summary = rmr.message_summary(sbuf_rts) - for _ in range(0, RETRY_TIMES): - mdc_logger.debug("_rts_msg: sending: {}".format(pre_send_summary)) - sbuf_rts = rmr.rmr_rts_msg(self.mrc, sbuf_rts, payload=pay, mtype=mtype) - msg_state = self._assert_good_send(sbuf_rts, pre_send_summary) - mdc_logger.debug("_rts_msg: result message state: {}".format(msg_state)) - if msg_state != rmr.RMR_ERR_RETRY: - break - - if msg_state != rmr.RMR_OK: - mdc_logger.warning("_rts_msg: failed after {} retries".format(RETRY_TIMES)) - return sbuf_rts # in some cases rts may return a new sbuf - - def _handle_sends(self): - # send out all messages waiting for us - while not self.instance_send_queue.empty(): - work_item = self.instance_send_queue.get(block=False, timeout=None) - payload = json.dumps(messages.a1_to_handler(*work_item)).encode("utf-8") - self._send_msg(payload, A1_POLICY_REQUEST, work_item[1]) - - # now send all the ei-job related data - while not self.ei_job_result_queue.empty(): - mdc_logger.debug("perform data delivery to consumer") - - work_item = self.ei_job_result_queue.get(block=False, timeout=None) - payload = json.dumps(messages.ei_to_handler(*work_item)).encode("utf-8") - ei_job_id = int(work_item[0]) - mdc_logger.debug("data-delivery: {}".format(payload)) - - # send the payload to consumer subscribed for ei_job_id - self._send_msg(payload, A1_EI_DATA_DELIVERY, ei_job_id) - - def loop(self): - """ - This loop runs forever, and has 3 jobs: - - send out any messages that have to go out (create instance, delete instance) - - read a1s mailbox and update the status of all instances based on acks from downstream policy handlers - - clean up the database (eg delete the instance) under certain conditions based on those statuses (NOT DONE YET) - """ - # loop forever - mdc_logger.debug("Work loop starting") - while self.keep_going: - - # Update 3/20/2020 - # We now handle our sends in a thread (that will just exit when it's done) because there is a difference between how send works in SI95 vs NNG. - # Send_msg via NNG formerly never blocked. - # However under SI95 this send may block for some arbitrary period of time on the first send to an endpoint for which a connection is not established - # If this send takes too long, this loop blocks, and the healthcheck will fail, which will cause A1s healthcheck to fail, which will cause Kubernetes to whack A1 and all kinds of horrible things happen. - # Therefore, now under SI95, we thread this. - Thread(target=self._handle_sends).start() - - # read our mailbox - for (msg, sbuf) in self.rcv_func(): - # TODO: in the future we may also have to catch SDL errors - try: - mtype = msg[rmr.RMR_MS_MSG_TYPE] - except (KeyError, TypeError, json.decoder.JSONDecodeError): - mdc_logger.warning("Dropping malformed message: {0}".format(msg)) - - if mtype == A1_POLICY_RESPONSE: - try: - # got a policy response, update status - pay = json.loads(msg[rmr.RMR_MS_PAYLOAD]) - data.set_policy_instance_status( - pay["policy_type_id"], pay["policy_instance_id"], pay["handler_id"], pay["status"] - ) - mdc_logger.debug("Successfully received status update: {0}".format(pay)) - except (PolicyTypeNotFound, PolicyInstanceNotFound): - mdc_logger.warning("Received a response for a non-existent type/instance: {0}".format(msg)) - except (KeyError, TypeError, json.decoder.JSONDecodeError): - mdc_logger.warning("Dropping malformed policy response: {0}".format(msg)) - - elif mtype == A1_POLICY_QUERY: - try: - # got a query, do a lookup and send out all instances - pti = json.loads(msg[rmr.RMR_MS_PAYLOAD])["policy_type_id"] - instance_list = data.get_instance_list(pti) # will raise if a bad type - mdc_logger.debug("Received a query for a known policy type: {0}".format(msg)) - for pii in instance_list: - instance = data.get_policy_instance(pti, pii) - payload = json.dumps(messages.a1_to_handler("CREATE", pti, pii, instance)).encode("utf-8") - sbuf = self._rts_msg(payload, sbuf, A1_POLICY_REQUEST) - except (PolicyTypeNotFound): - mdc_logger.warning("Received a policy query for a non-existent type: {0}".format(msg)) - except (KeyError, TypeError, json.decoder.JSONDecodeError): - mdc_logger.warning("Dropping malformed policy query: {0}".format(msg)) - - elif mtype == A1_EI_QUERY_ALL: - mdc_logger.debug("Received messaage {0}".format(msg)) - - # query A1-EI co-ordinator service to get the EI-types - resp = requests.get(ESC_EI_TYPE_PATH) - if resp.status_code != 200: - mdc_logger.warning("Received no reponse from A1-EI service") - - mdc_logger.debug("response from A1-EI service : {0}".format(resp.json())) - - # send the complete list of EI-types to xApp - sbuf = self._rts_msg(resp.content, sbuf, AI_EI_QUERY_ALL_RESP) - - elif mtype == A1_EI_CREATE_JOB: - mdc_logger.debug("Received message {0}".format(msg)) - payload = json.loads(msg[rmr.RMR_MS_PAYLOAD]) - mdc_logger.debug("Payload: {0}".format(payload)) - - uuidStr = payload["job-id"] - del payload["job-id"] - - mdc_logger.debug("Payload after removing job-id: {0}".format(payload)) - - # 1. send request to A1-EI Service to create A1-EI JOB - headers = {'Content-type': 'application/json'} - r = requests.put(ECS_EI_JOB_PATH + uuidStr, data=json.dumps(payload), headers=headers) - if (r.status_code != 201) and (r.status_code != 200): - mdc_logger.warning("failed to create EIJOB : {0}".format(r)) - else: - # 2. inform xApp for Job status - mdc_logger.debug("received successful response (ei-job-id) :{0}".format(uuidStr)) - rmr_data = """{{ - "ei_job_id": "{id}" - }}""".format(id=uuidStr) - mdc_logger.debug("rmr_Data to send: {0}".format(rmr_data)) - sbuf = self._rts_msg(str.encode(rmr_data), sbuf, A1_EI_CREATE_JOB_RESP) - - else: - mdc_logger.warning("Received message type {0} but A1 does not handle this".format(mtype)) - - # we must free each sbuf - rmr.rmr_free_msg(sbuf) - self.last_ran = time.time() - time.sleep(1) - - mdc_logger.debug("RMR Thread Ending!") - - -# Public - - -def start_rmr_thread(init_func_override=None, rcv_func_override=None): - """ - Start a1s rmr thread - - Parameters - ---------- - init_func_override: function (optional) - Function that initializes RMR and answers an RMR context. - Supply an empty function to skip initializing RMR. - - rcv_func_override: function (optional) - Function that receives messages from RMR and answers a list. - Supply a trivial function to skip reading from RMR. - """ - global __RMR_LOOP__ - if __RMR_LOOP__ is None: - __RMR_LOOP__ = _RmrLoop(init_func_override, rcv_func_override) - - -def stop_rmr_thread(): - """ - stops the rmr thread - """ - __RMR_LOOP__.keep_going = False - - -def queue_instance_send(item): - """ - push an item into the work queue - currently the only type of work is to send out messages - """ - __RMR_LOOP__.instance_send_queue.put(item) - - -def queue_ei_job_result(item): - """ - push an item into the ei_job_queue - """ - mdc_logger.debug("queuing data delivery item {0}".format(item)) - __RMR_LOOP__.ei_job_result_queue.put(item) - - -def healthcheck_rmr_thread(seconds=30): - """ - returns a boolean representing whether the rmr loop is healthy, by checking two attributes: - 1. is it running?, - 2. is it stuck in a long (> seconds) loop? - """ - return __RMR_LOOP__.thread.is_alive() and ((time.time() - __RMR_LOOP__.last_ran) < seconds) - - -def replace_rcv_func(rcv_func): - """purely for the ease of unit testing to test different rcv scenarios""" - __RMR_LOOP__.rcv_func = rcv_func diff --git a/a1/controller.py b/a1/controller.py deleted file mode 100644 index 19b8a26..0000000 --- a/a1/controller.py +++ /dev/null @@ -1,223 +0,0 @@ -# ================================================================================== -# Copyright (c) 2019-2020 Nokia -# Copyright (c) 2018-2020 AT&T Intellectual Property. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ================================================================================== -""" -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__) -mdc_logger.mdclog_format_init(configmap_monitor=True) - -a1_counters = Counter('A1Policy', 'Policy type and instance counters', ['counter']) - - -def _log_build_http_resp(exception, http_resp_code): - """ - helper method that logs the exception and returns a tuple of (str, int) as a http response - """ - msg = repr(exception) - mdc_logger.warning("Request failed, returning {0}: {1}".format(http_resp_code, msg)) - return msg, http_resp_code - - -def _try_func_return(func): - """ - helper method that runs the function and returns a detailed http response if an exception is raised. - """ - try: - return func() - except (ValidationError, exceptions.PolicyTypeAlreadyExists, exceptions.PolicyTypeIdMismatch, exceptions.CantDeleteNonEmptyType) as exc: - return _log_build_http_resp(exc, 400) - except (exceptions.PolicyTypeNotFound, exceptions.PolicyInstanceNotFound) as exc: - return _log_build_http_resp(exc, 404) - except (RejectedByBackend, NotConnected, BackendError) as exc: - """ - These are SDL errors. At the time of development here, we do not have a good understanding - which of these errors are "try again later it may work" and which are "never going to work". - There is some discussion that RejectedByBackend is in the latter category, suggesting it - should map to 400, but until we understand the root cause of these errors, it's confusing - to clients to give them a 400 (a "your fault" code) because they won't know how to fix. - For now, we log, and 503, and investigate the logs later to improve the handling/reporting. - """ - # mdc_logger.exception(exc) # waiting for https://jira.o-ran-sc.org/browse/RIC-39 - return _log_build_http_resp(exc, 503) - # let other types of unexpected exceptions blow up and log - - -# Healthcheck - - -def get_healthcheck(): - """ - Handles healthcheck GET - Currently, this checks: - 1. whether the a1 webserver is up (if it isn't, this won't even be called, so even entering this function confirms it is) - 2. checks whether the rmr thread is running and has completed a loop recently - 3. checks that our SDL connection is healthy - """ - if not a1rmr.healthcheck_rmr_thread(): - mdc_logger.error("A1 is not healthy due to the rmr thread") - return "rmr thread is unhealthy", 500 - if not data.SDL.healthcheck(): - mdc_logger.error("A1 is not healthy because it does not have a connection to SDL") - return "sdl connection is unhealthy", 500 - return "", 200 - - -# Policy types - - -def get_all_policy_types(): - """ - Handles GET /a1-p/policytypes - """ - return _try_func_return(data.get_type_list) - - -def create_policy_type(policy_type_id): - """ - Handles PUT /a1-p/policytypes/policy_type_id - """ - a1_counters.labels(counter='CreatePolicyTypeReqs').inc() - - def put_type_handler(): - data.store_policy_type(policy_type_id, body) - mdc_logger.debug("Policy type {} created.".format(policy_type_id)) - return "", 201 - - body = connexion.request.json - return _try_func_return(put_type_handler) - - -def get_policy_type(policy_type_id): - """ - Handles GET /a1-p/policytypes/policy_type_id - """ - return _try_func_return(lambda: data.get_policy_type(policy_type_id)) - - -def delete_policy_type(policy_type_id): - """ - Handles DELETE /a1-p/policytypes/policy_type_id - """ - a1_counters.labels(counter='DeletePolicyTypeReqs').inc() - - def delete_policy_type_handler(): - data.delete_policy_type(policy_type_id) - mdc_logger.debug("Policy type {} deleted.".format(policy_type_id)) - return "", 204 - - return _try_func_return(delete_policy_type_handler) - - -# Policy instances - - -def get_all_instances_for_type(policy_type_id): - """ - Handles GET /a1-p/policytypes/policy_type_id/policies - """ - return _try_func_return(lambda: data.get_instance_list(policy_type_id)) - - -def get_policy_instance(policy_type_id, policy_instance_id): - """ - Handles GET /a1-p/policytypes/polidyid/policies/policy_instance_id - """ - return _try_func_return(lambda: data.get_policy_instance(policy_type_id, policy_instance_id)) - - -def get_policy_instance_status(policy_type_id, policy_instance_id): - """ - Handles GET /a1-p/policytypes/polidyid/policies/policy_instance_id/status - - Return the aggregated status. The order of rules is as follows: - 1. If a1 has received at least one status, and *all* received statuses are "DELETED", we blow away the instance and return a 404 - 2. if a1 has received at least one status and at least one is OK, we return "IN EFFECT" - 3. "NOT IN EFFECT" otherwise (no statuses, or none are OK but not all are deleted) - """ - return _try_func_return(lambda: data.get_policy_instance_status(policy_type_id, policy_instance_id)) - - -def create_or_replace_policy_instance(policy_type_id, policy_instance_id): - """ - Handles PUT /a1-p/policytypes/polidyid/policies/policy_instance_id - """ - a1_counters.labels(counter='CreatePolicyInstanceReqs').inc() - instance = connexion.request.json - - def put_instance_handler(): - """ - Handles policy instance put - - For now, policy_type_id is used as the message type - """ - # validate the PUT against the schema - schema = data.get_policy_type(policy_type_id)["create_schema"] - validate(instance=instance, schema=schema) - - # store the instance - operation = data.store_policy_instance(policy_type_id, policy_instance_id, instance) - - # queue rmr send (best effort) - a1rmr.queue_instance_send((operation, policy_type_id, policy_instance_id, instance)) - - return "", 202 - - return _try_func_return(put_instance_handler) - - -def delete_policy_instance(policy_type_id, policy_instance_id): - """ - Handles DELETE /a1-p/policytypes/polidyid/policies/policy_instance_id - """ - a1_counters.labels(counter='DeletePolicyInstanceReqs').inc() - - def delete_instance_handler(): - data.delete_policy_instance(policy_type_id, policy_instance_id) - - # queue rmr send (best effort) - a1rmr.queue_instance_send(("DELETE", policy_type_id, policy_instance_id, "")) - - return "", 202 - - return _try_func_return(delete_instance_handler) - - -# data delivery - - -def data_delivery(): - """ - Handle data delivery /data-delivery - """ - - def data_delivery_handler(): - mdc_logger.debug("data: {}".format(connexion.request.json)) - ei_job_result_json = connexion.request.json - mdc_logger.debug("jobid: {}".format(ei_job_result_json.get("job"))) - a1rmr.queue_ei_job_result((ei_job_result_json.get("job"), ei_job_result_json)) - return "", 200 - - return _try_func_return(data_delivery_handler) diff --git a/a1/data.py b/a1/data.py deleted file mode 100644 index 8ec30cc..0000000 --- a/a1/data.py +++ /dev/null @@ -1,291 +0,0 @@ -# ================================================================================== -# Copyright (c) 2019-2020 Nokia -# Copyright (c) 2018-2020 AT&T Intellectual Property. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ================================================================================== -""" -Represents A1s database and database access functions. -""" -import distutils.util -import os -import time -from threading import Thread -from mdclogpy import Logger -from ricxappframe.xapp_sdl import SDLWrapper -from a1.exceptions import PolicyTypeNotFound, PolicyInstanceNotFound, PolicyTypeAlreadyExists, PolicyTypeIdMismatch, CantDeleteNonEmptyType - -# constants -INSTANCE_DELETE_NO_RESP_TTL = int(os.environ.get("INSTANCE_DELETE_NO_RESP_TTL", 5)) -INSTANCE_DELETE_RESP_TTL = int(os.environ.get("INSTANCE_DELETE_RESP_TTL", 5)) -USE_FAKE_SDL = bool(distutils.util.strtobool(os.environ.get("USE_FAKE_SDL", "False"))) -A1NS = "A1m_ns" -TYPE_PREFIX = "a1.policy_type." -INSTANCE_PREFIX = "a1.policy_instance." -METADATA_PREFIX = "a1.policy_inst_metadata." -HANDLER_PREFIX = "a1.policy_handler." - - -mdc_logger = Logger(name=__name__) -mdc_logger.mdclog_format_init(configmap_monitor=True) -if USE_FAKE_SDL: - mdc_logger.debug("Using fake SDL") -SDL = SDLWrapper(use_fake_sdl=USE_FAKE_SDL) - -# Internal helpers - - -def _generate_type_key(policy_type_id): - """ - generate a key for a policy type - """ - return "{0}{1}".format(TYPE_PREFIX, policy_type_id) - - -def _generate_instance_key(policy_type_id, policy_instance_id): - """ - generate a key for a policy instance - """ - return "{0}{1}.{2}".format(INSTANCE_PREFIX, policy_type_id, policy_instance_id) - - -def _generate_instance_metadata_key(policy_type_id, policy_instance_id): - """ - generate a key for a policy instance metadata - """ - return "{0}{1}.{2}".format(METADATA_PREFIX, policy_type_id, policy_instance_id) - - -def _generate_handler_prefix(policy_type_id, policy_instance_id): - """ - generate the prefix to a handler key - """ - return "{0}{1}.{2}.".format(HANDLER_PREFIX, policy_type_id, policy_instance_id) - - -def _generate_handler_key(policy_type_id, policy_instance_id, handler_id): - """ - generate a key for a policy handler - """ - return "{0}{1}".format(_generate_handler_prefix(policy_type_id, policy_instance_id), handler_id) - - -def _type_is_valid(policy_type_id): - """ - check that a type is valid - """ - if SDL.get(A1NS, _generate_type_key(policy_type_id)) is None: - raise PolicyTypeNotFound(policy_type_id) - - -def _instance_is_valid(policy_type_id, policy_instance_id): - """ - check that an instance is valid - """ - _type_is_valid(policy_type_id) - if SDL.get(A1NS, _generate_instance_key(policy_type_id, policy_instance_id)) is None: - raise PolicyInstanceNotFound(policy_type_id) - - -def _get_statuses(policy_type_id, policy_instance_id): - """ - shared helper to get statuses for an instance - """ - _instance_is_valid(policy_type_id, policy_instance_id) - prefixes_for_handler = "{0}{1}.{2}.".format(HANDLER_PREFIX, policy_type_id, policy_instance_id) - return list(SDL.find_and_get(A1NS, prefixes_for_handler).values()) - - -def _get_instance_list(policy_type_id): - """ - shared helper to get instance list for a type - """ - _type_is_valid(policy_type_id) - prefixes_for_type = "{0}{1}.".format(INSTANCE_PREFIX, policy_type_id) - instancekeys = SDL.find_and_get(A1NS, prefixes_for_type).keys() - return [k.split(prefixes_for_type)[1] for k in instancekeys] - - -def _clear_handlers(policy_type_id, policy_instance_id): - """ - delete all the handlers for a policy instance - """ - all_handlers_pref = _generate_handler_prefix(policy_type_id, policy_instance_id) - keys = SDL.find_and_get(A1NS, all_handlers_pref) - for k in keys: - SDL.delete(A1NS, k) - - -def _get_metadata(policy_type_id, policy_instance_id): - """ - get instance metadata - """ - _instance_is_valid(policy_type_id, policy_instance_id) - metadata_key = _generate_instance_metadata_key(policy_type_id, policy_instance_id) - return SDL.get(A1NS, metadata_key) - - -def _delete_after(policy_type_id, policy_instance_id, ttl): - """ - this is a blocking function, must call this in a thread to not block! - waits ttl seconds, then deletes the instance - """ - _instance_is_valid(policy_type_id, policy_instance_id) - - time.sleep(ttl) - - # ready to delete - _clear_handlers(policy_type_id, policy_instance_id) # delete all the handlers - SDL.delete(A1NS, _generate_instance_key(policy_type_id, policy_instance_id)) # delete instance - SDL.delete(A1NS, _generate_instance_metadata_key(policy_type_id, policy_instance_id)) # delete instance metadata - mdc_logger.debug("type {0} instance {1} deleted".format(policy_type_id, policy_instance_id)) - - -# Types - - -def get_type_list(): - """ - retrieve all type ids - """ - typekeys = SDL.find_and_get(A1NS, TYPE_PREFIX).keys() - # policy types are ints but they get butchered to strings in the KV - return [int(k.split(TYPE_PREFIX)[1]) for k in typekeys] - - -def store_policy_type(policy_type_id, body): - """ - store a policy type if it doesn't already exist - """ - if policy_type_id != body['policy_type_id']: - raise PolicyTypeIdMismatch("{0} vs. {1}".format(policy_type_id, body['policy_type_id'])) - key = _generate_type_key(policy_type_id) - if SDL.get(A1NS, key) is not None: - raise PolicyTypeAlreadyExists(policy_type_id) - SDL.set(A1NS, key, body) - - -def delete_policy_type(policy_type_id): - """ - delete a policy type; can only be done if there are no instances (business logic) - """ - pil = get_instance_list(policy_type_id) - if pil == []: # empty, can delete - SDL.delete(A1NS, _generate_type_key(policy_type_id)) - else: - raise CantDeleteNonEmptyType(policy_type_id) - - -def get_policy_type(policy_type_id): - """ - retrieve a type - """ - _type_is_valid(policy_type_id) - return SDL.get(A1NS, _generate_type_key(policy_type_id)) - - -# Instances - - -def store_policy_instance(policy_type_id, policy_instance_id, instance): - """ - Store a policy instance - """ - _type_is_valid(policy_type_id) - creation_timestamp = time.time() - - # store the instance - operation = "CREATE" - key = _generate_instance_key(policy_type_id, policy_instance_id) - if SDL.get(A1NS, key) is not None: - operation = "UPDATE" - # Reset the statuses because this is a new policy instance, even if it was overwritten - _clear_handlers(policy_type_id, policy_instance_id) # delete all the handlers - SDL.set(A1NS, key, instance) - - metadata_key = _generate_instance_metadata_key(policy_type_id, policy_instance_id) - SDL.set(A1NS, metadata_key, {"created_at": creation_timestamp, "has_been_deleted": False}) - - return operation - - -def get_policy_instance(policy_type_id, policy_instance_id): - """ - Retrieve a policy instance - """ - _instance_is_valid(policy_type_id, policy_instance_id) - return SDL.get(A1NS, _generate_instance_key(policy_type_id, policy_instance_id)) - - -def get_instance_list(policy_type_id): - """ - retrieve all instance ids for a type - """ - return _get_instance_list(policy_type_id) - - -def delete_policy_instance(policy_type_id, policy_instance_id): - """ - initially sets has_been_deleted in the status - then launches a thread that waits until the relevent timer expires, and finally deletes the instance - """ - _instance_is_valid(policy_type_id, policy_instance_id) - - # set the metadata first - deleted_timestamp = time.time() - metadata_key = _generate_instance_metadata_key(policy_type_id, policy_instance_id) - existing_metadata = _get_metadata(policy_type_id, policy_instance_id) - SDL.set( - A1NS, - metadata_key, - {"created_at": existing_metadata["created_at"], "has_been_deleted": True, "deleted_at": deleted_timestamp}, - ) - - # wait, then delete - vector = _get_statuses(policy_type_id, policy_instance_id) - if vector == []: - # handler is empty; we wait for t1 to expire then goodnight - clos = lambda: _delete_after(policy_type_id, policy_instance_id, INSTANCE_DELETE_NO_RESP_TTL) - else: - # handler is not empty, we wait max t1,t2 to expire then goodnight - clos = lambda: _delete_after( - policy_type_id, policy_instance_id, max(INSTANCE_DELETE_RESP_TTL, INSTANCE_DELETE_NO_RESP_TTL) - ) - Thread(target=clos).start() - - -# Statuses - - -def set_policy_instance_status(policy_type_id, policy_instance_id, handler_id, status): - """ - update the database status for a handler - called from a1's rmr thread - """ - _type_is_valid(policy_type_id) - _instance_is_valid(policy_type_id, policy_instance_id) - SDL.set(A1NS, _generate_handler_key(policy_type_id, policy_instance_id, handler_id), status) - - -def get_policy_instance_status(policy_type_id, policy_instance_id): - """ - Gets the status of an instance - """ - _instance_is_valid(policy_type_id, policy_instance_id) - metadata = _get_metadata(policy_type_id, policy_instance_id) - metadata["instance_status"] = "NOT IN EFFECT" - for i in _get_statuses(policy_type_id, policy_instance_id): - if i == "OK": - metadata["instance_status"] = "IN EFFECT" - break - return metadata diff --git a/a1/exceptions.py b/a1/exceptions.py deleted file mode 100644 index b7d1244..0000000 --- a/a1/exceptions.py +++ /dev/null @@ -1,47 +0,0 @@ -# ================================================================================== -# Copyright (c) 2019 Nokia -# Copyright (c) 2018-2019 AT&T Intellectual Property. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ================================================================================== -""" -Custom Exceptions -""" - - -class A1Error(Exception): - """A base class for A1 exceptions.""" - - def __init__(self, message): - # Call the base class constructor with the parameters it needs - super(A1Error, self).__init__(message) - - -class CantDeleteNonEmptyType(A1Error): - """tried to delete a type that isn't empty""" - - -class PolicyInstanceNotFound(A1Error): - """a policy instance cannot be found""" - - -class PolicyTypeNotFound(A1Error): - """a policy type instance cannot be found""" - - -class PolicyTypeAlreadyExists(A1Error): - """a policy type already exists and replace not supported at this time""" - - -class PolicyTypeIdMismatch(A1Error): - """a policy type request path ID differs from its body ID""" diff --git a/a1/messages.py b/a1/messages.py deleted file mode 100644 index 3663053..0000000 --- a/a1/messages.py +++ /dev/null @@ -1,41 +0,0 @@ -# ================================================================================== -# Copyright (c) 2019 Nokia -# Copyright (c) 2018-2019 AT&T Intellectual Property. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ================================================================================== -""" -rmr messages -""" - - -def a1_to_handler(operation, policy_type_id, policy_instance_id, payload=None): - """ - used to create the payloads that get sent to downstream policy handlers - """ - return { - "operation": operation, - "policy_type_id": policy_type_id, - "policy_instance_id": policy_instance_id, - "payload": payload, - } - - -def ei_to_handler(ei_job_id, payload=None): - """ - used to create the payloads that get sent to downstream policy handlers - """ - return { - "ei_job_id": ei_job_id, - "payload": payload, - } diff --git a/a1/openapi.yaml b/a1/openapi.yaml deleted file mode 100644 index 9c90599..0000000 --- a/a1/openapi.yaml +++ /dev/null @@ -1,372 +0,0 @@ -# ================================================================================== -# Copyright (c) 2019-2020 Nokia -# Copyright (c) 2018-2020 AT&T Intellectual Property. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ================================================================================== -openapi: 3.0.0 -info: - version: 2.1.0 - title: RIC A1 -paths: - '/a1-p/healthcheck': - get: - description: > - Perform a healthcheck on a1 - tags: - - A1 Mediator - operationId: a1.controller.get_healthcheck - responses: - 200: - description: > - A1 is healthy. - Anything other than a 200 should be considered a1 as failing - - '/a1-p/policytypes': - get: - description: "Get a list of all registered policy type ids" - tags: - - A1 Mediator - operationId: a1.controller.get_all_policy_types - responses: - 200: - description: "list of all registered policy type ids" - content: - application/json: - schema: - type: array - items: - "$ref": "#/components/schemas/policy_type_id" - example: [20000, 20020] - 503: - description: "Potentially transient backend database error. Client should attempt to retry later." - - '/a1-p/policytypes/{policy_type_id}': - parameters: - - name: policy_type_id - in: path - required: true - schema: - "$ref": "#/components/schemas/policy_type_id" - get: - description: > - Get this policy type - tags: - - A1 Mediator - operationId: a1.controller.get_policy_type - responses: - '200': - description: "policy type successfully found" - content: - application/json: - schema: - "$ref": "#/components/schemas/policy_type_schema" - '404': - description: > - policy type not found - '503': - description: "Potentially transient backend database error. Client should attempt to retry later." - delete: - description: > - Delete this policy type. Can only be performed if there are no instances of this type - tags: - - A1 Mediator - operationId: a1.controller.delete_policy_type - responses: - '204': - description: > - policy type successfully deleted - '400': - description: > - Policy type cannot be deleted because there are instances - All instances must be removed before a policy type can be deleted - '404': - description: > - policy type not found - '503': - description: "Potentially transient backend database error. Client should attempt to retry later." - put: - description: > - Create a new policy type . - Replace is not currently allowed; to replace, for now do a DELETE and then a PUT again. - - tags: - - A1 Mediator - operationId: a1.controller.create_policy_type - requestBody: - content: - application/json: - schema: - "$ref": "#/components/schemas/policy_type_schema" - example: - name: admission_control_policy - description: various parameters to control admission of dual connection - policy_type_id: 20000 - create_schema: - $schema: 'http://json-schema.org/draft-07/schema#' - type: object - properties: - enforce: - type: boolean - default: true - 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 - additionalProperties: false - - responses: - '201': - description: "policy type successfully created" - '400': - description: "illegal ID, or object already existed" - '503': - description: "Potentially transient backend database error. Client should attempt to retry later." - - '/a1-p/policytypes/{policy_type_id}/policies': - parameters: - - name: policy_type_id - in: path - required: true - schema: - "$ref": "#/components/schemas/policy_type_id" - get: - description: "get a list of all policy instance ids for this policy type id" - tags: - - A1 Mediator - operationId: a1.controller.get_all_instances_for_type - responses: - 200: - description: "list of all policy instance ids for this policy type id" - content: - application/json: - schema: - type: array - items: - "$ref": "#/components/schemas/policy_instance_id" - example: ["3d2157af-6a8f-4a7c-810f-38c2f824bf12", "06911bfc-c127-444a-8eb1-1bffad27cc3d"] - '503': - description: "Potentially transient backend database error. Client should attempt to retry later." - - - '/a1-p/policytypes/{policy_type_id}/policies/{policy_instance_id}': - parameters: - - name: policy_type_id - in: path - required: true - schema: - "$ref": "#/components/schemas/policy_type_id" - - - name: policy_instance_id - in: path - required: true - schema: - "$ref": "#/components/schemas/policy_instance_id" - - get: - description: > - Retrieve the policy instance - - tags: - - A1 Mediator - operationId: a1.controller.get_policy_instance - responses: - '200': - description: > - The policy instance. - the schema of this object is defined by the create_schema field of the policy type - content: - application/json: - schema: - type: object - '404': - description: > - there is no policy instance with this policy_instance_id or there is no policy type with this policy_type_id - '503': - description: "Potentially transient backend database error. Client should attempt to retry later." - - delete: - description: > - Delete this policy instance - - tags: - - A1 Mediator - operationId: a1.controller.delete_policy_instance - responses: - '202': - description: > - policy instance deletion initiated - '404': - description: > - there is no policy instance with this policy_instance_id or there is no policy type with this policy_type_id - '503': - description: "Potentially transient backend database error. Client should attempt to retry later." - - put: - description: > - Create or replace a policy instance of type policy_type_id. - The schema of the PUT body is defined by the create_schema field of the policy type. - - tags: - - A1 Mediator - operationId: a1.controller.create_or_replace_policy_instance - requestBody: - content: - application/json: - schema: - type: object - description: > - the schema of this object is defined by the create_schema field of the policy type - example: - enforce: true - window_length: 10 - blocking_rate: 20 - trigger_threshold: 10 - - responses: - '202': - description: > - Policy instance creation initiated - '400': - description: > - Bad PUT body for this policy instance - '404': - description: > - There is no policy type with this policy_type_id - '503': - description: "Potentially transient backend database error. Client should attempt to retry later." - - '/a1-p/policytypes/{policy_type_id}/policies/{policy_instance_id}/status': - parameters: - - name: policy_type_id - in: path - required: true - schema: - "$ref": "#/components/schemas/policy_type_id" - - - name: policy_instance_id - in: path - required: true - schema: - "$ref": "#/components/schemas/policy_instance_id" - - get: - description: > - Retrieve the policy instance status across all handlers of the policy - If this endpoint returns successfully (200), it is either IN EFFECT or NOT IN EFFECT. - IN EFFECT is returned if at least one policy handler in the RIC is implementing the policy - NOT IN EFFECT is returned otherwise - If a policy instance is successfully deleted, this endpoint will return a 404 (not a 200) - tags: - - A1 Mediator - operationId: a1.controller.get_policy_instance_status - responses: - '200': - description: > - successfully retrieved the status - content: - application/json: - schema: - type: object - properties: - instance_status: - type: string - enum: - - IN EFFECT - - NOT IN EFFECT - has_been_deleted: - type: boolean - created_at: - type: string - format: date-time - - '404': - description: > - there is no policy instance with this policy_instance_id or there is no policy type with this policy_type_id - '503': - description: "Potentially transient backend database error. Client should attempt to retry later." - - '/data-delivery': - - post: - description: > - Deliver data produced by data producer. - tags: - - A1 EI Data Delivery - operationId: a1.controller.data_delivery - requestBody: - content: - application/json: - schema: - type: object - description: > - object to represent data object - responses: - '200': - description: > - successfully delivered data from data producer - - '404': - description: > - no job id defined for this data delivery - -components: - schemas: - policy_type_schema: - type: object - required: - - name - - description - - policy_type_id - - create_schema - additionalProperties: false - properties: - name: - type: string - description: name of the policy type - description: - type: string - description: description of the policy type - policy_type_id: - description: the integer of the policy type - type: integer - create_schema: - type: object - description: > - jsonschema (following http://json-schema.org/draft-07/schema) of the CREATE payload to be sent to handlers of this policy - - policy_type_id: - description: > - represents a policy type identifier. Currently this is restricted to an integer range. - type: integer - minimum: 1 - maximum: 2147483647 - - policy_instance_id: - description: > - represents a policy instance identifier. UUIDs are advisable but can be any string - type: string - example: "3d2157af-6a8f-4a7c-810f-38c2f824bf12" diff --git a/a1/run.py b/a1/run.py deleted file mode 100644 index e13d199..0000000 --- a/a1/run.py +++ /dev/null @@ -1,43 +0,0 @@ -# ================================================================================== -# Copyright (c) 2019 Nokia -# Copyright (c) 2018-2019 AT&T Intellectual Property. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ================================================================================== -""" -A1 entrypoint -""" -from os import environ -from gevent.pywsgi import WSGIServer -from mdclogpy import Logger -from a1 import app -from a1 import a1rmr - - -mdc_logger = Logger() -mdc_logger.mdclog_format_init(configmap_monitor=True) - - -def main(): - """Entrypoint""" - mdc_logger.debug("A1Mediator starts") - # start rmr thread - mdc_logger.debug("Starting RMR thread with RMR_RTG_SVC {0}, RMR_SEED_RT {1}".format(environ.get('RMR_RTG_SVC'), environ.get('RMR_SEED_RT'))) - mdc_logger.debug("RMR initialization must complete before webserver can start") - a1rmr.start_rmr_thread() - mdc_logger.debug("RMR initialization complete") - # start webserver - port = 10000 - mdc_logger.debug("Starting gevent webserver on port {0}".format(port)) - http_server = WSGIServer(("", port), app) - http_server.serve_forever() diff --git a/a1-go/api/swagger.yaml b/api/swagger.yaml similarity index 100% rename from a1-go/api/swagger.yaml rename to api/swagger.yaml diff --git a/a1-go/cmd/a1.go b/cmd/a1.go similarity index 100% rename from a1-go/cmd/a1.go rename to cmd/a1.go diff --git a/a1-go/config/config.yaml b/config/config.yaml similarity index 100% rename from a1-go/config/config.yaml rename to config/config.yaml diff --git a/a1-go/config/config_test.yaml b/config/config_test.yaml similarity index 100% rename from a1-go/config/config_test.yaml rename to config/config_test.yaml diff --git a/container-tag.yaml b/container-tag.yaml deleted file mode 100644 index 9b116a4..0000000 --- a/container-tag.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# 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.5.2 diff --git a/docs/_static/logo.png b/docs/_static/logo.png deleted file mode 100644 index c3b6ce56468d87a3d9463ee75297b3895fc9a414..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43935 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

r@f1gRWk|A*%4`3la7*fhEBO++ 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 diff --git a/docs/a1_xapp_contract_openapi.yaml b/docs/a1_xapp_contract_openapi.yaml deleted file mode 100644 index b28198f..0000000 --- a/docs/a1_xapp_contract_openapi.yaml +++ /dev/null @@ -1,93 +0,0 @@ -openapi: 3.0.0 -info: - version: 1.0.0 - title: Contract between A1 and RIC Xapps - -components: - schemas: - - policy_type_id: - description: > - represents a policy type identifier. Currently this is restricted to an integer range. - type: integer - minimum: 1 - maximum: 2147483647 - - policy_instance_id: - description: > - represents a policy instance identifier. UUIDs are advisable but can be any string - type: string - example: "3d2157af-6a8f-4a7c-810f-38c2f824bf12" - - policy_query_schema: - type: object - required: - - policy_type_id - additionalProperties: false - properties: - policy_type_id: - "$ref": "#/components/schemas/policy_type_id" - - downstream_message_schema: - type: object - required: - - operation - - policy_type_id - - policy_instance_id - - payload - additionalProperties: false - properties: - operation: - description: the operation being performed - type: string - enum: - - CREATE - - DELETE - - UPDATE - policy_type_id: - "$ref": "#/components/schemas/policy_type_id" - policy_instance_id: - "$ref": "#/components/schemas/policy_instance_id" - payload: - description: payload for this operation - type: object - example: - operation: CREATE - policy_type_id: 12345678 - policy_instance_id: 3d2157af-6a8f-4a7c-810f-38c2f824bf12 - payload: - enforce: true - window_length: 10 - blocking_rate: 20 - trigger_threshold: 10 - - downstream_notification_schema: - type: object - required: - - policy_type_id - - policy_instance_id - - handler_id - - status - additionalProperties: false - properties: - policy_type_id: - "$ref": "#/components/schemas/policy_type_id" - policy_instance_id: - "$ref": "#/components/schemas/policy_instance_id" - handler_id: - description: > - id of the policy handler - type: string - status: - description: > - the status of this policy instance in this handler - type: string - enum: - - OK - - ERROR - - DELETED - example: - policy_type_id: 12345678 - policy_instance_id: 3d2157af-6a8f-4a7c-810f-38c2f824bf12 - handler_id: 1234-5678 - status: OK diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index 974c309..0000000 --- a/docs/conf.py +++ /dev/null @@ -1,3 +0,0 @@ -from docs_conf.conf import * - -linkcheck_ignore = ["http://localhost.*", "http://127.0.0.1.*", "https://gerrit.o-ran-sc.org.*"] diff --git a/docs/conf.yaml b/docs/conf.yaml deleted file mode 100644 index 721e54b..0000000 --- a/docs/conf.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -project_cfg: oran -project: A1 Mediator diff --git a/docs/deleted_flowchart.pdf b/docs/deleted_flowchart.pdf deleted file mode 100644 index 0089b29b35ada5436abbbf1ae5b2d598e60e60dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21180 zcmcG#WmF)`vL=eVHZ<<;?v1;G8_de&`ckiru^J5lN zWn^SVMr0Aiiij_ZSXNM&nx2Lcinw?0YVV}*I(xFWABqW&4$oT842qKzk5RTHa;&F3B**n-7>RLj%0A{4EL}3lt23?$xUk#f8 zEgKmc8Y(C1x>WDW2NAg&QV-CLE)!6}{aF|aI#hBR z#wq-Qcxik(aSnfOta6=YZSS;RxXW$1`rh>I{%X$O*%~+98Q%J~)vIOg>GUV(!nyF( zTx6@=1@SC0{<|b!Z{_~^MyuOepSbte$A*iurNY6(=0Xjw_4omfJM;Dk?Z^ui1!!EA zM#<6EjjMwRL%+&(li`>9{ja6>cMq^!1G^rgWl!^gEQ?L;C~KfbJu^}Tg|hCL4i5$% z?drpAy2b<4W%YsJU?pgW8#L)MJuqLi3}9E7Of&RPXsp&o!l8}!Y1UikIy*aTXjl8P zu-}gt??}AbI$0_N)=qC4o4wkWJ?!qCwO7n*xYVVu_6DBQP8zjwIz3i7+g?y#?O)He zC3fo7D=u(wXfM`FZiQ}p)g2&$?@3tAHx4rD)9FtoNPa&&>f(56m`Yu^E9zJy} zJP!N{y)O!gw|pSir)8ya%6ZWNtBP^+J`AZVQ*}Q6mLJrizcFQga?855B%>rzX;Odj z6SpE!eqySk#(yr+sh)%GLQCUhV~zS{%!c9o_-actQg&kfzQU3X%SSH<$=jiHBu$1E zi?D(v>7t{F$C72b`Q0A1vpnkP>g};ntMRM3yOk*i=1}E0GG1_kIlSp=(_BOrby3?j^SVW!Zx~WYQf)bQQv(d@A(EhE zHCx-6WhlF#X#A-2(|6^Hbc~iK9E~Tcjw~x%g|n#f<|p$BxF99a#pp`#Wqhj0{PSe7 zk7n@=Q=J?wX_tlZP(G}uT91HeTH@`+(!fU@HB9XOU)Bp+v>VR?Je2sRS%Uz`p4M71 z5q^uwa!oo%6F9U>NHvao)=%Ef?G?%-X|L|z?d&93A9xvu6fXh2ZXvXAH;u0THn*%K zg;T_#SzrUa6P?g9Tj&PzoC3wJ%nvGLdtblg9M(EYE4lGN4v;^b#L3af72uj;p8rai z%E^5%hE#kb1a)=ugF*%|BVy0_En`piuK2(+Uhmyd=G(Ya=yh?0B?{!2#wj)1)z1DU zqv%!S#N6??7LS$HTKl++tlcd2c~&;8Z_d12s+{HH;x^{0OzGn(9NB#=4&!i}BKXgj zG~Cq_?f0kKQfL4K8@1Qtlb>n7VLa24)+)UoUIWv>9*+8k#0KX5U$DK1pByU$NZ7j1 z=d1Y^^!#1*@6)d#e8Tof08U#1q;AVce9K2N0)@3|rKxko`^^~F5wOqYzc+Ra)zzCz z$NqwZ;Usz`f`a0cS4hH7*=)I&qV&8ihC$!6SQh>Xw4I1g7^`rY65VxK3cjRM8(%svEXX_%mJLRNlw{lZJ<_hjkc)woIL zO#*_c*M)n2P>Z9#0vuMtWx?Li(j}jnkNL91*~#GytHX0YEq8(uq`{TbC@swSBFbHz*wDgRRo;S27{p_vu5*q!dw zI?=aA#1yG=`{xtZPZ(2r!8A3&o|B(T?AJWXrSI5u2G>=vuC?|l*d96*O>Rz= zP2WM;X?|#t4O8(blXh`n==xS1+6%no!)3E;jhtX+Jm~-1esh{x@Wcgfs+el z--Ho|{6qNjZ}A5lclMlI@xD;`xQn_mbO@wTR+)sDS`0>2KT8fur=4mE=uu^Z0d#li zU9cE{|HwztNxY1lo=bF~?NXYy5J>w+yuHN@;U%*_S~!&~x3q996+h%A+};OSUoZ6$6-Tpg(aW zq#%YN)>own(8$WO&2Ggr#-AO*k0_!H#v*MDrz5?&xhPGyR@+e_v(r8!QBNS zgyEMFd+)LJM(11f0%~RFdx8Rm!;csCMSC6C%`xc-1@+texORewBP1zfIR$1lCOOvS zEe%UplSEfKTv`EDJ#qetoUkx%76i61;k{8hT`Z796L5G~9D#*tY+EN%r~-nS^(4>f zu94wo(O=sAaVkiiYJl23KCsA~DZ;ClVF ztcSo@q0O6a%UN2omenDme)Nvj2&65|ta9A52`vy6PB;ef`Q)ZlyAdO`_KrCTGiUxl zF^p10^K0I$Bdo@v>E$I9rYGJDrf_qozv7p{nrP@X#f$Mmde|)W0c3R0ks+1y8Wg5& zpbjl&4v)elPM)$KRRLU&0_FAdS#&7wqs}ufJbcVr6}qm&<(VWuROLSeOI{8PAmu;o zIhDn04VU>*}yjR`&{@#|MG%7d@U1_1tYY2aYV zOZ!Z`@)Ne;pwiC6--3B0vw=pA2%|D?gQP~~NeW@YC1YA?8jz2kvgR@#pf2}utl`cO zwF3j1fXz2qbkz;<%gr=Wk!bE@b7bZ|%M=sQh*_G!0Sj@(GnZGSX^Q#OD2Lo^j=zCc zjM;$X2tNYkedy{p`ZNw(D4>Z7gGc=|7E3e2;o#b7{IME)Oefpy0aG>O-5uDi*0G4z zFIG5gJq0vbanZ)DoMBFmE+dhMFzGeclG3X*>MEdekNpJ>mhHDEN{`sa+E)c}ygy`5 zRSjD)K#N7$-KVVsi*ypafB_wGO^C#MN%ph4sGfC@arpMy;yKBIB2$vZ-Ii&c%GEuc z@B6g8ULH1aa4M~>GW!!>t76ccGcNtHz&&&tM-i158gZ>z8q-<_PFmqN__xi<7~^7) zSC|{wEuaJmk_N&FwPA05uf6Upz1HmY0##f<7<(=rIfv*$rFbe&$J;c8fxF@JU447) zc)J|fcyDrqwkhN#;Gck=)S{mYqdK13Ib@D!RemFC^SagzTs z+wm8!PpS!_jV~OC@3UzcMz3Q8szy}Al9!dh6 zfoW%uj9o+{8mLx|oOV_EW9U9abO56w6{1PQgW$^$g`veu5tH@>7($K!phQ)DQ)gm~ zR}Q-as=5XHhJ(WNMOxfs#54kC+k(@(ECO*QsnAhNas1$WdgxbW>!olDuGYPw~x zI(6#(?YG~+BP(@fsV@e}$jeX~#}lQGD~goFB1~xe7(`Bax4rSNz0>U3 zpn*Ad7|0geYVCtKl#A;H;}PgP;-6nNXJh-K$Rwtg;3wJCa?8EMz|z{ zGZ8vN01w^Tf%I{MAR5OB7)|j`BZF_VM{IkQZ5wNUOkVTCmkF5?B5bhs7d( zbb*7L4BHSe4!sCEVi!QYW`4K4a~$$TllFixtRPH+a_ZR8@k*B&dQK4F3b_K)g|9&s z_(0CMYFxBci)?OWZT?2PMTBA)`h=xDsa>PkwX)7A1x~oDD z*;y&|>2>B_Z@u{bI!?p)cG5|!dg|i7wH{^P;?dU8sbJX`w}YeR`U?~Bohq*`d_Urh zyHd1<3dPN!QgFJV^^2Y5`0{zB2Fn~gWUF?CyLqidcWwKI@-G!9H}$K-p$LCUR=w@# zCJx0A2GukbzD^soK)rI2>YLLLN-1wN>X*EU?X;S6r_uQ0Lo7ncU6GR<# z=uXZ@L%z}8@-os-Z9Xq!zA`>W1{OhVPQ_q3Or0cQOG)X6 zTQV=BXeGWgn4eSC1UBV~#x2am?n^nmIab$lA}P!>~*a#7Nd z`C(pHmZ*crZintuz>^Pwqzsii8ek;bkz!rfzHr^b4Yv#OuvQrD~u;TeyGj#w_`>lW_BKxhlt|jeTM0@MYiH!Hgz9)JQ(7gDFVLK zCP^ev!&x@u=0rjROb&Pq;+Z=u2&5s~bPf!iOXoMPtgjJJPJ*=VbTh~5ShvD8a*W2n z+7PhR0b^5y;gTWMN{DJL-pOAU!(Qo``7`(g&GJRoAYdq9M!fstWVM!^m1!-^rl#=g zplQ4f1ozcU9``uPdOvtev}&P18Cn_qU2prKKFf5UwYkrhiJgV{bM%vD_>U5vqN|M| z9<8jd@t?b$p_Kz3<6q@Gc|&_^M>~B(dpwrE7y{N-4j<$8c%LP}k9wb!p@FF`zqJdV z2HgjPo{5bOkDi`|MGJ~n-rC`#1o*-HDF6Mf^K*@V%7(Ib*7^#D4tN?LiUo!7XcY}z z9PqU8Xa%e-tnCzRboCAKKC6oY_6&Gzf2k6%r^jRcLvnL}6c|4Y`IrGktEfni$Nmrd z-@52O?fFO9-!)2l`oC6^{@5NZ-)EuH>eED8K|?1~eM5N>{{J8K%S2~Ig(Z~WiFVfZ z$u9(W#5Wjjka*MIM8AT_g^Kye0tE$-I0=FZBO{V48z}w)6bUBK7ea>i_tRT|yh4!e z?~)S~+FOe-hrB3fzFtp#9Z=g?I%r&2GQRL!XaSIY@dN30PzCg1%27o=Kk3r^f;7^5 z4}^vQ2&W48Ys<(8MC3sbX!fO@DT5oW{qXb%m-^mc)Mk-8+_mRe%T#07__% zh0}`!bq^?Bp|%g5;T>f7CV`wGh@Jr@2Qs&eV5hgz;;wL?vUMmqiDWkiLv0+rjVUvt zRdzjX7k=-N{z~!g$ZWu_YnBGssg=Evc!$G5`%UXp;J$lOc9;vMm7s-j6I+!Rr zRG&)gL?J+8HOmx}*t2r1vop2%eUrz|r41Y}g;%aYw`ZkGlGBMyzKBe<4U5w<+IAFx zJ0kTo?Ane5C4f3hFy6hbGMNiJfGbddy7lyHoFI$uKYF)eNTI_|Pc7^Lv&?BI4_TkH z{H%vNgV^s=M$Vs=zx_nz@g)@d%4jIG_p|V)gy85X3|!KL!RtN3`_3_=xaA1$9Z#0j~R+Liqhg_?pXC5`&TtpTa+nfT<4QD!?bp zh78;%5E_H=n>605KV8Mi+JwpuuOU z>Khh>NcYI5pv%{EaMWKbTjEV9+;ENEs9Tu70jL7CyW=2u2O)r`gjFFhF9rEyREYo) z@y%li@_~wlQe)uqF`FYrV{LbV!Gd#ilhIK6(RYH?BDVCDb?5cO>E@F4la(b=_gRb( z7=0i83iRfxf2xv}@i;-UAf`o9_bTf|>u6LZRRb^To_@oE8~BC3jb-E2gr>=0#asc> z2)_`~>T}WqyG3&8(nhh4eBlS*W&c~x1HS{x8_OGKBO15AlQafW9mL!Z9zPL}$bv`_ z0R!Ry;xz!Vn@P^MNXCI62*I^iQ;)nlYLTx^42wiM4p}US2vwelJf+T8QOt}$i-e1$ z9Y1b}!$8CiO^x@vOa)OEo;ET34<6ZgG7@ANaV#PJC24l?W?olbSAha~*dnW0)sp0v z40V}yK~Hf{T9G)$r0b-eq4K!WSo4IFBzIz&_=P0rByE~3Qf+*1@wcBZO+a#8yj=>u zc~#0e!drFST!CUjsksSyA$qBLp?dz?%zbjiZ9jZ}G=7mO<~^#KCN99o;GZdQoo=d= zs^eHTT&BYoVa!#CrV3bEMh=_}5jflQx z+HaoNnOI?|H2wbGMxO9UVMXDQK#*XR0GJS6BB^9Ohdh^2Vp$?HOJ_!6rfarj_B=;Z z8aYuo;hZ^NT-JP@UKa{Wu0)ow63C@u$)faOx?UKeZrF1 z(kY5O^}KKWZ2AqhVVB%H8YESr8Dc-gB8Q|D7H8KLby_CsCSc6>O-PtZnOg^Pt5((2 z%7o{$*?Tw#J@Q=B9x0)@!d1gFP}Weism`brs86VH)yUOARW0j|tDbt6dsEG|^gRcH z%y#r^$A7Nv*OwPlZ#Z?Hd45aPs#-&7m2RE5gS#VmCV8d@=L&WSZil;HbZ{bT4e*>p zJUQHI8o!L#0Vj1Qt)3Ap+{jQHCm)9$&x3|R{f#I6fvmkDRf1s(8#a zpzfh=hM!nBtcp^Lk&CpQwudQ?PM~GjbnaXUDVKLDR+7wK&8Rgw>P_-iG=eCN*k290 z)~sgTSi~@ESZ)+?|7JIJ*J=cN=z8eci`j_T+0nSe=`HA`i}~Cft~_wQLUW~A+F7fs zW$U)%B-#?sQgrF}i10|HA?Aiv*!rwowp>+*j!Essem{RRzuZ8@^-^-P@P9=CKFduw|Oo{3(@PKX4G6by7n zJI;NZU0^L@jc4hO{Lx>%V>@8|x_K+$j#PRi%b3^v0K$gPez`sHfP0Y%!@1-d=2>z? z`kHmuFsUAsP0f?`?r{@wmy%jLY%DPmJN`92jXlfR z>x#cW@G?9UHJC%ifxD${cRHh@s$zY_H>vXbWwTxJO8fJRQc|gL1-sTa&kWD@v!K_= zd7ReFMD0^A)1&$e%l0Fi+SkOL@~93_&x0qeXWI3<7SFw?_Y6$naImLynhy8(@2gx# zp6jnu;KlIYc}CsXUQJ&0Pj_-jp=2mBOL)p(=N=Y}ira5avlnzGYD0{pgPH_r-xD4y zWqLp-iYF+A>x7R*%0(t39wXM=s&D#!k3=NR>;!g_zct(^OqFJNIX|}>>kak(HlI34 z;Spai>R^AGzq33rINY6aHof>+7`9&8!E@*Pn)9rGGZ1@pD)S(dkQK?}>;d_5!|^|1=${yzg`VM`*!VNZ{p5Yb!(svg{JQpr26%sBSb03Hf9BBtXVCsn zO#64FE{#Y3Kg6^mcDklPP zpHCB6AJjZG9M)IrF!?%lL^7J$?QARAlO|aS&;pdwrYg4y$n43x=6mjmv3IPudk0NyAzZHzA?(wQmg-T7<4P`s!`KqA@0dwXJwQ&WgGPzVQMpod(S z_chQuqINxiXUw*zop)p&d^qsq9H1JAJkd$O0Q;~2<IfP4GUSHuv({ACGW zaWMgGDFC1-^2~G4k^>-mfr>*0U}EjUlkr#6v1n$Nrc%(U$D~jV-~H!Irrxhdh7$2x zrzSvH657g|Y#y8f?vgH>eIZ%}Jdy`s!!Y}zp#?)@5uSm4(!{O`2l%nTE;-edfiU&l zLr!K2x*!pr`8_nqpoLs800N||>9`^F^h0`juL<(-?gCT?N1#Q07s0kMa(w)+K=g$* zk@xJk@q?a13a3|9nKn=WrqgI4U@&6ij?|jB2EiD~-f1fF;D+{CP@#ciGGtMg5{6*I zgBGE-QTcR@~$EQIS7l_4rlHkH0O1y^Y4|tNM2p8%Qz%}gxvIPpm-|myIU?Kz2L!(ru z6zy%|1+eSld`JTC%?79A!$I%`Vjl+3i~+_(QrCgkfJi3}l7cec^`#=4@M*f=VLQ(I zY7z+&wd?1=#)w&g8OH#KTY;+fdpp_TdFAPv_k@+x0kBi)3ljz11%QRh&jjo+ zLL4~Rfwc(lBae89eJ7?pY|#064OHFBakp zr5|?`y-t7{XEVqi0VX#Yt#{+K!np9=;|LrY{z5q0NaBSbS|Tv!a2apr0zy$1^mQzF z9<@KG(5j731bS90BK3|mue(?&j*e3~rz0TU$z1BhNc~{mnixyLx~wU!7-}VbjOhMs zA`CQ9marTS$Oxgfu%Ep9JpMd@Iecm6ZTjuz49q;sduf3hcKyfKqRSczYQzGJBiv_S zX3S8N{(Ta3E@=V>FrmNej4xT}Pa203F(`VI3qb6WH4XIiyVMM>a~^Jzl64cbGllOr z90KSzA44+mD?+!VxUPZ3F9umSiY|;QL<`VwT{yDf7{7FFVCwv4HogY)ZCFo@7CMJ>~-Y zJ(_j9@KVBsbUl)%D;R>842dIve2cdU2~ZF@mOmd!UXNamPZ5z>5V#ERtQR&n4GnKS zMo~V_;0GLCc#Z+k5aB^w_)x;GvK{V{XeP0B4A(B+V?K9$}-=?TBT14+LdLM>EvvbA(Tbs5Gdm*6vd0b>-)^+%*M&wO;6|9@-AiLCYcssCU;G}em09-ryEtGD2=Fx=Oq*? z72ihA8-9douxF;rq052GbISwut&R}bl-N!1dr0>gOR4hEAd6F$x3sbM(LUr}Se+$e`V)nWR3XhDfp`r!%>Z z&KlLEERBwh){gQi2+YVTq#V)AjQs2|nm2+pDlh^cJ5QrbX-z9kv!=6V#I2*NPS%yv zv;DPADj_7{ERnNEGU7C%qCBrGraV#}rFN@!qGn$fUJkFiQJb^!ur%6OT;0r`!m;FO zH@|xpSGm01K-0j#Vzxr*)Ra-0F=*Ag#_S%}9^Agz(f^j{9|pXV^J~jTAUZEfKR~~D zhkl??f$C?zq}sOv&tdmOXi7}4wQ~+rwm|j-j(WD&EsK*;`wFKH_5^Nbm*XGBQVLm8 zwP_^D4f@NLs8UJjG$UHenr{w{_6aR4O?RzoO$E)C_N{vrdpKtu)30sdefli}U!s+x zSu-m#A2WlwyzlLP4__@`%pF*sQU6}wd7T^>4L+$IDO^c9O51-%a3X2Ve0AV3Z*h2bw66*qA?T%OdxB7}=p7(j=yht@!LV$me#r13C+m_TemoDNAue~73;KQUen zuZuty!3hVFOE0>WyU#1nI}q{46^IOtDvo+&o+oc0YT%M)w6a=#Yf8GD*-;tTkg5`k zA+94XA^Ax%Puw96C8?e?%Vj@2D#S=jj7b#!BTe3fJe0hiT%gv^$TEE{?f8d!<}&k& zo72`Qv^~te!2~|ax2ah*!r}ieH zDyd8Sd0Yq0M29gc~Y7+U0sQ4Nhy@l`q%Ch7xW`K~!sv zlBR0&HMkOL1t;R#9*H+<5-D;^(>1|a!5&`O>2fsGo%O5ckK?q1w1V22-{~Fu62D$h zK72)`R7+4wxKO@Q{{Aw3G%}{`rJB%jd}X#U_ibKwA!Na!{8ppYI^(Hj+Yx?YLUp6b zw>d|zCp^-g^?OmOa=m@rG|AG>^D)Ga#hgx4V;X zFV0M-Ov`G`*JmoZl~xx}OLMCFN(%L9Ev-8DmF{2e>%7n29JN0FTNs{Yx%auZ-uIn< z(>PF8Y22`RKL&mPT=IQ;ntdwW0P&ic-JGmC$sX?-7TAnAo!qgW={V!S@@hH@d5#Ub zpz2(4QoJ@1BAi#9Wpj~qDQhKfK2o<=wbuw&8JW7k?%Y^0TUPhfe114a5+@y&_Fz-< zuse+3o&9wbF$2G{->}BTcs)8+OK(h@1F^}AKuwo}1T$-rEq`TByi=6$QzlkjFFu9cnD_v47uw)uof$7ki^ z^7ECK6;J1y3(n`_&BCo#i7n!e?T+Qw67V2+vp42PZSYpD*64eXJBJsn`|GvfwqO~r zq31m<>!r#Y?NXc+&XN=RbMrI!^Uf!i`JHvrAEZ6Nd!5Da4QIt;GZ$X>@9p|F`#Kkl zN44Jsw!8wSHs$4|&{Y>h+nkF*35S{*$A9A{75EkNeEq{v(h3NA45g_&4@q|Nli( zKC(kRR(6IzporrK+VPR#Nthbge?TUGulFC&3jKc`$^L6i0bK`O3v1)QfF1k45uJYn zB%hecr=kBg;t!-ktKg{T@COf4v~x84Yo*U@^shltJ}DVNNlH;e3nxPdQ+-`&L&p#N zTSE zx$fT`28NHGim8Ev$)9bqvO#^qIDh@HFn+cS%Yx91v%lOCdUo-HS89oH0bY1?qGv3Evn)xq7K0c8! zWWb~Ud<^pcx#3U7KQH7z#ht(K&cDC0_^_Fgj^RVw|LG%(Jq=d}4V9G6d(&EC^I9T_ z8YdpH33V#9MB$KHRfD6T&|#bxI&kp%c|EQz0Io22hR0}p@X_JI-R)xCy0!D7 zb;F(A?Uwmj4ffl(Vwv;C7MBYBV;zw)N~I`f+HB9Q;YZKCjnaJ^kJI0o<|Eg& z&}?uW`V(8P!?)DhLMtwFFR^)h`n1c@T8_7GSGOKO18L)#&o{sPMULGQ%zuyZVV@$G z8re&ZfjqWl<>Xlfv}wL!FPQA4;bzdIdXJP-Y=?6-d9gB|9*Kkn^1vklwOAM1vz5_A z<3*RMtBTWE&jCNYHl1r+iC%H-RYrjt$@~^t#jk1sc?bJ>Vq{6x1vDBY(FS=3`%+_j zRxCtUY?RpxwA1C=$-R55t>cEZNz;FNV;vxsW3F|q1IE{57GOUbmK8?`UFK%74LHt2 z*ZeEVO8wBdgK<-txwaHifY`swSKzhq(FzMfEIlN6Qi0gtzfdW*s~lsdD6Q`z*f>xJ3Z*g%)o2Ctx+Xx+vG6B-}(r$^Z&!nW^=%P~lyQrI9c8P>tG+aUI zu0B2$(99Q$0+A`r)ko4Gk;c8aPd`7)x(VR#@|`grAd${GyAL2CwM}qJQLvHRrsO>N zRm&2!Q5rATpbG;-?|MZP8A^zc!QpwgD>fk}N|1#p0G*y7!zvAI)n_ma?h2EK2TYU22H~DNo=V*Th7Fw>xK$1V*l!yI zu&wJ6L2K6HJ+~w26;oBoE&=S4;RTy?HMbZ_zJ80e#N_q)G}Utx6jz?fDh*S@JwTP3Kk%9y|6O z&&1A*>t#%}=WdUqR6t9m6%bq0Ne68;nGN3MHIu%D zZ$4-QtM-UpaB*qT;2`_(&=(EitZ9LY1CE+gumQ!2@06y7i2jQfbqQ~@97x4KQ3yp3 ztQoiF#x)s*XU;b_n=|B%1{@mz8+Z+d1GEMF2JetQ#I_ zt{M?Pu3(5BwH!~F7xno4mfayy!EpV4Ewv+Dt5_9#t$bsN)a>!(uDXKXqRtmAr_N2L z;-aC3T_tHGR7`pPih7OB28^V&#p7ra^LE(#?=0_L{(adBmU2rQ3L9AE*Ua|;`Ff+e z4sK(xT2eUn*S)d_FDl#`o#YBxp&VzhQZM+mJP387u|Itnx1AHXSsqPQh?bEpC_TEd z#OgtNEMwJGs1B})@{96V63-$R%1@*lekY0)C|YI@AxVnFvV5t@PvU5ndqA`R*DA_^ z{-RjEC=RPpjNgvYATRJ}_NSvu$!t*&R6bjk@WJd+J`VdJzfRAXKBP%IjqXP@BvKS7sV}9Mu;{-vOr-a`nblcO$8- zLWE;dqOf*v3#)#Ty0(N=RsnsHRZWQhg^8xAqpS`-XV~GeWc54Y3(*vOf8AU=gT&T) zSH*T7xHoK6)F^7y%S-=>HAn4p?F#j5xj&bll&JZ8jkr$P%?i>tu7ZcU{BG8f$8|7dh6U0N@pqxayjzQ`G9RB#+{r# zSMht0iiQC7mW{-q`WSPj9=G<5$NP3)?Y1gYt@zWwBd0sFCMerB+WK01y+CWSB1R8hXC))_9>4L>87ay zJDmNEl7lAP6XICqiH){*Na+Qj)rzp76e=WWQqI{f=r%6g>MQC6fV?`LKOJa?@3R19 zLp;#jjE{QRAb6ZE^(qzn)>gFjF8`!aKHETpd~9_W+DBd7Em)Y81$|csVum^0A)3Q} zR&K7wy2SWpn1r~MaE?3Ps+sB}w4&3`1LawR57e`06GX#AiE>%iUqzj3ZZqR4$j&^6 zxx|;wP0bPN&F&!_YJO_aro6uZ`P^)r)tDfc+PpSw717SXehF*i?nZ@c>SgBDaWneDDZEEjf8&l%12Nfe?q}kKrM;jXVlYV{=G@D5-|d7W&&InHUDiM^?eHR^ah7^M z8%=Fl>|DX2TA&xigGeskmri2Q7P7)fiX}8Bx|0Br;Oxd-(8EXvzBqbGtiZ7%+`tP! zXnW3ZgpglHWsJp+Z2eOd2kuTCH&;zU7O)5Iv;0~__^tuAwcm8AK{WSybro71-9>b8 z)I0g8NKRT;2+ASJ=g?&&Zt3ZD8km@jfW|OuGi^3NTo4k!4=kJwN=cm~0qwl4jBEg;4zsmFSNR3ux)_$~=QBSt**{JARNn9Huz+#~HEezn3cD-oR z(nYr+k1;^kadT#KENT{JC&R_^sf5CCtmXL+tcE8bXRkC&#dT=$C7~YxTadFpA>un_&BEoWd zp~bp}-C!DULc6bFw9ezW(M|-Pw09K536`i8&nl8XY9^Yatjys`ebkOF{ixpNVOMSAf#X3Ct_eAWL~WwC$*8F+epSJN{NdGc-H$R~NN7n2s;J z`ldh1LpCV4$^T3i{~3#A1W4`jm*a{@#Z=YYUj#S<*;}mn<#$-QNi?|#DYlz#+(#S> zS$V6fJFi8>^#xRQv7lEdAVkRW{_<=*Ch5&MBhPoijGI$+qkuXh_WKOx383 zG=1HpYSPQ4Z4Tuu;#Shh#ckbb<#5y8_#30aO1bPU@MLQkJGrQ$Y-L0jOf&M4^9jrm za?)1bLl3E;H!rtHntn|J;B=y80bXERxgY&)lm8HeZ>*gqr^c9&6?TRzDQX2Z*Ld2x z5{Hm~C%5(!**g$?;`w&%9y`%!jxHd-ks%!H9`Y_;Jo#k)ahSK7$RlGxnqk(L3R0Xc zWeU=B)@tn1Jp!+UY|$V(JxyR$N{P}<%rOAl;aY@)-H9+5hYpA77a<(s!Q*VD`c)ER6=|vO~RNT-P_ptNuw)4OaHif_?el-YA#i zeSb&SG@D5ZPpb&Hm59h?kw1vIo0j)(g)FtS!CZ!G2n#pFd$C`iCR|^uT;*xDb~H9- zU5@}p=z7haQvIMImX?cvCs=t4@QWe{`sph)Nbrjk0^Ji8MQ%}VG`OJZ7g1m4m;5-2 zX|Wh-KZg$rF9YO=NR*V11M)vuf_M2;uq%Y3{lk|j1R?_HOPG_DBAq#YSawAAPf8%c zU*7gi-qF7*=~ZZoqh+}AMJy~7G5clDbdFh6d;v-5A_slqh{h7-yqkW?Ei4u-^)trO zYw6(5io!6PDSv!=G8!uVYIc8jmQ2lc^ZeEpe>v30GftpN`&{*XdC4+9k1UOr2Q3`m zpOAWp8xA(%p3v#A_xn^RHb?b{N<$ttn3Nx!Zw2rgR15Hp>m>SQBcH(~$^(4lvRE@- zN$wSsINS`$J?)TgT>_W3bbKjs&5SuYV}SBQ<j`t-0rbOMF>r=r|*&A;`v+H8GzZV`& zu-2s3h1JhOP?K~^~z zhwY~3RSg~Vh%~@w+tH~`&7KI^A%^afMDt{zj9+n+dq_$Sxna_>yI)lWzuzN9oC(Px zQLToJV>{5n{w$re^~K-=okTDJVgheLQ3T~C+{6QzK%o;3>#yQZBQL@OB@^9RG7hr@ zI0L{2X-6U&abx5t^}uht~O7Il4H^v^S8C{Lt1;R6b0D5NJVP~SCPOlMHb z)x#EF7Av0wOp_h3<)#lZI}#JjzDMV?{lhY>%Zcr~#v5if>3SSt=k!g7B_W=-5RVu# ze4R8RF5+)i{!YV6ZnA?q9}WFemBR8Q{q*MX8)^6>dGN1q)LY{QH5w)L|ErMm0BR~* z<2WmV6e$X*bfg!7B#=T?dWX;v=^+u28WKQ|jvydaDWWt18&yO)ND-vC^iE(QfE4MA z6p{5^Sak>X&3kvwOy>OOJLfwm=Vs7?T_QOn;e_b2y>hf39mU#u+lJp8IOjh?6{-g7z!RH6D@7w;0jg<@St(s`T zH(CUmv4}}#?IIf~8SXTZXYN*?4`rv1G0VQASme{FNV6VMAqLNHZqtmV>{fhdaF83o z_NHZ;iZaYDqIPr9IkA*p)90hv>-TJPHu4RmU;NkO%eAl|nz@`?6=T;w#3Z|eUym{G zu9SA?F6a9{!$peAiaZgiTRJ=P>U`-3XKhu6(9UYZ#;aTFq3K4(`rKvg1-(jDQM4;l znM%TAD}z~u*$1grY&SGwea$k(iqtYumzMff<+S1PbXgs0*NDpJGB!KQk@erQLXwp; zzWXmaW{ag!lX)h3?t3*~lb@V0>)hKvtv22jOt7~$qrj~#XCOLm98{@3H|z)$8Yk*_ zLv!c($5HS7S4k%eK&= z{{E~4V`%n9sNg!;@|wav@aF{b_Mw&}NZ&*dx=o-rQKtXOrGz&upKWZ*rKTI{%Wl&1 zXa5SGJwFiFsKsN_dpU8dLy2FQ1TDhMm|-6e&6{(IFU_9H1FsU&t_P|#&V8<|%|Vyy zmZ-`QV;{~=&nTjX)px^l#6M8e@ZZV>3`2TR6B3R<42nBpqE;a3@hH8UldJk!wEy6C zmc-<_a>6e7wLkLyRt0%T6`p1~+iRA<)?LdSNQpq|8C!LfN20OnhRiz@d!H2;`Cf>c zzM7^-o<<8nku@~asjj&Kr(N$?*RGWdGiQ_D2-|!{K{6@h8eOjSsHqY@#){J7E8VgVSMSiT(1$il(fv4x{0x78DN(?d=&ylPXn)dJ?^zW~mv$2$lFJ z3pq3$z|+LtI#193i{FD3A>n4se|8UYlD9^JwPWH6M3PAVx^$DS&{Ck4+Fux^Ml5apwn@xpDxJ{j9xvu5;q@>)!C zMs7?psW#N`0IS;ZgVe)&F`&K(VZ?f`rpM0|YhHr-VulPlv zUQgGptn7!+Y)t!u?l%mHn z5L3MW*S%z&+iMSz%Z;~6o{+^qmnXY{D-kyUhqgZBE>4U=Ccr}jkg8GIEE)@M5F6Ys zsE0e^kf5crJ-nd=x&P64=2ye9V)SaCN%Rlis(XN}yD*!nh!CXWMi9)V?EABiCZqQ+ zqaMtpC-&rI8MG5klOCsV3UcPj^*Shy5qS3cEENJ@OWDwA!qf_~buXDh%iOeKXUif* zc^G&^i`4t9G7~H^9j!R?p=ve#3CZ#7ecY37)z_Pa-uJIar-Uy7?fo{2MVjB5S@GRRZr58CKU$PBM6nqP-^zD zI-gS-ahdw!i?dQyeY9Fv5%OT$%{(Rl#0i%^HbzPBNht$g!6!K5xEEV*F_UU>*BXt) zoe$0{rbH8QG{jRcE6DpTJBmwDH^M+676@deRqR;OJ)O78EFl>Wr`juxtG_}dL3ZF5~P1;YYkQX-gK(oyy(Xb?hwDF-j zXAJQc3kQVv`qLeYQqyUcI~HI8wkC@QZK(XAOkX7>~kkURa}9m9FrN;EHde*csjs*kwC^nZe82O`zCNHlAbSMoBTB>C-}y zLMn41qo${*7S>?eHp;o^g>Ye?NTT05y@h=%4K9aF)Ocxu-XcqsY?Zul^@!2L?#426+RsvBRYNDn>WRS_syAUg}aoTQyUVCEqea6HMe`o9m;%w zW;WqSF`C-eSzl}G>%(S+^tQaoryFY5-nZkJ>A~tDDx&6%Elc8-Q??!BY%*1Ey9fE( zuSn!Ek%?&QLn9u(Qqa2OqNt_G_wm_KH0rCI@*dCJ=2qB5%+8!TpV8jnZ|yJi{Ftq! zRi~TlF zb43z1P~qtL9UYl{V9GHbxYbiHm$Sd|&U_@0^!>ewJO72`_(^TRC857a4#1H9+mIEs zM?0f&0FRGxcJoF7%mPm6C+z|LNl^&8*rN0dxlGVlPe(Ubz*iOnONzm`ECdztj?Mtd zk5H3>S_pCJTtLO^Q6a6*KQjTo?vHFnZZ0mjxs+_N?tt%%#{S9$7|uT;^jusW z0kR*10qknH7#J)j`A^UxPjKiW3o1)PKwv2_;BbSXk|2nLIS3>Gv|o`wXFKxO9dW=^ zM;kT(>~B2I!41oGv?8DjRO;H}QNT<6jy4MjO2E7P0M3K!XhA>wz^;{nVp@P`02Tm< zI&SudeZcoV>i@%?_+Zf(fH8nbP=S73Tren90?LKq`dfp5Bqf1ihZmRY4-Etb0jB+N z4X88*0hqw?I1uPC<#7!Pf}I=-f`X-hQ$H35218-MfgaO92$1B-v0yM<0=PMj$3Z3j zy%xlwU4J4D0U!q_G$8LGi*jr}U~fPz^$86k2|BqxFa!oYnd9)_PmYCvAcyC7EC--L zPkw(8kObu4aS||qM>#Q8@{mCJso}7;0M>v#qKOP0eSvca;0FUYH-K9DQ7NwBig5!F zrlXrfLm7aWWT8+LS^@)-0--=?h!nyWf`Zzkz!;FN1j=3#1xCqJ{r`}^nGsJM@UuF) Ru^=EQ3_``nr=q7y^&fYb^W^{l diff --git a/docs/developer-guide.rst b/docs/developer-guide.rst deleted file mode 100644 index 9c0132c..0000000 --- a/docs/developer-guide.rst +++ /dev/null @@ -1,148 +0,0 @@ -.. This work is licensed under a Creative Commons Attribution 4.0 International License. -.. http://creativecommons.org/licenses/by/4.0 - -Developer Guide -=============== - -.. contents:: - :depth: 3 - :local: - -Tech Stack ----------- - -The A1 Mediator is implemented in Python, currently version 3.8, and -depends on these third-party packages and technologies: - -- OpenAPI3 -- Connexion -- Flask with Gevent serving -- Swagger -- Prometheus - - -Version bumping A1 ------------------- - -This project follows semver. When the version string changes, these -files must be updated: - -#. ``setup.py`` -#. ``container-tag.yaml`` -#. ``integration_tests/a1mediator/Chart.yaml`` -#. ``docs/release-notes.rst`` -#. ``a1/openapi.yaml`` But note this is an API version, not a software version; there's no need to bump on non-API changes. -#. And over in the ric-plt/ric-dep repo that contains the A1 Mediator helm chart, files ``values.yaml`` and ``Chart.yaml``. - -It's convenient to use the Python utility `bumpversion` to maintain -the first three items. After setup (``pip install bumpversion``) you -can change the patch version like this:: - - bumpversion --verbose patch - -Or change the minor version like this:: - - bumpversion --verbose minor - -After the `bumpversion` utility has modified the files, update the -release notes then commit. - - -Version bumping RMR -------------------- - -A1 (Dockerfile), Dockerfile-Unit-Test, and all three integration test -receivers use an Alpine base image and install RMR from a base builder -image. Must update and rebuild all 5 containers in the A1 repo (or -just A1 itself for production usage). - -In addition these items in this repo must be kept in sync: - -#. ``rmr-version.yaml`` controls what rmr gets installed for unit - testing in Jenkins -#. ``integration_tests/install_rmr.sh`` is a useful script for a - variety of local testing. - - -Version bumping Python ----------------------- - -If you want to update the version of python; for example this was -recently done to move from 3.7 to 3.8, update these files: - -#. ``Dockerfile`` -#. ``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 ------------- - -Running the unit tests requires the python packages ``tox`` and ``pytest``. - -The RMR library is also required during unit tests. If running -directly from tox (outside a Docker container), install RMR using the -script in the integration_tests directory: ``install_rmr.sh``. - -Upon completion, view the test coverage like this: - -:: - - tox - open htmlcov/index.html - -Alternatively, you can run the unit tests in Docker (this is somewhat -less nice because you don't get the pretty HTML) - -:: - - docker build --no-cache -f Dockerfile-Unit-Test . - - -Integration testing -------------------- - -This tests A1’s external API with three test receivers. This requires -docker, kubernetes and helm. - -Build all the images: - -:: - - docker build -t a1:latest . - cd integration_tests/testxappcode - docker build -t delayreceiver:latest -f Dockerfile-delay-receiver . - docker build -t queryreceiver:latest -f Dockerfile-query-receiver . - docker build -t testreceiver:latest -f Dockerfile-test-receiver . - - -Then, run all the tests from the root (this requires the python packages ``tox``, ``pytest``, and ``tavern``). - -:: - - tox -c tox-integration.ini - -This script: - -#. Deploys 3 helm charts (5 containers) into a local kubernetes installation -#. Port forwards a pod ClusterIP to localhost -#. Uses “tavern” to run some tests against the server -#. Barrages the server with Apache bench -#. Tears everything down diff --git a/docs/favicon.ico b/docs/favicon.ico deleted file mode 100644 index 00b0fd0ef0b4e78fbb8cdb413ce84561dfeb404f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15086 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

4K$ruCE1 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 XY+}&}KC=mD;X?Iq*l*cA?B4$ePY*3q diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index d679286..0000000 --- a/docs/index.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. This work is licensed under a Creative Commons Attribution 4.0 International License. -.. SPDX-License-Identifier: CC-BY-4.0 - -A1 Mediator -=========== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - - overview.rst - installation-guide.rst - user-guide-api.rst - developer-guide.rst - release-notes.rst - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/docs/installation-guide.rst b/docs/installation-guide.rst deleted file mode 100644 index e04fdf2..0000000 --- a/docs/installation-guide.rst +++ /dev/null @@ -1,75 +0,0 @@ -.. This work is licensed under a Creative Commons Attribution 4.0 International License. -.. http://creativecommons.org/licenses/by/4.0 -.. Copyright (C) 2019 AT&T Intellectual Property - -Installation Guide -================== - -.. contents:: - :depth: 3 - :local: - -Environment Variables ---------------------- - -You can set the following environment variables when launching a container to change the A1 behavior: - -1. ``A1_RMR_RETRY_TIMES``: the number of times failed rmr operations such as timeouts and send failures should be retried before A1 gives up and returns a 503. The default is ``4``. - -2. ``INSTANCE_DELETE_NO_RESP_TTL``: Please refer to the delete flowchart in docs/; this is ``T1`` there. The default is 5 (seconds). Basically, the number of seconds that a1 waits to remove an instance from the database after a delete is called in the case that no downstream apps responded. - -3. ``INSTANCE_DELETE_RESP_TTL``: Please refer to the delete flowchart in docs/; this is ``T2`` there. The default is 5 (seconds). Basically, the number of seconds that a1 waits to remove an instance from the database after a delete is called in the case that downstream apps responded. - -4. ``USE_FAKE_SDL``: This allows testing of the A1 feature without a DBaaS SDL container. The default is False. - -5. ``prometheus_multiproc_dir``: The directory where Prometheus gathers metrics. The default is /tmp. - - -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 -~~~~~~~~~~~~~~~ -:: - - docker build --no-cache -t a1:latest . - -.. _running-1: - -Start the container -~~~~~~~~~~~~~~~~~~~ - -The A1 container depends on a companion DBaaS (SDL) container, but if that is not convenient set -an environment variable as shown below to mock that service. Also a sample RMR routing table is -supplied in file `local.rt` for mounting as a volume. The following command uses both: - -:: - - docker run -e USE_FAKE_SDL=True -p 10000:10000 -v /path/to/local.rt:/opt/route/local.rt a1:latest - -View container API -~~~~~~~~~~~~~~~~~~ - -A web user interface generated from the OpenAPI specification can be accessed at this URL: - -:: - - http://docker-host-name-or-ip:10000/ui - -Check container health -~~~~~~~~~~~~~~~~~~~~~~ - -The following command requests the container health. Expect an internal server error if the -Storage Data Layer (SDL) service is not available or has not been mocked as shown above. - -:: - - curl docker-host-name-or-ip:10000/a1-p/healthcheck diff --git a/docs/overview.rst b/docs/overview.rst deleted file mode 100644 index 450e92f..0000000 --- a/docs/overview.rst +++ /dev/null @@ -1,133 +0,0 @@ -.. This work is licensed under a Creative Commons Attribution 4.0 International License. -.. SPDX-License-Identifier: CC-BY-4.0 - -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 -xApps via RMR messages (the "southbound" interface). - -Code ----- - -Code is managed in this Gerrit repository: - -https://gerrit.o-ran-sc.org/r/admin/repos/ric-plt/a1 - - -Policy Overview ----------------- - -There are two "object types" associated with policy: policy types and -policy instances. - -Policy Types -~~~~~~~~~~~~ - -Policy types define the name, description, and most importantly the -schema of all instances of that type. Think of policy types as -defining a JSON schema for the messages sent from A1 to xapps. Xapps -do not receive policy types from A1; types are used only by A1 to -validate instance creation requests. However, xapps must register to -receive instances of type ids in their xapp descriptor. Xapp -developers can also create new policy types, though the exact process -of where these are stored is still TBD. For practical purposes, when -the RIC is running, A1s API needs to be invoked to load the policy -types before instances can be created. Xapps can "sign up" for -multiple policy types using their xapp descriptor. - -Policy Instances -~~~~~~~~~~~~~~~~ - -Policy instances are concrete instantiations of a policy type. They -give concrete values of a policy. There may be many instances of a -single type. Whenever a policy instance is created in A1, messages are -sent over RMR to all xapps registered for that policy type; see below. -Xapps are expected to handle multiple simultaneous instances of each -type that they are registered for. - - -Known differences from A1 1.0.0 spec ------------------------------------- - -This is a list of some of the known differences between the API here -and the a1 spec dated 2019.09.30. In some cases, the spec is -deficient and RIC is "ahead", in other cases this does not yet conform -to recent spec changes. - -#. [RIC is ahead] There is no notion of policy types in the spec, - however this aspect is quite critical for the intended use of the - RIC, where many Xapps may implement the same policy, and new Xapps - may be created often that define new types. Moreover, policy types - define the schema for policy instances, and without types, A1 - cannot validate whether instances are valid, which the RIC A1m - does. The RIC A1 Mediator view of things is that, there are a set - of schemas, called policy types, and one or more instances of each - schema can be created. Instances are validated against types. The - spec currently provides no mechanism for the implementation of A1 - to know whether policy [instances] are correct since there is no - schema for them. This difference has the rather large consequence - that none of the RIC A1m URLs match the spec. -#. [RIC is ahead] There is a rich status URL in the RIC A1m for policy - instances, but this is not in the spec. -#. [RIC is ahead] There is a state machine for when instances are - actually deleted from the RIC (at which point all URLs referencing - it are a 404); this is a configurable option when deploying the RIC - A1m. -#. [CR coming to spec] The spec contains a PATCH for partially - updating a policy instance, and creating/deleting multiple - instances, however the team agreed to remove this from a later - version of the Spec. The RIC A1m does not have this operation. -#. [Spec is ahead] The RIC A1 PUT bodies for policy instances do not - exactly conform to the "scope" and "statements" block that the spec - defines. They are very close otherwise, however. (I would argue - some of the spec is redundant; for example "policy [instance] id" - is a key inside the PUT body to create an instance, but it is - already in the URL.) -#. [Spec is ahead] The RIC A1m does not yet notify external clients - when instance statuses change. -#. [Spec is ahead] The spec defines that a query of all policy - instances should return the full bodies, however right now the RIC - A1m returns a list of IDs (assuming subsequent queries can fetch - the bodies). -#. [?] The spec document details some very specific "types", but the - RIC A1m allows these to be loaded in (see #1). For example, spec - section 4.2.6.2. We believe this should be removed from the spec - and rather defined as a type. Xapps can be created that define new - types, so the spec will quickly become "stale" if "types" are - defined in the spec. - - -Resiliency ----------- - -A1 is resilient to the majority of failures, but not all currently -(though a solution is known). - -A1 uses the RIC SDL library to persist all policy state information: -this includes the policy types, policy instances, and policy statuses. -If state is built up in A1, and A1 fails (where Kubernetes will then -restart it), none of this state is lost. - -The tiny bit of state that *is currently* in A1 (volatile) is its -"next second" job queue. Specifically, when policy instances are -created or deleted, A1 creates jobs in a job queue (in memory). An -rmr thread polls that thread every second, dequeues the jobs, and -performs them. - -If A1 were killed at *exactly* the right time, you could have jobs -lost, meaning the PUT or DELETE of an instance wouldn't actually take. -This isn't drastic, as the operations are idempotent and could always -be re-performed. - -In order for A1 to be considered completely resilient, this job queue -would need to be moved to SDL. SDL uses Redis as a backend, and Redis -natively supports queues via LIST, LPUSH, RPOP. I've asked the SDL -team to consider an extension to SDL to support these Redis -operations. diff --git a/docs/policy_instance_state_diagram.pdf b/docs/policy_instance_state_diagram.pdf deleted file mode 100644 index d25d3398c5ae72f8b94f117123a850f0b5f8e8fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17090 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 -and this project adheres to `Semantic Versioning `__. - -[2.5.0] - 2021-06-22 --------------------- - -* Enhancement to add A1-EI support. -* Upgrade RMR to version 4.5.2. -* Base docker image changed to ubuntu:18.04. -* Upgrade ricxappframe to version 2.0.0. -* Upgrade MDC logging. - -[2.4.0] - 2020-12-08 --------------------- - -* Reference RMR version 4.4.6 via the builder image. - -[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 --------------------- - -* Fix _send_msg method to free allocated RMR message buffers -* Adjust send-message methods to retry only on RMR_ERR_RETRY -* Extend send-message methods to log message state after send -* Use constants from ricxappframe.rmr instead of hardcoded strings -* Upgrade RMR to version 4.0.5 -* Upgrade tavern to version 1.2.2 -* Extend user guide with southbound API schemas - - -[2.1.8] - 2020-04-30 --------------------- - -* Revise Dockerfile to set user as owner of .local dir with a1 package -* Rename console shell start script to run-a1 from run.py -* Extend start script to report webserver listening port -* Add tiny RMR routing table for use in demo and test -* Extend documentation for running a container locally -* Add documentation of start/init parameters to _RmrLoop class -* Add new environment variable USE_FAKE_SDL (`RIC-351 `_) -* Respond with error if policy type ID differs from ID in object on create -* Upgrade integration tests to use Tavern version 1.0.0 - - -[2.1.7] - 2020-04-28 --------------------- - -* Upgrade to rmr 4.0.2 -* Upgrade integration tests to xapp-frame-go version 0.4.8 which drops NNG -* Extend exception handler to return error details in HTTP response -* Ensure that policy type ID on path matches ID in object -* Add OpenAPI spec to RST documentation - - -[2.1.6] - 4/7/2020 -------------------- - -* Switch to rmr 3.6.3 -* Switch to using rmr in the ricxappframe - - -[2.1.5] - 3/19/2020 -------------------- - -* Switch to python3.8 -* Switch to SI95 from NNG (rmr v3 vs rmr v1) -* The switch to SI95 led to a rabbit hole in which we eventually discovered that rmr_send may sometimes block for an arbitrary period of time. Because of this issue, a1's sends are now threaded. Please see the longer comment about this in a1rmr. -* Bump version of py xapp frame (SDL used only) in A1 -* Bump version of go xapp frame (0.0.24 -> 0.4.2) in integration tests -* Add some additional logging in A1 - - -[2.1.4] - 3/6/2020 -------------------- - -* SDL Wrapper was moved into the python xapp framework; use it from there instead. - - -[2.1.3] - 2/13/2020 -------------------- - -* This is a pretty big amount of work/changes, however no APIs were changed hence the semver patch -* Switches A1's three test receivers (integration tests) over to golang; this was mostly done to learn the go xapp framework and they are identical in functionality. -* Upgrades the version of rmr in A1 and all integration receivers to 1.13.* -* Uses a much fancier Docker build to reduce the size of a1's image. The python:3.7-alpine image itself is 98MB and A1 is now only ~116MB, so we're done optimizing A1's container size. - -[2.1.2] - 1/22/2020 -------------------- - -* Upgrades from sdl 2.0.2 to 2.0.3 -* Integrates an sdl healthcheck into a1's healthcheck - - -[2.1.1] - 1/14/2020 -------------------- - -* Upgrades from sdl 1.0.0 to 2.0.2 -* Delete a1test_helpers because SDL 2.0.2 provides the mockup we need -* Remove general catch all from A1 - - -[2.1.0] - 1/8/2020 ------------------- - -* Represents a resillent version of 2.0.0 that uses Redis for persistence -* Now relies on SDL and dbaas; SDL is the python interface library to dbaas -* Adds a 503 http code to nearly all http methods, as A1 now depends on an upstream system -* Integration tests have a copy of a dbaas helm chart, however the goal is to simplify that deployment per https://jira.o-ran-sc.org/browse/RIC-45 -* Unit tests have a mockup of SDL, however again the goal is to simplify as SDL grows per https://jira.o-ran-sc.org/browse/RIC-44 - - -[2.0.0] - 12/9/2019 -------------------- - -* Implements new logic around when instances are deleted. See flowcharts in docs/. Basically timeouts now trigger to actually delete instances from a1s database, and these timeouts are configurable. -* Eliminates the barrier to deleting an instance when no xapp evdr replied (via timeouts) -* Add two new ENV variables that control timeouts -* Make unit tests more modular so new workflows can be tested easily -* Fixes the API for ../status to return a richer structure. This is an (albeit tiny) API change. -* Clean up unused items in the integration tests helm chart -* Removed "RMR_RCV_RETRY_INTERVAL" leftovers since this isn't used anymore -* Uses the standard RIC logging library -* Switch the backend routing scheme to using subscription id with constant message types, per request. -* Given the above, policy type ids can be any valid 32bit greater than 0 -* Decouple the API between northbound and A1 from A1 with xapps. This is now two seperate OpenAPI files -* Update example for AC Xapp -* Updgrade rmr and rmr-python to utilize new features; lots of cleanups because of that -* Implements a POLICY QUERY feature where A1 listens for queries for a policy type. A1 then responds via multiple RTS messages every policy instance of that policy type (and expects an ACK back from xapps as usual). This feature can be used for xapp recovery etc. - - -[1.0.4] - 10/24/2019 --------------------- - -* Only external change here is to healthcheck the rmr thread as part of a1s healthcheck. k8s will now respin a1 if that is failing. -* Refactors (simplifies) how we wait for rmr initialization; it is now called as part of __init__ -* Refactors (simplifies) how the thread is actually launched; it is now internal to the object and also a part of __init__ -* Cleans up unit testing; a1rmr now exposes a replace_rcv_func; useful for unit testing, harmless if not called otherwise -* Upgrades to rmr-python 1.0.0 for simpler message allocation - - -[1.0.3] - 10/22/2019 --------------------- - -* Move database cleanup (e.g., deleting instances based on statuses) into the polling loop -* Rework how unit testing works with the polling loop; prior, exceptions were being thrown silently from the thread but not printed. The polling thread has now been paramaterized with override functions for the purposes of testing -* Make type cleanup more efficient since we know exactly what instances were touched, and it's inefficient to iterate over all instances if they were not -* Bump rmr-python version, and bump rmr version -* Still an item left to do in this work; refactor the thread slightly to tie in a healthcheck with a1s healthcheck. We need k8s to restart a1 if that thread dies too. - - -[1.0.2] - 10/17/2019 --------------------- - -* a1 now has a seperate, continuous polling thread, which will enable operations like database cleanup - (based on ACKs) and external notifications in real time, rather than when the API is invoked -* all rmr send and receive operations are now in this thread -* introduces a thread safe job queue between the two threads -* Not done yet: database cleanups in the thread -* Bump rmr python version -* Clean up some logging - - -[1.0.1] - 10/15/2019 --------------------- - -* Moves the "database" access calls to mimick the SDL API, in preparation for moving to SDL -* Does not yet actually use SDL or Redis, but the transition to those will be much shorter after this change. - - -[1.0.0] - 10/7/2019 -------------------- - -* Represents v1.0.0 of the A1 API for O-RAN-SC Release A -* Finished here: - - Implement type DELETE - - Clean up where policy instance cleanups happen - - -[0.14.1] - 10/2/2019 --------------------- - -:: - - * Upgrade rmr to 1.9.0 - * Upgrade rmr-python to 0.13.2 - * Use the new helpers module in rmr-python for the rec all functionality - * Switch rmr mode to a multithreaded mode that continuously reads from rmr and populates an internal queue of messages with a deterministic queue size (2048) which is better behavior for A1 - * Fix a memory leak (python obj is garbage collected but not the underlying C memory allocation) - - - -[0.14.0] - 10/1/2019 --------------------- - -:: - - * Implement instance delete - * Moves away from the status vector and now aggregates statuses - * Pop through a1s mailbox "3x as often"; on all 3 kinds of instance GET since all such calls want the latest information - * Misc cleanups in controller (closures ftw) - * Add rmr-version.yaml for CICD jobs - -[0.13.0] - 9/25/2019 --------------------- - -:: - - * Implement GET all policy type ids - * Implement GET all policy instance ids for a policy type - * fix a tiny bug in integration test receiver - - -[0.12.1] - 9/20/2019 --------------------- - -:: - - * switch to rmr 1.8.1 to pick up a non blocking variant of rmr that deals with bad routing tables (no hanging connections / blocking calls) - * improve test receiver to behave with this setup - * add integration test for this case - * this also switches past 1.5.x, which included another change that altered the behavior of rts; deal with this with a change to a1s helmchart (env: `RMR_SRC_ID`) that causes the sourceid to be set to a1s service name, which was not needed prior - * improve integration tests overall - - -[0.12.0] - 9/19/2019 --------------------- - -:: - - * Implement type PUT - * Implement type GET - * Remove RIC manifest - * Read type GET to get schema for instance PUT - * Remove Utils (no longer needed) - * lots more tests (unit and integration) - -[0.11.0] - 9/17/2019 --------------------- - -:: - - * This is on the road to release 1.0.0. It is not meant to be tested (E2E) as it's own release - * Implement the Release A spec in the openapi.yaml - * Rework A1 to follow that spec - * Remove rmr_mapping now that we use policyid as the mtype to send and a well known mtype for the ACKs - * Add the delay receiver test to the tavern integration tests - * Remove unneeded ENV variables from helm charts - * Switch away from builder images to avoid quicksand; upgrade rmr at our own pace - - -[0.10.3] - 8/20/2019 --------------------- - -:: - - * Update to later rmr-python - * Add docs about upgrading rmr - * remove bombarder since tavern runs apache bench - - -[0.10.2] - 8/14/2019 --------------------- - -:: - - * Update to later rmr-python - -[0.10.1] - 8/9/2019 -------------------- - -:: - - * Greatly reduce the size of A1 docker from 1.25GB to ~278MB. - * Add a seperate dockerfile for unit testing - - -[0.10.0] - 7/30/2019 --------------------- - -:: - - * Rename all /ric/ URLs to be consistent with requirements of /a1-p/ - - -[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 -------------------- - -:: - - * 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 -------------------- - -:: - - * Use base Docker with NNG version 1.1.1 - - - -[0.8.2] - 6/5/2019 ------------------- - -:: - - * Upgrade RMR due to a bug that was preventing rmr from init in kubernetes - - - -[0.8.1] - 5/31/2019 -------------------- - -:: - - * Run unit tests as part of docker build - - - -[0.8.0] - 5/28/2019 -------------------- - -:: - - * Convert docs to appropriate format - * Move rmr string to int mapping to a file - - - -[0.7.2] - 5/24/2019 -------------------- - -:: - - * Use tavern to test the actual running docker container - * Restructures the integration tests to run as a single tox command - * Re-ogranizes the README and splits out the Developers guide, which is not needed by users. - - -[0.7.1] - 5/23/2019 -------------------- - -:: - - * Adds a defense mechanism against A1 getting queue-overflowed with messages A1 doesnt care about; A1 now ignores all incoming messages it's not waiting for, so it's queue size should now always be "tiny", i.e., never exceeding the number of valid requests it's waiting for ACKs back for - * Adds a test "bombarding" script that tests this - - -[0.7.0] - 5/22/19 ------------------ - -:: - - * Main purpose of this change is to fix a potential race condition where A1 sends out M1 expecting ACK1, and while waiting for ACK1, sends out M2 expecting ACK2, but gets back ACK2, ACK1. Prior to this change, A1 may have eaten ACK2 and never fufilled the ACK1 request. - * Fix a bug in the unit tests (found using a fresh container with no RIC manifest!) - * Fix a (critical) bug in a1rmr due to a rename in the last iteration (RMR_ERR_RMR_RCV_RETRY_INTERVAL) - * Make unit tests faster by setting envs in tox - * Move to the now publically available rmr-python - * Return a 400 if am xapp does not expect a body, but the PUT provides one - * Adds a new test policy to the example RIC manifest and a new delayed receiver to test the aformentiond race condition - - -[0.6.0] -------- - -:: - - * Upgrade to rmr 0.10.0 - * Fix bad api spec RE GET - * Fix a (big) bug where transactionid wasn't being checked, which wouldn't have worked on sending two policies to the same downstream policy handler - - -[0.5.1] - 5/13/2019 -------------------- - -:: - - * Rip some testing structures out of here that should have been in rmr (those are now in rmr 0.9.0, upgrade to that) - * Run Python BLACK for formatting - - -[0.5.0] - 5/10/2019 -------------------- - -:: - - * Fix a blocking execution bug by moving from rmr's timeout to a non blocking call + retry loop + asyncronous sleep - * Changes the ENV RMR_RCV_TIMEOUT to RMR_RCV_RETRY_INTERVAL - - -[0.4.0] - 5/9.2019 ------------------- - -:: - - * Update to rmr 0.8.3 - * Change 503 to 504 for the case where downstream does not reply, per recommendation - * Add a 502 with different reasons if the xapp replies but with a bad/malformed/missing status - * Make testing much more modular, in anticipating of moving some unit test functionality into rmr itself - - -[0.3.4] - 5/8/2019 ------------------- - -:: - - * Crash immediately if manifest isn't mounted - * Add unit tests for utils - * Add missing lic - - -[0.3.3] -------- - -:: - - * Upgrade A1 to rmr 0.8.0 - * Go from deb RMR installation to git - * Remove obnoxious receiver logging - - -[0.3.2] -------- - -:: - - * Upgrade A1 to rmr 0.6.0 - - -[0.3.1] -------- - -:: - - * Add license headers - - -[0.3.0] -------- - -:: - - * Introduce RIC Manifest - * Move some testing functionality into a helper module - * Read the policyname to rmr type mapping from manifest - * Do PUT payload validation based on the manifest - - -[0.2.0] -------- - -:: - - * Bump rmr python dep version - * Include a Dockerized test receiver - * Stencil out the mising GET - * Update the OpenAPI - * Include a test docker compose file - - -[0.1.0] -------- - -:: - - * Initial Implementation diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt deleted file mode 100644 index 09a0c1c..0000000 --- a/docs/requirements-docs.txt +++ /dev/null @@ -1,5 +0,0 @@ -sphinx -sphinx-rtd-theme -sphinxcontrib-httpdomain -recommonmark -lfdocs-conf diff --git a/docs/user-guide-api.rst b/docs/user-guide-api.rst deleted file mode 100644 index 5136d06..0000000 --- a/docs/user-guide-api.rst +++ /dev/null @@ -1,115 +0,0 @@ -.. This work is licensed under a Creative Commons Attribution 4.0 International License. -.. SPDX-License-Identifier: CC-BY-4.0 - -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. - -Example Messages ----------------- - -Send the following JSON to create policy type 20008, which supports instances with -a single integer value: - -.. code-block:: yaml - - { - "name": "tsapolicy", - "description": "tsa parameters", - "policy_type_id": 20008, - "create_schema": { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "threshold": { - "type": "integer", - "default": 0 - } - }, - "additionalProperties": false - } - } - - -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 - - { - "threshold" : 5 - } - - -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 -------------------------- - -The schema for messages sent by A1 to Xapps is labeled ``downstream_message_schema`` -in the Southbound API Specification section below. A1 sends policy instance requests -using message type 20010. - -The schemas for messages sent by Xapps to A1 appear in the Southbound API -Specification section below. Xapps must use a message type and content appropriate -for the scenario: - -#. When an Xapp receives a CREATE message for a policy instance, the Xapp - must respond by sending a message of type 20011 to A1. The content is - defined by schema ``downstream_notification_schema``. The most convenient - way is to use RMR's return-to-sender (RTS) feature after setting the - message type appropriately. -#. Since policy instances can "deprecate" other instances, there are - times when Xapps need to asynchronously tell A1 that a policy is no - longer active. Use the same message type and schema as above. -#. Xapps can request A1 to re-send all instances of policy type T using a - query, message type 20012. The schema for that message is defined by - ``policy_query_schema`` (just a body with ``{policy_type_id: ... }``). - When A1 receives this, A1 will send the Xapp a CREATE message N times, - where N is the number of policy instances for type T. The Xapp should reply - normally to each of those as the first item above. That is, after the Xapp - performs the query, the N CREATE messages sent and the N replies - are "as normal". The query just kicks off this process rather than - an external caller to A1. - - -Northbound API Specification ----------------------------- - -This section shows the Open API specification for the A1 Mediator's -northbound interface, which accepts policy type and policy instance requests. -Alternately, if you have checked out the code and are running the server, -you can see a formatted version at this URL: ``http://localhost:10000/ui/``. - - -.. literalinclude:: ../a1/openapi.yaml - :language: yaml - :linenos: - - -Southbound API Specification ----------------------------- - -This section shows Open API schemas for the A1 Mediator's southbound interface, -which communicates with Xapps via RMR. A1 sends policy instance requests using -message type 20010. Xapps may send requests to A1 using message types 20011 and -20012. - - -.. literalinclude:: a1_xapp_contract_openapi.yaml - :language: yaml - :linenos: diff --git a/a1-go/go.mod b/go.mod similarity index 100% rename from a1-go/go.mod rename to go.mod diff --git a/a1-go/go.sum b/go.sum similarity index 100% rename from a1-go/go.sum rename to go.sum diff --git a/integration_tests/a1mediator/.helmignore b/integration_tests/a1mediator/.helmignore deleted file mode 100644 index 50af031..0000000 --- a/integration_tests/a1mediator/.helmignore +++ /dev/null @@ -1,22 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/integration_tests/a1mediator/Chart.yaml b/integration_tests/a1mediator/Chart.yaml deleted file mode 100644 index 1a4eb5b..0000000 --- a/integration_tests/a1mediator/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: A1 Helm chart for Kubernetes -name: a1mediator -version: 2.2.0 diff --git a/integration_tests/a1mediator/templates/_helpers.tpl b/integration_tests/a1mediator/templates/_helpers.tpl deleted file mode 100644 index fa46bab..0000000 --- a/integration_tests/a1mediator/templates/_helpers.tpl +++ /dev/null @@ -1,45 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "a1mediator.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "a1mediator.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "a1mediator.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Common labels -*/}} -{{- define "a1mediator.labels" -}} -app.kubernetes.io/name: {{ include "a1mediator.name" . }} -helm.sh/chart: {{ include "a1mediator.chart" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end -}} diff --git a/integration_tests/a1mediator/templates/config.yaml b/integration_tests/a1mediator/templates/config.yaml deleted file mode 100644 index 811c118..0000000 --- a/integration_tests/a1mediator/templates/config.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: a1conf -data: - local.rt: | - newrt|start - mse|20010|6660666|testreceiverrmrservice:4560 - mse|20010|20001|delayreceiverrmrservice:4563 - # purposefully bad route to make sure rmr doesn't block on non listening receivers: - rte|20010|testreceiverrmrservice:4563 - newrt|end diff --git a/integration_tests/a1mediator/templates/deployment.yaml b/integration_tests/a1mediator/templates/deployment.yaml deleted file mode 100644 index 45b1a45..0000000 --- a/integration_tests/a1mediator/templates/deployment.yaml +++ /dev/null @@ -1,63 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "a1mediator.fullname" . }} - labels: -{{ include "a1mediator.labels" . | indent 4 }} -spec: - replicas: {{ .Values.replicaCount }} - selector: - matchLabels: - app.kubernetes.io/name: {{ include "a1mediator.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - template: - metadata: - labels: - app.kubernetes.io/name: {{ include "a1mediator.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - spec: - imagePullSecrets: - - name: "{{ .Values.lf_docker_reg_secret }}" - containers: - - name: {{ .Chart.Name }} - volumeMounts: - - name: a1conf - mountPath: /opt/route/local.rt - subPath: local.rt - env: - # this sets the source field in messages from a1 to point back to a1s service name, rather than it's random pod name - - name: RMR_SRC_ID - value: {{ .Values.rmrservice.name }} - - name: PYTHONUNBUFFERED - value: "1" - - name: A1_RMR_RETRY_TIMES - value: "{{ .Values.rmr_timeout_config.rcv_retry_times }}" - - name: INSTANCE_DELETE_NO_RESP_TTL - value: "5" - - name: INSTANCE_DELETE_RESP_TTL - value: "10" - - name: DBAAS_SERVICE_HOST - value: "dbaas" - - name: DBAAS_SERVICE_PORT - value: "6379" - - image: "a1:latest" - imagePullPolicy: {{ .Values.image.pullPolicy }} - ports: - - name: http - containerPort: {{ .Values.httpservice.port }} - protocol: TCP - livenessProbe: - httpGet: - path: /a1-p/healthcheck - port: http - readinessProbe: - httpGet: - path: /a1-p/healthcheck - port: http - resources: - {{- toYaml .Values.resources | nindent 12 }} - volumes: - - name: "a1conf" - configMap: - name: "a1conf" diff --git a/integration_tests/a1mediator/templates/helper.yaml b/integration_tests/a1mediator/templates/helper.yaml deleted file mode 100644 index c036840..0000000 --- a/integration_tests/a1mediator/templates/helper.yaml +++ /dev/null @@ -1,3 +0,0 @@ -{{- define "imagePullSecret" }} -{{- printf "{\"auths\": {\"%s\": {\"auth\": \"%s\"}}}" .Values.imageCredentials.registry (printf "%s:%s" .Values.imageCredentials.username .Values.imageCredentials.password | b64enc) | b64enc }} -{{- end }} diff --git a/integration_tests/a1mediator/templates/service.yaml b/integration_tests/a1mediator/templates/service.yaml deleted file mode 100644 index 3321aa6..0000000 --- a/integration_tests/a1mediator/templates/service.yaml +++ /dev/null @@ -1,56 +0,0 @@ -# This is the service for A1's external facing HTTP API -apiVersion: v1 -kind: Service -metadata: - name: {{ include "a1mediator.fullname" . }} - labels: -{{ include "a1mediator.labels" . | indent 4 }} - -spec: - type: {{ .Values.httpservice.type }} - ports: - - port: {{ .Values.httpservice.port }} - targetPort: http - protocol: TCP - name: http - selector: - app.kubernetes.io/name: {{ include "a1mediator.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - ---- -# This is the service for rmr between A1 and the xapps -apiVersion: v1 -kind: Service -metadata: - name: {{ .Values.rmrservice.name }} - labels: -{{ include "a1mediator.labels" . | indent 4 }} - -spec: - type: {{ .Values.rmrservice.type }} - ports: - - port: {{ .Values.rmrservice.port }} - targetPort: {{ .Values.rmrservice.port }} - protocol: TCP - selector: - app.kubernetes.io/name: {{ include "a1mediator.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - ---- -# This is the service for the "hidden" port 4561 that rmr listens on for route manager -apiVersion: v1 -kind: Service -metadata: - name: {{ .Values.rmrrtemgrservice.name }} - labels: -{{ include "a1mediator.labels" . | indent 4 }} - -spec: - type: {{ .Values.rmrrtemgrservice.type }} - ports: - - port: {{ .Values.rmrrtemgrservice.port }} - targetPort: {{ .Values.rmrrtemgrservice.port }} - protocol: TCP - selector: - app.kubernetes.io/name: {{ include "a1mediator.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} diff --git a/integration_tests/a1mediator/templates/tests/test-connection.yaml b/integration_tests/a1mediator/templates/tests/test-connection.yaml deleted file mode 100644 index ca68368..0000000 --- a/integration_tests/a1mediator/templates/tests/test-connection.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{ include "a1mediator.fullname" . }}-test-connection" - labels: -{{ include "a1mediator.labels" . | indent 4 }} - annotations: - "helm.sh/hook": test-success -spec: - containers: - - name: wget - image: busybox - command: ['wget'] - args: ['{{ include "a1mediator.fullname" . }}:{{ .Values.httpservice.port }}'] - restartPolicy: Never diff --git a/integration_tests/a1mediator/values.yaml b/integration_tests/a1mediator/values.yaml deleted file mode 100644 index e8bd0d2..0000000 --- a/integration_tests/a1mediator/values.yaml +++ /dev/null @@ -1,31 +0,0 @@ -replicaCount: 1 - -image: - repository: a1 - tag: latest - pullPolicy: Never - -# name of the secret that allows for privagte registry docker pulls. -# if the value is "lfhelper", there is a helper function included in this chart, and it uses imageCredentials . -# imageCredentials is referenced in secrets.yaml, and uses a helper function to formulate the docker reg username and password into a valid dockerconfig.json. -# This is the service for A1's external facing HTTP API -httpservice: - name: a1httpservice - port: 10000 # This is hardcoded in a1, probably dangerous to change - type: ClusterIP - -# This is the service for rmr between A1 and the xapps -rmrservice: - name: a1rmrservice - port: 4562 # This is hardcoded in a1, probably dangerous to change - type: ClusterIP - -# This is the service for the "hidden" port 4561 that rmr listens on for route manager -rmrrtemgrservice: - name: a1rmrrtemgrservice - port: 4561 # This is hardcoded in rmr, probably dangerous to change - type: ClusterIP - -# these are ENV variables that A1 takes; see docs -rmr_timeout_config: - rcv_retry_times: 20 diff --git a/integration_tests/dbaas-service/Chart.yaml b/integration_tests/dbaas-service/Chart.yaml deleted file mode 100644 index 8d183de..0000000 --- a/integration_tests/dbaas-service/Chart.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) 2019 AT&T Intellectual Property. -# Copyright (c) 2019 Nokia. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# -# This source code is part of the near-RT RIC (RAN Intelligent Controller) -# platform project (RICP). -# - -apiVersion: v1 -appVersion: "1.0" -description: DBaaS realized with standalone, non-persistent, non-redundant Redis -name: dbaas -version: 0.1.0 diff --git a/integration_tests/dbaas-service/README b/integration_tests/dbaas-service/README deleted file mode 100644 index c15a3e0..0000000 --- a/integration_tests/dbaas-service/README +++ /dev/null @@ -1 +0,0 @@ -This was stolen from https://gerrit.o-ran-sc.org/r/gitweb?p=ric-plt/dbaas.git diff --git a/integration_tests/dbaas-service/templates/deployment.yaml b/integration_tests/dbaas-service/templates/deployment.yaml deleted file mode 100644 index 8446b8e..0000000 --- a/integration_tests/dbaas-service/templates/deployment.yaml +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2019 AT&T Intellectual Property. -# Copyright (c) 2019 Nokia. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# -# This source code is part of the near-RT RIC (RAN Intelligent Controller) -# platform project (RICP). -# - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ .Values.backend.name }} -spec: - replicas: {{ .Values.backend.replicas }} - selector: - matchLabels: - app: {{ .Values.backend.name }} - template: - metadata: - labels: - app: {{ .Values.backend.name }} - spec: - terminationGracePeriodSeconds: {{ .Values.backend.terminationGracePeriodSeconds }} - containers: - - image: {{ .Values.backend.image.name }}:{{ .Values.backend.image.tag }} - imagePullPolicy: {{ .Values.backend.image.imagePullPolicy }} - ports: - - containerPort: {{ .Values.backend.targetPort }} - name: {{ .Values.backend.name }} - restartPolicy: Always diff --git a/integration_tests/dbaas-service/templates/service.yaml b/integration_tests/dbaas-service/templates/service.yaml deleted file mode 100644 index 472aeac..0000000 --- a/integration_tests/dbaas-service/templates/service.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 2019 AT&T Intellectual Property. -# Copyright (c) 2019 Nokia. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# -# This source code is part of the near-RT RIC (RAN Intelligent Controller) -# platform project (RICP). -# - -apiVersion: v1 -kind: Service -metadata: - name: {{ .Chart.Name }} -spec: - selector: - app: {{ .Values.backend.name }} - ports: - - port: {{ .Values.backend.port }} - targetPort: {{ .Values.backend.targetPort }} diff --git a/integration_tests/dbaas-service/values.yaml b/integration_tests/dbaas-service/values.yaml deleted file mode 100644 index 053ac96..0000000 --- a/integration_tests/dbaas-service/values.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 2019 AT&T Intellectual Property. -# Copyright (c) 2019 Nokia. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# -# This source code is part of the near-RT RIC (RAN Intelligent Controller) -# platform project (RICP). -# - -backend: - terminationGracePeriodSeconds: 0 - replicas: 1 - name: "redis-standalone" - port: 6379 - targetPort: 6379 - image: - name: nexus3.o-ran-sc.org:10002/o-ran-sc/ric-plt-dbaas - tag: 0.2.2 - imagePullPolicy: IfNotPresent diff --git a/integration_tests/getlogs.sh b/integration_tests/getlogs.sh deleted file mode 100755 index fbf2f1b..0000000 --- a/integration_tests/getlogs.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -echo "\n\n a1" > log.txt -kubectl get pods --namespace=default | awk '{ print $1 }' | egrep '^a1-a1mediator-' | xargs kubectl logs -p >> log.txt 2>&1 -kubectl get pods --namespace=default | awk '{ print $1 }' | egrep '^a1-a1mediator-' | xargs kubectl logs >> log.txt 2>&1 - -echo "\n\n test receiver" >> log.txt -# the -p gets the "previous logs" in case the prior container crashed.. very useful for debugging a new receiver. -kubectl get pods --namespace=default | awk '{ print $1 }' | egrep '^testreceiver-' | xargs -I X kubectl logs -p X testreceiver >> log.txt 2>&1 -kubectl get pods --namespace=default | awk '{ print $1 }' | egrep '^testreceiver-' | xargs -I X kubectl logs X testreceiver >> log.txt 2>&1 - -echo "\n\n delay" >> log.txt -kubectl get pods --namespace=default | awk '{ print $1 }' | egrep '^testreceiver-' | xargs -I X kubectl logs -p X delayreceiver >> log.txt 2>&1 -kubectl get pods --namespace=default | awk '{ print $1 }' | egrep '^testreceiver-' | xargs -I X kubectl logs X delayreceiver >> log.txt 2>&1 - -echo "\n\n query" >> log.txt -kubectl get pods --namespace=default | awk '{ print $1 }' | egrep '^testreceiver-' | xargs -I X kubectl logs -p X queryreceiver >> log.txt 2>&1 -kubectl get pods --namespace=default | awk '{ print $1 }' | egrep '^testreceiver-' | xargs -I X kubectl logs X queryreceiver >> log.txt 2>&1 - -echo "\n\n sdl-redis" >> log.txt -kubectl get pods --namespace=default | awk '{ print $1 }' | egrep '^redis-standalone' | xargs kubectl logs >> log.txt 2>&1 diff --git a/integration_tests/install_rmr.sh b/integration_tests/install_rmr.sh deleted file mode 100755 index 2e58f37..0000000 --- a/integration_tests/install_rmr.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -git clone --branch 4.0.5 https://gerrit.oran-osc.org/r/ric-plt/lib/rmr \ - && cd rmr \ - && mkdir .build; cd .build \ - && echo "<<>>" \ - && cmake .. -DDEV_PKG=1; make install \ - && echo "<<< installing rmr .so>>>" \ - && cmake .. -DPACK_EXTERNALS=1; sudo make install \ - && echo "cleanup" \ - && cd ../.. \ - && rm -rf rmr diff --git a/integration_tests/portforward.sh b/integration_tests/portforward.sh deleted file mode 100755 index 17f56b7..0000000 --- a/integration_tests/portforward.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -# fail on error -set -eux -pod=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=a1mediator,app.kubernetes.io/instance=a1" -o jsonpath="{.items[0].metadata.name}") -# this listener must run to forward the port, it's not just a config change -# it logs a line periodically that don't add much value, capture in a file. -rm forward.log -kubectl port-forward "$pod" 10000:10000 > forward.log 2>&1 & diff --git a/integration_tests/test_a1.tavern.yaml b/integration_tests/test_a1.tavern.yaml deleted file mode 100644 index 4657fcc..0000000 --- a/integration_tests/test_a1.tavern.yaml +++ /dev/null @@ -1,670 +0,0 @@ -# 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 admission control - -stages: - - name: type not there yet - request: - url: http://localhost:10000/a1-p/policytypes/6660666 - method: GET - response: - status_code: 404 - - - name: type list empty - request: - url: http://localhost:10000/a1-p/policytypes - method: GET - response: - status_code: 200 - json: [] - - - name: instance list 404 - request: - url: http://localhost:10000/a1-p/policytypes/6660666/policies - method: GET - response: - status_code: 404 - - - name: put the type - request: - url: http://localhost:10000/a1-p/policytypes/6660666 - method: PUT - json: - name: Admission Control - description: various parameters to control admission of dual connection - policy_type_id: 6660666 - create_schema: - "$schema": http://json-schema.org/draft-07/schema# - type: object - additionalProperties: false - properties: - class: - type: integer - minimum: 1 - maximum: 256 - description: integer id representing class to which we are applying policy - enforce: - type: boolean - description: Whether to enable or disable enforcement of policy on this class - window_length: - type: integer - minimum: 15 - maximum: 300 - description: Sliding window length in seconds - trigger_threshold: - type: integer - minimum: 1 - blocking_rate: - type: number - minimum: 0 - maximum: 100 - required: - - class - - enforce - - window_length - - trigger_threshold - - blocking_rate - response: - status_code: 201 - - - name: type there now - request: - url: http://localhost:10000/a1-p/policytypes/6660666 - method: GET - response: - status_code: 200 - - - name: now in type list - request: - url: http://localhost:10000/a1-p/policytypes - method: GET - response: - status_code: 200 - json: [6660666] - - - name: instance list 200 but empty - request: - url: http://localhost:10000/a1-p/policytypes/6660666/policies - method: GET - response: - status_code: 200 - json: [] - - - name: test the admission control policy get not there yet - request: - url: http://localhost:10000/a1-p/policytypes/6660666/policies/admission_control_policy - method: GET - response: - status_code: 404 - - - name: test the admission control policy status get not there yet - request: - url: http://localhost:10000/a1-p/policytypes/6660666/policies/admission_control_policy/status - method: GET - response: - status_code: 404 - - - name: bad body for admission control policy - request: - url: http://localhost:10000/a1-p/policytypes/6660666/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/a1-p/policytypes/6660666/policies/admission_control_policy - method: PUT - data: "asdf" - response: - status_code: 415 - - # put it properly - - name: put the admission control policy instance - request: - url: http://localhost:10000/a1-p/policytypes/6660666/policies/admission_control_policy - method: PUT - json: - class: 12 - enforce: true - window_length: 20 - blocking_rate: 20 - trigger_threshold: 10 - headers: - content-type: application/json - response: - status_code: 202 - - - name: cant delete type with instances - delay_before: 3 # wait for the type acks to come back first - request: - url: http://localhost:10000/a1-p/policytypes/6660666 - method: DELETE - response: - status_code: 400 - - - name: test the admission control policy get - request: - url: http://localhost:10000/a1-p/policytypes/6660666/policies/admission_control_policy - method: GET - response: - status_code: 200 - json: - class: 12 - enforce: true - window_length: 20 - blocking_rate: 20 - trigger_threshold: 10 - - - name: test the admission control policy status get - delay_before: 10 # give it a few seconds for rmr - request: - url: http://localhost:10000/a1-p/policytypes/6660666/policies/admission_control_policy/status - method: GET - response: - status_code: 200 - json: - instance_status: "IN EFFECT" - has_been_deleted: False - created_at: !anyfloat - - - name: instance list 200 and contains the instance - request: - url: http://localhost:10000/a1-p/policytypes/6660666/policies - method: GET - response: - status_code: 200 - json: - - admission_control_policy - - # DELETE the instance and make sure subsequent GETs return properly - - name: delete the instance - delay_after: 4 - request: - url: http://localhost:10000/a1-p/policytypes/6660666/policies/admission_control_policy - method: DELETE - response: - status_code: 202 - - - name: status should now be not in effect but still there - delay_before: 3 # give it a few seconds for rmr - delay_after: 10 # 3 + 10 > 10; that is, wait until t2 expires - request: - url: http://localhost:10000/a1-p/policytypes/6660666/policies/admission_control_policy/status - method: GET - response: - status_code: 200 - json: - instance_status: "NOT IN EFFECT" - has_been_deleted: True - deleted_at: !anyfloat - created_at: !anyfloat - - - name: instance list 200 but no instance - request: - url: http://localhost:10000/a1-p/policytypes/6660666/policies - method: GET - response: - status_code: 200 - json: [] - - - name: cant get instance status - request: - url: http://localhost:10000/a1-p/policytypes/6660666/policies/admission_control_policy/status - method: GET - response: - status_code: 404 - - - name: cant get instance - request: - url: http://localhost:10000/a1-p/policytypes/6660666/policies/admission_control_policy - method: GET - response: - status_code: 404 - - - name: delete ac type - request: - url: http://localhost:10000/a1-p/policytypes/6660666 - method: DELETE - response: - status_code: 204 - - - name: cant delete again - request: - url: http://localhost:10000/a1-p/policytypes/6660666 - method: DELETE - response: - status_code: 404 - - - name: cant get - request: - url: http://localhost:10000/a1-p/policytypes/6660666 - method: DELETE - response: - status_code: 404 - - - name: empty type list - request: - url: http://localhost:10000/a1-p/policytypes - method: GET - response: - status_code: 200 - json: [] - - ---- - -test_name: test the delay receiver - -stages: - - - name: test the delay policy type not there yet - request: - url: http://localhost:10000/a1-p/policytypes/20001 - method: GET - response: - status_code: 404 - - - name: not yet in type list - request: - url: http://localhost:10000/a1-p/policytypes - method: GET - response: - status_code: 200 - json: [] - - - name: instance list 404 - request: - url: http://localhost:10000/a1-p/policytypes/20001/policies - method: GET - response: - status_code: 404 - - - name: put the type - request: - url: http://localhost:10000/a1-p/policytypes/20001 - method: PUT - json: - name: test policy - description: just for testing - policy_type_id: 20001 - create_schema: - "$schema": http://json-schema.org/draft-07/schema# - type: object - properties: - test: - type: string - required: - - test - additionalProperties: false - response: - status_code: 201 - - - name: type there now - request: - url: http://localhost:10000/a1-p/policytypes/20001 - method: GET - response: - status_code: 200 - json: - name: test policy - description: just for testing - policy_type_id: 20001 - create_schema: - "$schema": http://json-schema.org/draft-07/schema# - type: object - properties: - test: - type: string - required: - - test - additionalProperties: false - - - name: now in type list - request: - url: http://localhost:10000/a1-p/policytypes - method: GET - response: - status_code: 200 - json: - - 20001 - - - name: instance list 200 but empty - request: - url: http://localhost:10000/a1-p/policytypes/20001/policies - method: GET - response: - status_code: 200 - json: [] - - - name: test the delay policy instance get not there yet - request: - url: http://localhost:10000/a1-p/policytypes/20001/policies/delaytest - method: GET - response: - status_code: 404 - - - name: test the delay policy status get not there yet - request: - url: http://localhost:10000/a1-p/policytypes/20001/policies/delaytest/status - method: GET - response: - status_code: 404 - - - name: bad body for delaytest - request: - url: http://localhost:10000/a1-p/policytypes/20001/policies/delaytest - method: PUT - json: - not: "welcome" - response: - status_code: 400 - - - name: create delay policy instance - request: - url: http://localhost:10000/a1-p/policytypes/20001/policies/delaytest - method: PUT - json: - test: foo - headers: - content-type: application/json - response: - status_code: 202 - - - name: test the delay status get, not in effect yet - request: - url: http://localhost:10000/a1-p/policytypes/20001/policies/delaytest/status - method: GET - response: - status_code: 200 - json: - instance_status: "NOT IN EFFECT" - has_been_deleted: False - created_at: !anyfloat - - - name: test the delay policy get - request: - url: http://localhost:10000/a1-p/policytypes/20001/policies/delaytest - method: GET - response: - status_code: 200 - json: - test: foo - - - name: instance list 200 and there - request: - url: http://localhost:10000/a1-p/policytypes/20001/policies - method: GET - response: - status_code: 200 - json: - - delaytest - - - name: test the delay status get - max_retries: 3 - delay_before: 6 # give it a few seconds for rmr ; delay reciever sleeps for 5 seconds by default - request: - url: http://localhost:10000/a1-p/policytypes/20001/policies/delaytest/status - method: GET - response: - status_code: 200 - json: - instance_status: "IN EFFECT" - has_been_deleted: False - created_at: !anyfloat - - # DELETE the instance and make sure subsequent GETs return properly - - name: delete the instance - request: - url: http://localhost:10000/a1-p/policytypes/20001/policies/delaytest - method: DELETE - response: - status_code: 202 - - - name: test the delay status get immediately - request: - url: http://localhost:10000/a1-p/policytypes/20001/policies/delaytest/status - method: GET - response: - status_code: 200 - json: - instance_status: "IN EFFECT" - has_been_deleted: True - deleted_at: !anyfloat - created_at: !anyfloat - - - name: test the delay status get after delay but before timers - delay_before: 7 - request: - url: http://localhost:10000/a1-p/policytypes/20001/policies/delaytest/status - method: GET - response: - status_code: 200 - json: - instance_status: "NOT IN EFFECT" - has_been_deleted: True - deleted_at: !anyfloat - created_at: !anyfloat - - - name: test the delay status get after delay and after the timers - delay_before: 7 - request: - url: http://localhost:10000/a1-p/policytypes/20001/policies/delaytest/status - method: GET - response: - status_code: 404 - ---- - -test_name: test query - -stages: - - name: type not there yet - request: - url: http://localhost:10000/a1-p/policytypes/1006001 - method: GET - response: - status_code: 404 - - - name: put the type - request: - url: http://localhost:10000/a1-p/policytypes/1006001 - method: PUT - json: - name: query test - description: test - policy_type_id: 1006001 - create_schema: - "$schema": http://json-schema.org/draft-07/schema# - type: object - additionalProperties: false - properties: - foo: - type: string - required: - - foo - response: - status_code: 201 - - - name: type there now - request: - url: http://localhost:10000/a1-p/policytypes/1006001 - method: GET - response: - status_code: 200 - - - name: instance list 200 but empty - request: - url: http://localhost:10000/a1-p/policytypes/1006001/policies - method: GET - response: - status_code: 200 - json: [] - - - name: instance 1 - request: - url: http://localhost:10000/a1-p/policytypes/1006001/policies/qt1 - method: PUT - json: - foo: "bar1" - headers: - content-type: application/json - response: - status_code: 202 - - - name: instance 2 - request: - url: http://localhost:10000/a1-p/policytypes/1006001/policies/qt2 - method: PUT - json: - foo: "bar2" - headers: - content-type: application/json - response: - status_code: 202 - - - name: instance list - request: - url: http://localhost:10000/a1-p/policytypes/1006001/policies - method: GET - response: - status_code: 200 - json: [qt1, qt2] - - # after the query, a1 should send, query receiver should send back, and the policy should be in effect - # sometimes in kubernetes, this test takes a long time to work because of an k8s issue - # empirically we find that the si95 rmr finally "detects" failure after about 75 seconds, retries, and then works. - - name: test the query status get - max_retries: 100 - delay_before: 1 - request: - url: http://localhost:10000/a1-p/policytypes/1006001/policies/qt1/status - method: GET - response: - status_code: 200 - json: - instance_status: "IN EFFECT" - has_been_deleted: False - created_at: !anyfloat - - - name: test the query status get 2 - max_retries: 100 - delay_before: 1 - request: - url: http://localhost:10000/a1-p/policytypes/1006001/policies/qt2/status - method: GET - response: - status_code: 200 - json: - instance_status: "IN EFFECT" - has_been_deleted: False - created_at: !anyfloat - ---- - -test_name: test bad routing file endpoint - -stages: - - - name: put the type - request: - url: http://localhost:10000/a1-p/policytypes/20002 - method: PUT - json: - name: test policy - description: just for testing - policy_type_id: 20002 - create_schema: - "$schema": http://json-schema.org/draft-07/schema# - type: object - properties: - test: - type: string - required: - - test - additionalProperties: false - - - name: create policy instance that will go to a broken routing endpoint - request: - url: http://localhost:10000/a1-p/policytypes/20002/policies/brokentest - method: PUT - json: - test: foo - headers: - content-type: application/json - response: - status_code: 202 - - - name: should be no status - request: - url: http://localhost:10000/a1-p/policytypes/20002/policies/brokentest/status - method: GET - response: - status_code: 200 - json: [] - - # this one cant currently be deleted, see the comment in a1/data.py - ---- - -test_name: bad_requests - -stages: - - - name: bad type get - request: - url: http://localhost:10000/a1-p/policytypes/20666 - method: GET - response: - status_code: 404 - - - name: bad instance get bad type - request: - url: http://localhost:10000/a1-p/policytypes/20666/policies/nonono - method: GET - response: - status_code: 404 - - - name: bad int range 1 - request: - url: http://localhost:10000/a1-p/policytypes/0 - method: PUT - json: - name: test policy - description: just for testing - policy_type_id: 0 - create_schema: - "$schema": http://json-schema.org/draft-07/schema# - type: object - response: - status_code: 400 - - - name: bad int range 2 - request: - url: http://localhost:10000/a1-p/policytypes/2147483648 - method: PUT - json: - name: test policy - description: just for testing - policy_type_id: 2147483648 - create_schema: - "$schema": http://json-schema.org/draft-07/schema# - type: object - response: - status_code: 400 diff --git a/integration_tests/testreceiver/.helmignore b/integration_tests/testreceiver/.helmignore deleted file mode 100644 index 50af031..0000000 --- a/integration_tests/testreceiver/.helmignore +++ /dev/null @@ -1,22 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/integration_tests/testreceiver/Chart.yaml b/integration_tests/testreceiver/Chart.yaml deleted file mode 100644 index 917669d..0000000 --- a/integration_tests/testreceiver/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -appVersion: "1.0" -description: Test receiver for a1 integration tests -name: testreceiver -version: 0.1.0 diff --git a/integration_tests/testreceiver/templates/_helpers.tpl b/integration_tests/testreceiver/templates/_helpers.tpl deleted file mode 100644 index f35130c..0000000 --- a/integration_tests/testreceiver/templates/_helpers.tpl +++ /dev/null @@ -1,45 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "testreceiver.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "testreceiver.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "testreceiver.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Common labels -*/}} -{{- define "testreceiver.labels" -}} -app.kubernetes.io/name: {{ include "testreceiver.name" . }} -helm.sh/chart: {{ include "testreceiver.chart" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end -}} diff --git a/integration_tests/testreceiver/templates/config.yaml b/integration_tests/testreceiver/templates/config.yaml deleted file mode 100644 index dd4772b..0000000 --- a/integration_tests/testreceiver/templates/config.yaml +++ /dev/null @@ -1,40 +0,0 @@ -#note: the xapp frame calls rmrready, which requires a route table, even if the app only uses rts. So we can never fully delete these. -# -apiVersion: v1 -kind: ConfigMap -metadata: - name: testreceiverconf -data: - local.rt: | - newrt|start - # right now the test receivers in go cannot use rts so we need this. See the comment in the receiver xapp - rte|20011|a1rmrservice:4562 - newrt|end - ---- - -apiVersion: v1 -kind: ConfigMap -metadata: - name: delayreceiverconf -data: - local.rt: | - newrt|start - # right now the test receivers in go cannot use rts so we need this. See the comment in the receiver xapp - rte|20011|a1rmrservice:4562 - newrt|end - ---- - -apiVersion: v1 -kind: ConfigMap -metadata: - name: queryreceiverconf -data: - local.rt: | - newrt|start - # this query is initiated in the query receiver - rte|20012|a1rmrservice:4562 - # right now the test receivers in go cannot use rts so we need this. See the comment in the receiver xapp - rte|20011|a1rmrservice:4562 - newrt|end diff --git a/integration_tests/testreceiver/templates/deployment.yaml b/integration_tests/testreceiver/templates/deployment.yaml deleted file mode 100644 index af17968..0000000 --- a/integration_tests/testreceiver/templates/deployment.yaml +++ /dev/null @@ -1,93 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "testreceiver.fullname" . }} - labels: -{{ include "testreceiver.labels" . | indent 4 }} -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: {{ include "testreceiver.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - template: - metadata: - labels: - app.kubernetes.io/name: {{ include "testreceiver.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - spec: - containers: - #query receiver - - name: queryreceiver - image: queryreceiver:latest - imagePullPolicy: Never - resources: - {{- toYaml .Values.resources | nindent 12 }} - volumeMounts: - - name: queryreceiverconf - mountPath: /opt/route/local.rt - subPath: local.rt - env: - # tells the test xapp to do a query - - name: DO_QUERY - value: "YES" - # this sets the source field in messages from a1 to point back to a1s service name, rather than it's random pod name - - name: RMR_SRC_ID - value: {{ .Values.queryrmrservice.name }} - - name: HANDLER_ID - value: "query_tester" - # the xapp framework requires this to work, even if SDL isn't used. - # it does an SDL healthcheck before it starts up properly - # moreover, the db config section doesn't appear to be honored; with that set, but not this, it doesn't find SDL - # so we need this here for the test receiver which uses the xapp framework to work - - name: DBAAS_SERVICE_HOST - value: "dbaas" - - name: DBAAS_SERVICE_PORT - value: "6379" - - # test receiver - - name: testreceiver - image: testreceiver:latest - imagePullPolicy: Never - resources: - {{- toYaml .Values.resources | nindent 12 }} - volumeMounts: - - name: testreceiverconf - mountPath: /opt/route/local.rt - subPath: local.rt - env: - - name: DBAAS_SERVICE_HOST - value: "dbaas" - - name: DBAAS_SERVICE_PORT - value: "6379" - - # test receiver that delays until sending - - name: delayreceiver - image: delayreceiver:latest - imagePullPolicy: Never - resources: - {{- toYaml .Values.resources | nindent 12 }} - volumeMounts: - - name: delayreceiverconf - mountPath: /opt/route/local.rt - subPath: local.rt - env: - - name: TEST_RCV_SEC_DELAY - value: "5" - - name: HANDLER_ID - value: "delay_receiver" - - name: DBAAS_SERVICE_HOST - value: "dbaas" - - name: DBAAS_SERVICE_PORT - value: "6379" - - volumes: - - name: "testreceiverconf" - configMap: - name: "testreceiverconf" - - name: "delayreceiverconf" - configMap: - name: "delayreceiverconf" - - name: "queryreceiverconf" - configMap: - name: "queryreceiverconf" diff --git a/integration_tests/testreceiver/templates/service.yaml b/integration_tests/testreceiver/templates/service.yaml deleted file mode 100644 index ecb90a0..0000000 --- a/integration_tests/testreceiver/templates/service.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ .Values.testrmrservice.name }} - labels: -{{ include "testreceiver.labels" . | indent 4 }} -spec: - type: {{ .Values.testrmrservice.type }} - ports: - - port: {{ .Values.testrmrservice.port }} - targetPort: {{ .Values.testrmrservice.port }} - protocol: TCP - selector: - app.kubernetes.io/name: {{ include "testreceiver.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - ---- - -apiVersion: v1 -kind: Service -metadata: - name: {{ .Values.delayrmrservice.name }} - labels: -{{ include "testreceiver.labels" . | indent 4 }} -spec: - type: {{ .Values.delayrmrservice.type }} - ports: - - port: {{ .Values.delayrmrservice.port }} - targetPort: {{ .Values.delayrmrservice.port }} - protocol: TCP - selector: - app.kubernetes.io/name: {{ include "testreceiver.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - ---- - -apiVersion: v1 -kind: Service -metadata: - name: {{ .Values.queryrmrservice.name }} - labels: -{{ include "testreceiver.labels" . | indent 4 }} -spec: - type: {{ .Values.queryrmrservice.type }} - ports: - - port: {{ .Values.queryrmrservice.port }} - targetPort: {{ .Values.queryrmrservice.port }} - protocol: TCP - selector: - app.kubernetes.io/name: {{ include "testreceiver.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} diff --git a/integration_tests/testreceiver/values.yaml b/integration_tests/testreceiver/values.yaml deleted file mode 100644 index f045b63..0000000 --- a/integration_tests/testreceiver/values.yaml +++ /dev/null @@ -1,16 +0,0 @@ -replicaCount: 1 - -testrmrservice: - name: testreceiverrmrservice - type: ClusterIP - port: 4560 - -delayrmrservice: - name: delayreceiverrmrservice - type: ClusterIP - port: 4563 - -queryrmrservice: - name: queryreceiverrmrservice - type: ClusterIP - port: 4564 diff --git a/integration_tests/testxappcode/Dockerfile-delay-receiver b/integration_tests/testxappcode/Dockerfile-delay-receiver deleted file mode 100644 index 5c1c239..0000000 --- a/integration_tests/testxappcode/Dockerfile-delay-receiver +++ /dev/null @@ -1,51 +0,0 @@ -# ================================================================================== -# Copyright (c) 2020 Nokia -# Copyright (c) 2020 AT&T Intellectual Property. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ================================================================================== - -# This Dockerfile uses a two stage Docker build -FROM nexus3.o-ran-sc.org:10004/o-ran-sc/bldr-alpine3:12-a3.11 - -# copy rmr headers and libraries from builder image in lieu of an Alpine package -COPY --from=nexus3.o-ran-sc.org:10002/o-ran-sc/bldr-alpine3-rmr:4.0.5 /usr/local/include/rmr /usr/local/include/rmr -COPY --from=nexus3.o-ran-sc.org:10002/o-ran-sc/bldr-alpine3-rmr:4.0.5 /usr/local/lib64/librmr* /usr/local/lib64/ - -# go will complain if there is a go.mod at the root of the GOPATH so we can't. -RUN mkdir myxapp -COPY receiver.go myxapp/receiver.go -COPY go.mod myxapp/go.mod - -# do the build -WORKDIR myxapp -ENV GO111MODULE on -ENV GO_ENABLED 0 -ENV GOOS linux -RUN go build -a -installsuffix cgo -o receiver receiver.go - -# 2nd stage -FROM alpine:3.11 - -# copy rmr libraries from builder image in lieu of an Alpine package -COPY --from=nexus3.o-ran-sc.org:10002/o-ran-sc/bldr-alpine3-rmr:4.0.5 /usr/local/lib64/librmr* /usr/local/lib64/ - -COPY --from=0 /myxapp/receiver . -COPY delay-config-file.yaml . - -# rmr setup -RUN mkdir -p /opt/route/ -ENV LD_LIBRARY_PATH /usr/local/lib:/usr/local/lib64 -ENV RMR_SEED_RT /opt/route/local.rt - -CMD ["./receiver", "-f", "delay-config-file.yaml"] diff --git a/integration_tests/testxappcode/Dockerfile-query-receiver b/integration_tests/testxappcode/Dockerfile-query-receiver deleted file mode 100644 index a7dd178..0000000 --- a/integration_tests/testxappcode/Dockerfile-query-receiver +++ /dev/null @@ -1,51 +0,0 @@ -# ================================================================================== -# Copyright (c) 2020 Nokia -# Copyright (c) 2020 AT&T Intellectual Property. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ================================================================================== - -# This Dockerfile uses a two stage Docker build -FROM nexus3.o-ran-sc.org:10004/o-ran-sc/bldr-alpine3:12-a3.11 - -# copy rmr headers and libraries from builder image in lieu of an Alpine package -COPY --from=nexus3.o-ran-sc.org:10002/o-ran-sc/bldr-alpine3-rmr:4.0.5 /usr/local/include/rmr /usr/local/include/rmr -COPY --from=nexus3.o-ran-sc.org:10002/o-ran-sc/bldr-alpine3-rmr:4.0.5 /usr/local/lib64/librmr* /usr/local/lib64/ - -# go will complain if there is a go.mod at the root of the GOPATH so we can't. -RUN mkdir myxapp -COPY receiver.go myxapp/receiver.go -COPY go.mod myxapp/go.mod - -# do the build -WORKDIR myxapp -ENV GO111MODULE on -ENV GO_ENABLED 0 -ENV GOOS linux -RUN go build -a -installsuffix cgo -o receiver receiver.go - -# 2nd stage -FROM alpine:3.11 - -# copy rmr libraries from builder image in lieu of an Alpine package -COPY --from=nexus3.o-ran-sc.org:10002/o-ran-sc/bldr-alpine3-rmr:4.0.5 /usr/local/lib64/librmr* /usr/local/lib64/ - -COPY --from=0 /myxapp/receiver . -COPY query-config-file.yaml . - -# rmr setup -RUN mkdir -p /opt/route/ -ENV LD_LIBRARY_PATH /usr/local/lib:/usr/local/lib64 -ENV RMR_SEED_RT /opt/route/local.rt - -CMD ["./receiver", "-f", "query-config-file.yaml"] diff --git a/integration_tests/testxappcode/Dockerfile-test-receiver b/integration_tests/testxappcode/Dockerfile-test-receiver deleted file mode 100644 index 196f256..0000000 --- a/integration_tests/testxappcode/Dockerfile-test-receiver +++ /dev/null @@ -1,51 +0,0 @@ -# ================================================================================== -# Copyright (c) 2020 Nokia -# Copyright (c) 2020 AT&T Intellectual Property. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ================================================================================== - -# This Dockerfile uses a two stage Docker build -FROM nexus3.o-ran-sc.org:10004/o-ran-sc/bldr-alpine3:12-a3.11 - -# copy rmr headers and libraries from builder image in lieu of an Alpine package -COPY --from=nexus3.o-ran-sc.org:10002/o-ran-sc/bldr-alpine3-rmr:4.0.5 /usr/local/include/rmr /usr/local/include/rmr -COPY --from=nexus3.o-ran-sc.org:10002/o-ran-sc/bldr-alpine3-rmr:4.0.5 /usr/local/lib64/librmr* /usr/local/lib64/ - -# go will complain if there is a go.mod at the root of the GOPATH so we can't. -RUN mkdir myxapp -COPY receiver.go myxapp/receiver.go -COPY go.mod myxapp/go.mod - -# do the build -WORKDIR myxapp -ENV GO111MODULE on -ENV GO_ENABLED 0 -ENV GOOS linux -RUN go build -a -installsuffix cgo -o receiver receiver.go - -# 2nd stage -FROM alpine:3.11 - -# copy rmr libraries from builder image in lieu of an Alpine package -COPY --from=nexus3.o-ran-sc.org:10002/o-ran-sc/bldr-alpine3-rmr:4.0.5 /usr/local/lib64/librmr* /usr/local/lib64/ - -COPY --from=0 /myxapp/receiver . -COPY test-config-file.yaml . - -# rmr setup -RUN mkdir -p /opt/route/ -ENV LD_LIBRARY_PATH /usr/local/lib:/usr/local/lib64 -ENV RMR_SEED_RT /opt/route/local.rt - -CMD ["./receiver", "-f", "test-config-file.yaml"] diff --git a/integration_tests/testxappcode/delay-config-file.yaml b/integration_tests/testxappcode/delay-config-file.yaml deleted file mode 100644 index 8db59f7..0000000 --- a/integration_tests/testxappcode/delay-config-file.yaml +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2019-2020 AT&T Intellectual Property. -# Copyright (c) 2019-2020 Nokia. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"local": - "host": ":8080" -"logger": - "level": 4 -"rmr": - "protPort": "tcp:4563" - "maxSize": 4096 - "numWorkers": 1 -"db": - "host": "dbaas" - "port": 6379 - "namespaces": ["sdl", "rnib"] -"test": - "mode": "forwarder" - "mtype": 10004 - "subId": 1111 - "size": 100 - "rate": 10 - "amount": 10 - "rounds": 1 - "store": 0 - "waitForAck": 0 - diff --git a/integration_tests/testxappcode/go.mod b/integration_tests/testxappcode/go.mod deleted file mode 100644 index 7e202d4..0000000 --- a/integration_tests/testxappcode/go.mod +++ /dev/null @@ -1,15 +0,0 @@ -go 1.13 - -module gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/example-xapp - -require ( - gerrit.o-ran-sc.org/r/ric-plt/xapp-frame v0.4.8 - github.com/go-openapi/runtime v0.19.11 // indirect - github.com/go-openapi/spec v0.19.6 // indirect -) - -replace gerrit.o-ran-sc.org/r/ric-plt/xapp-frame => gerrit.o-ran-sc.org/r/ric-plt/xapp-frame.git v0.4.8 - -replace gerrit.o-ran-sc.org/r/ric-plt/sdlgo => gerrit.o-ran-sc.org/r/ric-plt/sdlgo.git v0.7.0 - -replace gerrit.o-ran-sc.org/r/com/golog => gerrit.o-ran-sc.org/r/com/golog.git v0.0.1 diff --git a/integration_tests/testxappcode/query-config-file.yaml b/integration_tests/testxappcode/query-config-file.yaml deleted file mode 100644 index 871d166..0000000 --- a/integration_tests/testxappcode/query-config-file.yaml +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2019-2020 AT&T Intellectual Property. -# Copyright (c) 2019-2020 Nokia. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"local": - "host": ":8080" -"logger": - "level": 4 -"rmr": - "protPort": "tcp:4564" - "maxSize": 4096 - "numWorkers": 1 -"db": - "host": "dbaas" - "port": 6379 - "namespaces": ["sdl", "rnib"] -"test": - "mode": "forwarder" - "mtype": 10004 - "subId": 1111 - "size": 100 - "rate": 10 - "amount": 10 - "rounds": 1 - "store": 0 - "waitForAck": 0 - diff --git a/integration_tests/testxappcode/receiver.go b/integration_tests/testxappcode/receiver.go deleted file mode 100644 index 9012094..0000000 --- a/integration_tests/testxappcode/receiver.go +++ /dev/null @@ -1,227 +0,0 @@ -/* -================================================================================== - Copyright (c) 2020 AT&T Intellectual Property. - Copyright (c) 2020 Nokia - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -================================================================================== -*/ -package main - -import ( - "encoding/json" - "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp" - "os" - "strconv" - "time" -) - -var delay int // used for the delay receiver -var handlerID string // used for the delay receiver too -var doQuery bool // used for the query receiver - -type a1Receiver struct { - msgChan chan *xapp.RMRParams - appReady bool - rmrReady bool -} - -type policyRequest struct { - Operation string `json:"operation"` - PolicyTypeID int `json:"policy_type_id"` - PolicyInstanceID string `json:"policy_instance_id"` - Pay interface{} `json:"payload"` -} - -type policyRequestResponse struct { - PolicyTypeID int `json:"policy_type_id"` - PolicyInstanceID string `json:"policy_instance_id"` - HandlerID string `json:"handler_id"` - Status string `json:"status"` -} - -type policyQuery struct { - PolicyTypeID int `json:"policy_type_id"` -} - -func (e *a1Receiver) sendMsgRetry(params *xapp.RMRParams) { - // helper for rmr that handles retries and sleep - retries := 0 - for { // just keep trying until it works - if e.rmrReady { // we must wait for ready, else SendMsg will blow with a nullptr - if ok := xapp.Rmr.SendMsg(params); ok { - xapp.Logger.Info("Msg successfully sent after %d retries!", retries) - return - } - retries++ - //xapp.Logger.Info("Query failed to send...") - } else { - xapp.Logger.Info("rmr not ready...") - time.Sleep(time.Duration(1) * time.Second) - } - } -} - -func (e *a1Receiver) handlePolicyReq(msg *xapp.RMRParams) { - - // unmarshal the request - var dat policyRequest - if err := json.Unmarshal(msg.Payload, &dat); err != nil { - panic(err) - } - - var status string - switch dat.Operation { - case "CREATE": - status = "OK" - case "DELETE": - status = "DELETED" - } - - // form the response - res := &policyRequestResponse{ - dat.PolicyTypeID, - dat.PolicyInstanceID, - "test_receiver", - status, - } - - outgoing, err := json.Marshal(res) - if err != nil { - panic(err) - } - - /* - WARNING: - we want to use rts here. However, the current go xapp framework rts is broken. - */ - params := &xapp.RMRParams{ - Mtype: 20011, - Payload: outgoing, - } - - if delay > 0 { - xapp.Logger.Info("Xapp is sleeping...") - time.Sleep(time.Duration(delay) * time.Second) // so much work to replicate python's time.sleep(5)... - } - - e.sendMsgRetry(params) - - xapp.Logger.Info("Policy response sent!") -} - -func (e *a1Receiver) sendQuery() { - // form the query - res := &policyQuery{ - 1006001, - } - outgoing, err := json.Marshal(res) - if err != nil { - panic(err) - } - params := &xapp.RMRParams{ - Mtype: 20012, - Payload: outgoing, - } - - for { - /* We do this in a loop here, because even when the query first works, it could be the case that - a1 does not even have the type yet, or there are no instances yet. In this integration test, - we just keep pounding away so that eventually a1 returns the list this int test is looking for. - A real xapp would NOT call the query in a loop like this. - */ - e.sendMsgRetry(params) - xapp.Logger.Info("Query sent successfully") - time.Sleep(time.Duration(1) * time.Second) - } -} - -func (e *a1Receiver) messageLoop() { - for { - xapp.Logger.Info("Waiting for message..") - - msg := <-e.msgChan - - xapp.Logger.Info("Message received!") - defer xapp.Rmr.Free(msg.Mbuf) - - switch msg.Mtype { - case 20010: - e.handlePolicyReq(msg) - default: - panic("Unexpected message type!") - } - } -} - -// Consume: This named function is a required callback for e to use the xapp interface. it is called on all received rmr messages. -func (e *a1Receiver) Consume(rp *xapp.RMRParams) (err error) { - e.msgChan <- rp - return -} - -func (e *a1Receiver) Run() { - // Set MDC (read: name visible in the logs) - xapp.Logger.SetMdc(handlerID, "0.1.0") - - /* from reading the xapp frame code... - this SetReadyCB sets off a chain of events.. - it sets readycb and readycbparams at the module level in xapp.go - nothing happens yet.. - when the xapp is ran with` xapp.Run, this callback actually gets passed into the Rmr client which is not exposed in the xapp - Rmr.SetReadyCB(xappReadyCb, nil) - This "primes" the rmr client with it's own readycb, which is now set to this callback function - When the rmr client is ready, it invokes the callback - so basically, when rmr is ready, this function is invoked - I think the xapp frame code could have been greatly simplified by just passing this into the invocation of Run() and then just passing that into the rmr client init! - */ - xapp.SetReadyCB(func(d interface{}) { e.rmrReady = true }, true) - - // start message loop. We cannot wait for e.rmrReady here since that doesn't get populated until Run() runs. - go e.messageLoop() - - if doQuery { - // we are in the query tester; kick off a loop that does that until it works - go e.sendQuery() - } - - xapp.Run(e) -} - -func newA1Receiver(appReady, rmrReady bool) *a1Receiver { - return &a1Receiver{ - msgChan: make(chan *xapp.RMRParams), - rmrReady: rmrReady, - appReady: appReady, - } -} - -func main() { - - delay = 0 - if d, ok := os.LookupEnv("TEST_RCV_SEC_DELAY"); ok { - delay, _ = strconv.Atoi(d) - } - - handlerID = "test_receiver" - if hid, ok := os.LookupEnv("HANDLER_ID"); ok { - handlerID = hid - } - - doQuery = false - if _, ok := os.LookupEnv("DO_QUERY"); ok { - doQuery = true - } - - newA1Receiver(true, false).Run() -} diff --git a/integration_tests/testxappcode/test-config-file.yaml b/integration_tests/testxappcode/test-config-file.yaml deleted file mode 100644 index cd59770..0000000 --- a/integration_tests/testxappcode/test-config-file.yaml +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2019-2020 AT&T Intellectual Property. -# Copyright (c) 2019-2020 Nokia. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"local": - "host": ":8080" -"logger": - "level": 4 -"rmr": - "protPort": "tcp:4560" - "maxSize": 4096 - "numWorkers": 1 -"db": - "host": "dbaas" - "port": 6379 - "namespaces": ["sdl", "rnib"] -"test": - "mode": "forwarder" - "mtype": 10004 - "subId": 1111 - "size": 100 - "rate": 10 - "amount": 10 - "rounds": 1 - "store": 0 - "waitForAck": 0 - diff --git a/local.rt b/local.rt deleted file mode 100644 index 02816b5..0000000 --- a/local.rt +++ /dev/null @@ -1,4 +0,0 @@ -# Trivial RMR route table -newrt | start -rte | 1 | app10:4560 -newrt | end diff --git a/a1-go/pkg/a1/a1.go b/pkg/a1/a1.go similarity index 100% rename from a1-go/pkg/a1/a1.go rename to pkg/a1/a1.go diff --git a/a1-go/pkg/a1/a1_test.go b/pkg/a1/a1_test.go similarity index 100% rename from a1-go/pkg/a1/a1_test.go rename to pkg/a1/a1_test.go diff --git a/a1-go/pkg/models/policy_instance_id.go b/pkg/models/policy_instance_id.go similarity index 100% rename from a1-go/pkg/models/policy_instance_id.go rename to pkg/models/policy_instance_id.go diff --git a/a1-go/pkg/models/policy_type_id.go b/pkg/models/policy_type_id.go similarity index 100% rename from a1-go/pkg/models/policy_type_id.go rename to pkg/models/policy_type_id.go diff --git a/a1-go/pkg/models/policy_type_schema.go b/pkg/models/policy_type_schema.go similarity index 100% rename from a1-go/pkg/models/policy_type_schema.go rename to pkg/models/policy_type_schema.go diff --git a/a1-go/pkg/policy/policyManager.go b/pkg/policy/policyManager.go similarity index 100% rename from a1-go/pkg/policy/policyManager.go rename to pkg/policy/policyManager.go diff --git a/a1-go/pkg/policy/policyManager_test.go b/pkg/policy/policyManager_test.go similarity index 100% rename from a1-go/pkg/policy/policyManager_test.go rename to pkg/policy/policyManager_test.go diff --git a/a1-go/pkg/policy/types.go b/pkg/policy/types.go similarity index 100% rename from a1-go/pkg/policy/types.go rename to pkg/policy/types.go diff --git a/a1-go/pkg/restapi/configure_a1.go b/pkg/restapi/configure_a1.go similarity index 100% rename from a1-go/pkg/restapi/configure_a1.go rename to pkg/restapi/configure_a1.go diff --git a/a1-go/pkg/restapi/doc.go b/pkg/restapi/doc.go similarity index 100% rename from a1-go/pkg/restapi/doc.go rename to pkg/restapi/doc.go diff --git a/a1-go/pkg/restapi/embedded_spec.go b/pkg/restapi/embedded_spec.go similarity index 100% rename from a1-go/pkg/restapi/embedded_spec.go rename to pkg/restapi/embedded_spec.go diff --git a/a1-go/pkg/restapi/operations/a1_api.go b/pkg/restapi/operations/a1_api.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_api.go rename to pkg/restapi/operations/a1_api.go diff --git a/a1-go/pkg/restapi/operations/a1_e_i_data_delivery/a1_controller_data_delivery.go b/pkg/restapi/operations/a1_e_i_data_delivery/a1_controller_data_delivery.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_e_i_data_delivery/a1_controller_data_delivery.go rename to pkg/restapi/operations/a1_e_i_data_delivery/a1_controller_data_delivery.go diff --git a/a1-go/pkg/restapi/operations/a1_e_i_data_delivery/a1_controller_data_delivery_parameters.go b/pkg/restapi/operations/a1_e_i_data_delivery/a1_controller_data_delivery_parameters.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_e_i_data_delivery/a1_controller_data_delivery_parameters.go rename to pkg/restapi/operations/a1_e_i_data_delivery/a1_controller_data_delivery_parameters.go diff --git a/a1-go/pkg/restapi/operations/a1_e_i_data_delivery/a1_controller_data_delivery_responses.go b/pkg/restapi/operations/a1_e_i_data_delivery/a1_controller_data_delivery_responses.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_e_i_data_delivery/a1_controller_data_delivery_responses.go rename to pkg/restapi/operations/a1_e_i_data_delivery/a1_controller_data_delivery_responses.go diff --git a/a1-go/pkg/restapi/operations/a1_e_i_data_delivery/a1_controller_data_delivery_urlbuilder.go b/pkg/restapi/operations/a1_e_i_data_delivery/a1_controller_data_delivery_urlbuilder.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_e_i_data_delivery/a1_controller_data_delivery_urlbuilder.go rename to pkg/restapi/operations/a1_e_i_data_delivery/a1_controller_data_delivery_urlbuilder.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_create_or_replace_policy_instance.go b/pkg/restapi/operations/a1_mediator/a1_controller_create_or_replace_policy_instance.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_create_or_replace_policy_instance.go rename to pkg/restapi/operations/a1_mediator/a1_controller_create_or_replace_policy_instance.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_create_or_replace_policy_instance_parameters.go b/pkg/restapi/operations/a1_mediator/a1_controller_create_or_replace_policy_instance_parameters.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_create_or_replace_policy_instance_parameters.go rename to pkg/restapi/operations/a1_mediator/a1_controller_create_or_replace_policy_instance_parameters.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_create_or_replace_policy_instance_responses.go b/pkg/restapi/operations/a1_mediator/a1_controller_create_or_replace_policy_instance_responses.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_create_or_replace_policy_instance_responses.go rename to pkg/restapi/operations/a1_mediator/a1_controller_create_or_replace_policy_instance_responses.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_create_or_replace_policy_instance_urlbuilder.go b/pkg/restapi/operations/a1_mediator/a1_controller_create_or_replace_policy_instance_urlbuilder.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_create_or_replace_policy_instance_urlbuilder.go rename to pkg/restapi/operations/a1_mediator/a1_controller_create_or_replace_policy_instance_urlbuilder.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_create_policy_type.go b/pkg/restapi/operations/a1_mediator/a1_controller_create_policy_type.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_create_policy_type.go rename to pkg/restapi/operations/a1_mediator/a1_controller_create_policy_type.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_create_policy_type_parameters.go b/pkg/restapi/operations/a1_mediator/a1_controller_create_policy_type_parameters.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_create_policy_type_parameters.go rename to pkg/restapi/operations/a1_mediator/a1_controller_create_policy_type_parameters.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_create_policy_type_responses.go b/pkg/restapi/operations/a1_mediator/a1_controller_create_policy_type_responses.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_create_policy_type_responses.go rename to pkg/restapi/operations/a1_mediator/a1_controller_create_policy_type_responses.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_create_policy_type_urlbuilder.go b/pkg/restapi/operations/a1_mediator/a1_controller_create_policy_type_urlbuilder.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_create_policy_type_urlbuilder.go rename to pkg/restapi/operations/a1_mediator/a1_controller_create_policy_type_urlbuilder.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_instance.go b/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_instance.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_instance.go rename to pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_instance.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_instance_parameters.go b/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_instance_parameters.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_instance_parameters.go rename to pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_instance_parameters.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_instance_responses.go b/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_instance_responses.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_instance_responses.go rename to pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_instance_responses.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_instance_urlbuilder.go b/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_instance_urlbuilder.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_instance_urlbuilder.go rename to pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_instance_urlbuilder.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_type.go b/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_type.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_type.go rename to pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_type.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_type_parameters.go b/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_type_parameters.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_type_parameters.go rename to pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_type_parameters.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_type_responses.go b/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_type_responses.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_type_responses.go rename to pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_type_responses.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_type_urlbuilder.go b/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_type_urlbuilder.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_type_urlbuilder.go rename to pkg/restapi/operations/a1_mediator/a1_controller_delete_policy_type_urlbuilder.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_all_instances_for_type.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_all_instances_for_type.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_all_instances_for_type.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_all_instances_for_type.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_all_instances_for_type_parameters.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_all_instances_for_type_parameters.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_all_instances_for_type_parameters.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_all_instances_for_type_parameters.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_all_instances_for_type_responses.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_all_instances_for_type_responses.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_all_instances_for_type_responses.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_all_instances_for_type_responses.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_all_instances_for_type_urlbuilder.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_all_instances_for_type_urlbuilder.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_all_instances_for_type_urlbuilder.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_all_instances_for_type_urlbuilder.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_all_policy_types.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_all_policy_types.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_all_policy_types.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_all_policy_types.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_all_policy_types_parameters.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_all_policy_types_parameters.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_all_policy_types_parameters.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_all_policy_types_parameters.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_all_policy_types_responses.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_all_policy_types_responses.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_all_policy_types_responses.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_all_policy_types_responses.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_all_policy_types_urlbuilder.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_all_policy_types_urlbuilder.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_all_policy_types_urlbuilder.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_all_policy_types_urlbuilder.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_healthcheck.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_healthcheck.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_healthcheck.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_healthcheck.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_healthcheck_parameters.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_healthcheck_parameters.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_healthcheck_parameters.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_healthcheck_parameters.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_healthcheck_responses.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_healthcheck_responses.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_healthcheck_responses.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_healthcheck_responses.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_healthcheck_urlbuilder.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_healthcheck_urlbuilder.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_healthcheck_urlbuilder.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_healthcheck_urlbuilder.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_parameters.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_parameters.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_parameters.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_parameters.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_responses.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_responses.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_responses.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_responses.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_status.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_status.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_status.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_status.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_status_parameters.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_status_parameters.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_status_parameters.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_status_parameters.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_status_responses.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_status_responses.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_status_responses.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_status_responses.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_status_urlbuilder.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_status_urlbuilder.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_status_urlbuilder.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_status_urlbuilder.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_urlbuilder.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_urlbuilder.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_urlbuilder.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_policy_instance_urlbuilder.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_type.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_type.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_type.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_policy_type.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_type_parameters.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_type_parameters.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_type_parameters.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_policy_type_parameters.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_type_responses.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_type_responses.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_type_responses.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_policy_type_responses.go diff --git a/a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_type_urlbuilder.go b/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_type_urlbuilder.go similarity index 100% rename from a1-go/pkg/restapi/operations/a1_mediator/a1_controller_get_policy_type_urlbuilder.go rename to pkg/restapi/operations/a1_mediator/a1_controller_get_policy_type_urlbuilder.go diff --git a/a1-go/pkg/restapi/server.go b/pkg/restapi/server.go similarity index 100% rename from a1-go/pkg/restapi/server.go rename to pkg/restapi/server.go diff --git a/a1-go/pkg/restful/restful.go b/pkg/restful/restful.go similarity index 100% rename from a1-go/pkg/restful/restful.go rename to pkg/restful/restful.go diff --git a/a1-go/pkg/restful/types.go b/pkg/restful/types.go similarity index 100% rename from a1-go/pkg/restful/types.go rename to pkg/restful/types.go diff --git a/a1-go/pkg/resthooks/resthooks.go b/pkg/resthooks/resthooks.go similarity index 100% rename from a1-go/pkg/resthooks/resthooks.go rename to pkg/resthooks/resthooks.go diff --git a/a1-go/pkg/resthooks/resthooks_test.go b/pkg/resthooks/resthooks_test.go similarity index 100% rename from a1-go/pkg/resthooks/resthooks_test.go rename to pkg/resthooks/resthooks_test.go diff --git a/a1-go/pkg/resthooks/types.go b/pkg/resthooks/types.go similarity index 100% rename from a1-go/pkg/resthooks/types.go rename to pkg/resthooks/types.go diff --git a/a1-go/pkg/rmr/messages.go b/pkg/rmr/messages.go similarity index 100% rename from a1-go/pkg/rmr/messages.go rename to pkg/rmr/messages.go diff --git a/a1-go/pkg/rmr/rmr.go b/pkg/rmr/rmr.go similarity index 100% rename from a1-go/pkg/rmr/rmr.go rename to pkg/rmr/rmr.go diff --git a/releases/container-release-ric-plt-a1.yaml b/releases/container-release-ric-plt-a1.yaml deleted file mode 100644 index 410749f..0000000 --- a/releases/container-release-ric-plt-a1.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -distribution_type: container -container_release_tag: 2.5.2 -container_pull_registry: nexus3.o-ran-sc.org:10004 -container_push_registry: nexus3.o-ran-sc.org:10002 -project: ric-plt/a1 -ref: f0ac3fb27a3bda65076f790def3284065c95b098 -containers: - - name: ric-plt-a1 - version: 2.5.2 diff --git a/rmr-version.yaml b/rmr-version.yaml deleted file mode 100644 index 02723fb..0000000 --- a/rmr-version.yaml +++ /dev/null @@ -1,3 +0,0 @@ -# CI script installs RMR from PackageCloud using this version ---- -version: 4.7.4 diff --git a/setup.py b/setup.py deleted file mode 100644 index 7b0684a..0000000 --- a/setup.py +++ /dev/null @@ -1,30 +0,0 @@ -# ================================================================================== -# Copyright (c) 2019-2020 Nokia -# Copyright (c) 2018-2020 AT&T Intellectual Property. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ================================================================================== -from setuptools import setup, find_packages - -setup( - name="a1", - 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", "prometheus-client", "mdclogpy", "ricxappframe>=2.0.0,<3.0.0"], - package_data={"a1": ["openapi.yaml"]}, -) diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index f4c84bf..0000000 --- a/tests/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# ================================================================================== -# Copyright (c) 2019 Nokia -# Copyright (c) 2018-2019 AT&T Intellectual Property. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ================================================================================== - -# empty __init__.py so that pytest can add correct path to coverage report, -- per pytest -# https://github.com/pytest-dev/pytest-cov/issues/98#issuecomment-451344057 diff --git a/tests/conftest.py b/tests/conftest.py deleted file mode 100644 index c5b3a57..0000000 --- a/tests/conftest.py +++ /dev/null @@ -1,85 +0,0 @@ -""" -pytest conftest -""" -# ================================================================================== -# Copyright (c) 2019 Nokia -# Copyright (c) 2018-2019 AT&T Intellectual Property. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ================================================================================== -import tempfile -import os -import pytest -from a1 import app - - -@pytest.fixture -def client(): - """ - http://flask.pocoo.org/docs/1.0/testing/ - """ - - db_fd, app.app.config["DATABASE"] = tempfile.mkstemp() - app.app.config["TESTING"] = True - cl = app.app.test_client() - - yield cl - - os.close(db_fd) - os.unlink(app.app.config["DATABASE"]) - - -@pytest.fixture -def adm_type_good(): - """ - represents a good put for adm control type - """ - return { - "name": "Policy for Rate Control", - "policy_type_id": 6660666, - "description": "This policy is associated with rate control. An instance of the policy specifies the traffic class to which it applies and parameters to use to control how much it must be throttled in case of an overload. Each instance of the policy that is created MUST be associated with a unique class ID (identifyed by the key 'class', which is used by the xAPP to differentiate traffic. If an agent tries to create a policy with the SAME class id, it will be rejected by the xAPP, even if it has a unique policy instance id. ", - "create_schema": { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": False, - "properties": { - "class": { - "type": "integer", - "minimum": 1, - "maximum": 256, - "description": "integer id representing class to which we are applying policy", - }, - "enforce": { - "type": "boolean", - "description": "Whether to enable or disable enforcement of policy on this class", - }, - "window_length": { - "type": "integer", - "minimum": 15, - "maximum": 300, - "description": "Sliding window length in seconds", - }, - "trigger_threshold": {"type": "integer", "minimum": 1}, - "blocking_rate": {"type": "number", "minimum": 0, "maximum": 100}, - }, - "required": ["class", "enforce", "blocking_rate", "trigger_threshold", "window_length"], - }, - } - - -@pytest.fixture -def adm_instance_good(): - """ - represents a good put for adm control instance - """ - return {"class": 12, "enforce": True, "window_length": 20, "blocking_rate": 20, "trigger_threshold": 10} diff --git a/tests/test_controller.py b/tests/test_controller.py deleted file mode 100644 index 9a73ea8..0000000 --- a/tests/test_controller.py +++ /dev/null @@ -1,413 +0,0 @@ -""" -tests for controller -""" -# ================================================================================== -# Copyright (c) 2019-2020 Nokia -# Copyright (c) 2018-2020 AT&T Intellectual Property. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ================================================================================== -import time -import json -from ricxappframe.rmr.rmr_mocks import rmr_mocks -from ricxappframe.xapp_sdl import SDLWrapper -from ricsdl.exceptions import RejectedByBackend, NotConnected, BackendError -from a1 import a1rmr, data - -RCV_ID = "test_receiver" -ADM_CRTL_TID = 6660666 -ADM_CTRL_IID = "admission_control_policy" -ADM_CTRL_POLICIES = "/a1-p/policytypes/{0}/policies".format(ADM_CRTL_TID) -ADM_CTRL_INSTANCE = ADM_CTRL_POLICIES + "/" + ADM_CTRL_IID -ADM_CTRL_INSTANCE_STATUS = ADM_CTRL_INSTANCE + "/status" -ADM_CTRL_TYPE = "/a1-p/policytypes/{0}".format(ADM_CRTL_TID) -ACK_MT = 20011 - - -def _fake_dequeue(): - """for monkeypatching with a good status""" - pay = json.dumps( - {"policy_type_id": ADM_CRTL_TID, "policy_instance_id": ADM_CTRL_IID, "handler_id": RCV_ID, "status": "OK"} - ).encode() - fake_msg = {"payload": pay, "message type": ACK_MT} - return [(fake_msg, None)] - - -def _fake_dequeue_none(): - """for monkeypatching with no waiting messages""" - return [] - - -def _fake_dequeue_deleted(): - """for monkeypatching with a DELETED status""" - new_msgs = [] - good_pay = json.dumps( - {"policy_type_id": ADM_CRTL_TID, "policy_instance_id": ADM_CTRL_IID, "handler_id": RCV_ID, "status": "DELETED"} - ).encode() - - # non existent type id - pay = json.dumps( - {"policy_type_id": 911, "policy_instance_id": ADM_CTRL_IID, "handler_id": RCV_ID, "status": "DELETED"} - ).encode() - fake_msg = {"payload": pay, "message type": ACK_MT} - new_msgs.append((fake_msg, None)) - - # bad instance id - pay = json.dumps( - {"policy_type_id": ADM_CRTL_TID, "policy_instance_id": "darkness", "handler_id": RCV_ID, "status": "DELETED"} - ).encode() - fake_msg = {"payload": pay, "message type": ACK_MT} - new_msgs.append((fake_msg, None)) - - # good body but bad message type - fake_msg = {"payload": good_pay, "message type": ACK_MT * 3} - new_msgs.append((fake_msg, None)) - - # insert a bad one with a malformed body to make sure we keep going - new_msgs.append(({"payload": "asdf", "message type": ACK_MT}, None)) - - # not even a json - new_msgs.append(("asdf", None)) - - # good - fake_msg = {"payload": good_pay, "message type": ACK_MT} - new_msgs.append((fake_msg, None)) - - return new_msgs - - -def _test_put_patch(monkeypatch): - rmr_mocks.patch_rmr(monkeypatch) - # assert that rmr bad states don't cause problems - monkeypatch.setattr("ricxappframe.rmr.rmr.rmr_send_msg", rmr_mocks.send_mock_generator(10)) - - -def _no_ac(client): - # no type there yet - res = client.get(ADM_CTRL_TYPE) - assert res.status_code == 404 - - # no types at all - res = client.get("/a1-p/policytypes") - assert res.status_code == 200 - assert res.json == [] - - # instance 404 because type not there yet - res = client.get(ADM_CTRL_POLICIES) - assert res.status_code == 404 - - -def _put_ac_type(client, typedef): - _no_ac(client) - - # put the type - res = client.put(ADM_CTRL_TYPE, json=typedef) - assert res.status_code == 201 - - # cant replace types - res = client.put(ADM_CTRL_TYPE, json=typedef) - assert res.status_code == 400 - - # type there now - res = client.get(ADM_CTRL_TYPE) - assert res.status_code == 200 - assert res.json == typedef - - # type in type list - res = client.get("/a1-p/policytypes") - assert res.status_code == 200 - assert res.json == [ADM_CRTL_TID] - - # instance 200 but empty list - res = client.get(ADM_CTRL_POLICIES) - assert res.status_code == 200 - assert res.json == [] - - -def _delete_ac_type(client): - res = client.delete(ADM_CTRL_TYPE) - assert res.status_code == 204 - - # cant get - res = client.get(ADM_CTRL_TYPE) - assert res.status_code == 404 - - # cant invoke delete on it again - res = client.delete(ADM_CTRL_TYPE) - assert res.status_code == 404 - - _no_ac(client) - - -def _put_ac_instance(client, monkeypatch, instancedef): - # no instance there yet - res = client.get(ADM_CTRL_INSTANCE) - assert res.status_code == 404 - res = client.get(ADM_CTRL_INSTANCE_STATUS) - assert res.status_code == 404 - - # create a good instance - _test_put_patch(monkeypatch) - res = client.put(ADM_CTRL_INSTANCE, json=instancedef) - assert res.status_code == 202 - - # replace is allowed on instances - res = client.put(ADM_CTRL_INSTANCE, json=instancedef) - assert res.status_code == 202 - - # instance 200 and in list - res = client.get(ADM_CTRL_POLICIES) - assert res.status_code == 200 - assert res.json == [ADM_CTRL_IID] - - -def _delete_instance(client): - # cant delete type until there are no instances - res = client.delete(ADM_CTRL_TYPE) - assert res.status_code == 400 - - # delete it - res = client.delete(ADM_CTRL_INSTANCE) - assert res.status_code == 202 - - # should be able to do multiple deletes until it's actually gone - res = client.delete(ADM_CTRL_INSTANCE) - assert res.status_code == 202 - - -def _instance_is_gone(client, seconds_to_try=10): - for _ in range(seconds_to_try): - # idea here is that we have to wait for the seperate thread to process the event - try: - res = client.get(ADM_CTRL_INSTANCE_STATUS) - assert res.status_code == 404 - except AssertionError: - time.sleep(1) - - res = client.get(ADM_CTRL_INSTANCE_STATUS) - assert res.status_code == 404 - - # list still 200 but no instance - res = client.get(ADM_CTRL_POLICIES) - assert res.status_code == 200 - assert res.json == [] - - # cant get instance - res = client.get(ADM_CTRL_INSTANCE) - assert res.status_code == 404 - - -def _verify_instance_and_status(client, expected_instance, expected_status, expected_deleted, seconds_to_try=5): - # get the instance - res = client.get(ADM_CTRL_INSTANCE) - assert res.status_code == 200 - assert res.json == expected_instance - - for _ in range(seconds_to_try): - # idea here is that we have to wait for the seperate thread to process the event - res = client.get(ADM_CTRL_INSTANCE_STATUS) - assert res.status_code == 200 - assert res.json["has_been_deleted"] == expected_deleted - try: - assert res.json["instance_status"] == expected_status - return - except AssertionError: - time.sleep(1) - assert res.json["instance_status"] == expected_status - - -# Module level Hack - - -def setup_module(): - """module level setup""" - - # swap sdl for the fake backend - data.SDL = SDLWrapper(use_fake_sdl=True) - - def noop(): - pass - - # launch the thread with a fake init func and a patched rcv func; we will "repatch" later - a1rmr.start_rmr_thread(init_func_override=noop, rcv_func_override=_fake_dequeue_none) - - -# Actual Tests - - -def test_workflow(client, monkeypatch, adm_type_good, adm_instance_good): - """ - test a full A1 workflow - """ - - # put type and instance - _put_ac_type(client, adm_type_good) - _put_ac_instance(client, monkeypatch, adm_instance_good) - - """ - we test the state transition diagram of all 5 states here; - 1. not in effect, not deleted - 2. in effect, not deleted - 3. in effect, deleted - 4. not in effect, deleted - 5. gone (timeout expires) - """ - - # try a status get but we didn't get any ACKs yet to test NOT IN EFFECT - _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", False) - - # now pretend we did get a good ACK - a1rmr.replace_rcv_func(_fake_dequeue) - _verify_instance_and_status(client, adm_instance_good, "IN EFFECT", False) - - # delete the instance - _delete_instance(client) - - # status after a delete, but there are no messages yet, should still return - _verify_instance_and_status(client, adm_instance_good, "IN EFFECT", True) - - # now pretend we deleted successfully - a1rmr.replace_rcv_func(_fake_dequeue_deleted) - - # status should be reflected first (before delete triggers) - _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", True) - - # instance should be totally gone after a few seconds - _instance_is_gone(client) - - # delete the type - _delete_ac_type(client) - - -def test_cleanup_via_t1(client, monkeypatch, adm_type_good, adm_instance_good): - """ - create a type, create an instance, but no acks ever come in, delete instance - """ - _put_ac_type(client, adm_type_good) - - a1rmr.replace_rcv_func(_fake_dequeue_none) - - _put_ac_instance(client, monkeypatch, adm_instance_good) - - """ - here we test the state transition diagram when it never goes into effect: - 1. not in effect, not deleted - 2. not in effect, deleted - 3. gone (timeout expires) - """ - - _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", False) - - # delete the instance - _delete_instance(client) - - _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", True) - - # instance should be totally gone after a few seconds - _instance_is_gone(client) - - # delete the type - _delete_ac_type(client) - - -def test_bad_instances(client, monkeypatch, adm_type_good): - """ - test various failure modes - """ - # put the type (needed for some of the tests below) - rmr_mocks.patch_rmr(monkeypatch) - res = client.put(ADM_CTRL_TYPE, json=adm_type_good) - assert res.status_code == 201 - - # bad body - res = client.put(ADM_CTRL_INSTANCE, json={"not": "expected"}) - assert res.status_code == 400 - - # bad media type - res = client.put(ADM_CTRL_INSTANCE, data="notajson") - assert res.status_code == 415 - - # delete a non existent instance - res = client.delete(ADM_CTRL_INSTANCE + "DARKNESS") - assert res.status_code == 404 - - # get a non existent instance - a1rmr.replace_rcv_func(_fake_dequeue) - res = client.get(ADM_CTRL_INSTANCE + "DARKNESS") - assert res.status_code == 404 - - # delete the type (as cleanup) - res = client.delete(ADM_CTRL_TYPE) - assert res.status_code == 204 - - # test 503 handlers - - def monkey_set(ns, key, value): - # set a key override function that throws sdl errors on certain keys - if key == "a1.policy_type.111": - raise RejectedByBackend() - if key == "a1.policy_type.112": - raise NotConnected() - if key == "a1.policy_type.113": - raise BackendError() - - monkeypatch.setattr("a1.data.SDL.set", monkey_set) - - def create_alt_id(json, id): - """ - Overwrites the json's policy type ID, attempts create and tests for 503 - """ - json['policy_type_id'] = id - url = "/a1-p/policytypes/{0}".format(id) - res = client.put(url, json=json) - assert res.status_code == 503 - - create_alt_id(adm_type_good, 111) - create_alt_id(adm_type_good, 112) - create_alt_id(adm_type_good, 113) - - -def test_illegal_types(client, adm_type_good): - """ - Test illegal types - """ - # below valid range - res = client.put("/a1-p/policytypes/0", json=adm_type_good) - assert res.status_code == 400 - # ID mismatch - res = client.put("/a1-p/policytypes/1", json=adm_type_good) - assert res.status_code == 400 - # above valid range - res = client.put("/a1-p/policytypes/2147483648", json=adm_type_good) - assert res.status_code == 400 - - -def test_healthcheck(client): - """ - test healthcheck - """ - res = client.get("/a1-p/healthcheck") - 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-integration.ini b/tox-integration.ini deleted file mode 100644 index c065542..0000000 --- a/tox-integration.ini +++ /dev/null @@ -1,76 +0,0 @@ -# ================================================================================== -# Copyright (c) 2019-2020 Nokia -# Copyright (c) 2018-2020 AT&T Intellectual Property. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ================================================================================== -[tox] -envlist = int - -[testenv:int] -whitelist_externals= - sleep - helm - ab - echo - pkill - kubectl - getlogs.sh -passenv = * -deps = - tavern == 1.2.2 # pin the version -changedir=integration_tests -commands_pre= - echo "WARNING: make sure you are running with latest docker builds!" -# Fish function that builds and tags the 3 receivers. Run from integration_tests/testxappcode/ -#function a1intbuild -# docker build -t a1:latest . -# cd integration_tests/testxappcode -# docker build -t testreceiver:latest . -f Dockerfile-test-receiver -# docker build -t delayreceiver:latest . -f Dockerfile-delay-receiver -# docker build -t queryreceiver:latest . -f Dockerfile-query-receiver -# cd ../.. -#end - sleep 3 -# helm v3 is helm install [name] [chart] - echo "linting" - helm lint a1mediator - helm lint testreceiver - helm lint dbaas-service - helm install --devel testreceiver testreceiver - helm install --devel a1 a1mediator - helm install --devel dbaas dbaas-service - kubectl get pods --namespace=default - echo "delay so pods can start" - sleep 60 - kubectl get pods --namespace=default - echo "forward ports" - ./portforward.sh - echo "delay so port forward can start" - sleep 5 -commands= - echo "running tavern via pytest" - pytest --version - pytest test_a1.tavern.yaml - echo "running apache bench (ab) on healthcheck endpoint" - # use -v 4 to make ab chatty - ab -n 100 -c 10 -v 0 http://localhost:10000/a1-p/healthcheck -commands_post= - echo "log collection" - ./getlogs.sh - echo "teardown" - helm delete testreceiver - helm delete a1 - helm delete dbaas - pkill -9 kubectl - sleep 10 diff --git a/tox.ini b/tox.ini deleted file mode 100644 index e305c60..0000000 --- a/tox.ini +++ /dev/null @@ -1,84 +0,0 @@ -# ================================================================================== -# Copyright (c) 2019-2020 Nokia -# Copyright (c) 2018-2020 AT&T Intellectual Property. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ================================================================================== - -# Code - -[tox] -envlist = code,flake8,docs,docs-linkcheck -minversion = 2.0 - -[pytest] -testpaths = tests - -[testenv:code] -basepython = python3 -deps= - pytest - coverage - pytest-cov -setenv = - LD_LIBRARY_PATH = /usr/local/lib/:/usr/local/lib64 - 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 = -# sometimes the -s flag is helpful; add -s after pytest; which streams the logs as they come in, rather than saving them all for the end of tests - pytest --cov a1 --cov-report xml --cov-report term-missing --cov-report html --cov-fail-under=70 --junitxml=/tmp/tests.xml - coverage xml -i - -[testenv:flake8] -basepython = python3 -skip_install = true -deps = flake8 -commands = flake8 setup.py a1 tests - -[flake8] -extend-ignore = E501,E741,E731 - -[testenv:clm] -# use pip to gather dependencies with versions for CLM analysis -whitelist_externals = sh -commands = sh -c 'pip freeze > requirements.txt' - -# Docs - -# verbatim as asked for by the docs instructions: https://wiki.o-ran-sc.org/display/DOC/Configure+Repo+for+Documentation -[testenv:docs] -basepython = python3 -deps = - sphinx - sphinx-rtd-theme - sphinxcontrib-httpdomain - recommonmark - lfdocs-conf - -commands = - sphinx-build -W -b html -n -d {envtmpdir}/doctrees ./docs/ {toxinidir}/docs/_build/html - echo "Generated docs available in {toxinidir}/docs/_build/html" -whitelist_externals = echo - -[testenv:docs-linkcheck] -basepython = python3 -deps = sphinx - sphinx-rtd-theme - sphinxcontrib-httpdomain - recommonmark - lfdocs-conf -commands = sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees ./docs/ {toxinidir}/docs/_build/linkcheck -- 2.16.6