From 14eff645eb8db8f5d9fdc7d47bb081c505790509 Mon Sep 17 00:00:00 2001 From: dave kormann Date: Sat, 15 Feb 2020 03:38:42 -0500 Subject: [PATCH] IMPL: A1 <-> XApp Message Flow Testing This change adds support for testing RMR message flow between the A1 mediator and XApps. It consists of two major components: o An XApp which receives policy updates from A1 o A Robot Framework testsuite to send policy requests to A1 This change also cleans up the ric_robot_suite subdirectory somewhat as part of an ongoing process of improving robot usability. Signed-off-by: dave kormann Change-Id: I589e9202ab277924ac848f912a01ee718cb17e14 --- README.md | 20 +- ric_robot_suite/README | 23 ++ ric_robot_suite/docker/nanobot/Dockerfile | 1 + .../{README-nanobot => docker/nanobot/README} | 14 +- ric_robot_suite/docker/ric-robot/Dockerfile | 10 +- ric_robot_suite/{ => docker/ric-robot}/README.md | 0 ric_robot_suite/{ => docker/ric-robot}/build.sh | 0 ric_robot_suite/{ => docker/ric-robot}/docgen.py | 0 .../config/integration_robot_properties.py | 0 .../ric-robot}/eteshare/config/vm_properties.py | 0 .../ric-robot}/eteshare/health/index.php | 0 .../ric-robot}/eteshare/nodeb/setup/index.php | 0 .../eteshare/ric/v1/xapps/DemoXapp1/101/index.php | 0 .../eteshare/ric/v1/xapps/DemoXapp1/index.php | 0 .../ric-robot}/eteshare/ric/v1/xapps/index.php | 0 .../{ => docker/ric-robot}/html/index.html | 0 ric_robot_suite/{ => docker/ric-robot}/red.xml | 0 ric_robot_suite/{ => docker/ric-robot}/runTags.sh | 0 ric_robot_suite/{ => docker/ric-robot}/setup.sh | 0 .../{ => docker/ric-robot}/version.properties | 0 ric_robot_suite/docker/robot-xapp/Dockerfile | 52 +++ ric_robot_suite/helm/nanobot/README | 45 ++- ric_robot_suite/helm/nanobot/charts/.PLACEHOLDER | 0 .../public/properties/global_properties.robot | 9 + .../public/testsuites/a1mediator.robot | 129 ++++++++ ric_robot_suite/helm/nanobot/values.yaml | 4 +- ric_robot_suite/helm/{ => ric-robot}/README.md | 0 .../helm/{ => ric-robot}/robot_install.sh | 0 .../a1mediator/a1mediator_interface.robot | 118 +++++++ ric_robot_suite/xapp/README | 13 + ric_robot_suite/xapp/config-file.json | 80 +++++ ric_robot_suite/xapp/local.rt | 5 + ric_robot_suite/xapp/schema.json | 357 +++++++++++++++++++++ ric_robot_suite/xapp/src/go.mod | 11 + ric_robot_suite/xapp/src/robot-xapp.go | 165 ++++++++++ 35 files changed, 1018 insertions(+), 38 deletions(-) create mode 100644 ric_robot_suite/README rename ric_robot_suite/{README-nanobot => docker/nanobot/README} (85%) rename ric_robot_suite/{ => docker/ric-robot}/README.md (100%) rename ric_robot_suite/{ => docker/ric-robot}/build.sh (100%) rename ric_robot_suite/{ => docker/ric-robot}/docgen.py (100%) rename ric_robot_suite/{ => docker/ric-robot}/eteshare/config/integration_robot_properties.py (100%) rename ric_robot_suite/{ => docker/ric-robot}/eteshare/config/vm_properties.py (100%) rename ric_robot_suite/{ => docker/ric-robot}/eteshare/health/index.php (100%) rename ric_robot_suite/{ => docker/ric-robot}/eteshare/nodeb/setup/index.php (100%) rename ric_robot_suite/{ => docker/ric-robot}/eteshare/ric/v1/xapps/DemoXapp1/101/index.php (100%) rename ric_robot_suite/{ => docker/ric-robot}/eteshare/ric/v1/xapps/DemoXapp1/index.php (100%) rename ric_robot_suite/{ => docker/ric-robot}/eteshare/ric/v1/xapps/index.php (100%) rename ric_robot_suite/{ => docker/ric-robot}/html/index.html (100%) rename ric_robot_suite/{ => docker/ric-robot}/red.xml (100%) rename ric_robot_suite/{ => docker/ric-robot}/runTags.sh (100%) rename ric_robot_suite/{ => docker/ric-robot}/setup.sh (100%) rename ric_robot_suite/{ => docker/ric-robot}/version.properties (100%) create mode 100644 ric_robot_suite/docker/robot-xapp/Dockerfile create mode 100644 ric_robot_suite/helm/nanobot/charts/.PLACEHOLDER create mode 100644 ric_robot_suite/helm/nanobot/configmap-src/public/testsuites/a1mediator.robot rename ric_robot_suite/helm/{ => ric-robot}/README.md (100%) rename ric_robot_suite/helm/{ => ric-robot}/robot_install.sh (100%) create mode 100644 ric_robot_suite/robot/resources/a1mediator/a1mediator_interface.robot create mode 100644 ric_robot_suite/xapp/README create mode 100644 ric_robot_suite/xapp/config-file.json create mode 100644 ric_robot_suite/xapp/local.rt create mode 100644 ric_robot_suite/xapp/schema.json create mode 100644 ric_robot_suite/xapp/src/go.mod create mode 100755 ric_robot_suite/xapp/src/robot-xapp.go diff --git a/README.md b/README.md index bb51058..3fad272 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,12 @@ -# RIC end-to-end (E2E) deployment and testing: -1. Clone the it_dep and it_test folders on your VM -2. Install kubernetes using the script in it_dep_kubernetes/setup-1node-k8s.sh -3. Install the RIC using the script in it_dep_ricplt/ric_install.sh -4. Copy robot_install.sh from it_test_ric_robot_suite_helm_ into it_dep_ricplt/ -5. cd into it_dep_ricplt and run robot_install.sh. It will create a softlink to the it_test_ric_robot_suite helm director and do the helm install of rig-robot from it_dep_ricplt -6. The command “kubectl -n ricplatform get pods” will show the additional pod for ric-robot. -7. cd into it_test_ric_robot_suite_helm_ric-robot and run ./ete.sh health to verify that the RIC is ready and deployed. +This repository contains tools for end-to-end testing of the RIC. Among the included tools: + +o In simulators/, an implementation of the gNodeB E2 interface to allow +testing without a live gNodeB and a workload generator to exercise the +interfaces between XApps +o In ric_robot_suite/, implementations of the Robot Framework +(robotframework.org) to enable end-to-end test automation for +development and integration testing. + +These components generally assume a deployed and running RIC. + +See the component subdirectories for documentation on the components themselves. diff --git a/ric_robot_suite/README b/ric_robot_suite/README new file mode 100644 index 0000000..832cbe8 --- /dev/null +++ b/ric_robot_suite/README @@ -0,0 +1,23 @@ +This directory contains containers, Helm charts, libraries, and +testsuites to support automated, end-to-end testing of the RIC. +Testing is implemented in the Robot Framework (robotframework.org). + +The simplest way to execute the tests is using the helm chart in +ric_robot_suite/helm/nanobot. See ric_robot_suite/helm/nanobot/README +for instructions on configuring, installing, and extending the chart. +The chart depends on the docker image in docker/nanobot. + +Roadmap: + +test/ +|- ric_robot_suite/ (this directory) + |- docker/ + |- nanobot/ : Dockerfile for the robot framework image + |- robot-xapp/ : Dockerfile for an xapp used by some tests + |- helm/ + |- nanobot/ : Chart for the robot framework + |- ric-python-utils : RIC test-support libraries; these are built into the + docker image. + |- robot/ : Test libraries, testsuites, and support files to be + built into the docker image + |- xapp/ : Go source and deployment files for the test xapp \ No newline at end of file diff --git a/ric_robot_suite/docker/nanobot/Dockerfile b/ric_robot_suite/docker/nanobot/Dockerfile index 062dbe4..dcdfed2 100644 --- a/ric_robot_suite/docker/nanobot/Dockerfile +++ b/ric_robot_suite/docker/nanobot/Dockerfile @@ -47,6 +47,7 @@ COPY ric-python-utils/ricutils/*.py /robot/lib/python/ # files/libraries, so we pick and choose what to copy.. RUN mkdir /robot/resources COPY robot/resources/json_templater.robot /robot/resources +COPY robot/resources/a1mediator /robot/resources/a1mediator COPY robot/resources/appmgr /robot/resources/appmgr COPY robot/resources/dashboard /robot/resources/dashboard COPY robot/resources/e2mgr /robot/resources/e2mgr diff --git a/ric_robot_suite/README-nanobot b/ric_robot_suite/docker/nanobot/README similarity index 85% rename from ric_robot_suite/README-nanobot rename to ric_robot_suite/docker/nanobot/README index 526d2b8..1c8811e 100644 --- a/ric_robot_suite/README-nanobot +++ b/ric_robot_suite/docker/nanobot/README @@ -13,12 +13,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -In addition to the default robot framework container, this tree -includes a minimal robot framework container called "nanobot" and -intented for automated testing or resource-limited environments while -supporting all testsuites implemented by the larger container. The -nanobot container lacks the xvfb-based browser and lighttpd/PHP -dashboard. +This dockerfile implements a minimal robot framework container called +"nanobot", intended for automated testing or resource-limited +environments. To build the container, first check the following ENV values at the top of docker/nanobot/Dockerfile to ensure they reflect the RIC @@ -73,5 +70,6 @@ Output: /output.xml Log: /log.html Report: /report.html -A Helm chart for the container can be found in helm/nanobot. See -helm/nanobot/README for more information on the chart. \ No newline at end of file +A Helm chart for the container can be found in +ric_robot_suite/helm/nanobot. See ric_robot_suite/helm/nanobot/README +for more information on the chart. \ No newline at end of file diff --git a/ric_robot_suite/docker/ric-robot/Dockerfile b/ric_robot_suite/docker/ric-robot/Dockerfile index e44a47f..ea1b13e 100644 --- a/ric_robot_suite/docker/ric-robot/Dockerfile +++ b/ric_robot_suite/docker/ric-robot/Dockerfile @@ -67,12 +67,12 @@ RUN pip install robotframework==3.0.4 \ # Copy the robot code COPY robot /var/opt/RIC/robot -COPY red.xml /var/opt/RIC +COPY docker/ric-robot/red.xml /var/opt/RIC COPY ric-python-utils /var/opt/RIC/ric-python-utils -COPY runTags.sh /var/opt/RIC -COPY setup.sh /var/opt/RIC -COPY version.properties /var/opt/RIC -COPY README.md /var/opt/RIC +COPY docker/ric-robot/runTags.sh /var/opt/RIC +COPY docker/ric-robot/setup.sh /var/opt/RIC +COPY docker/ric-robot/version.properties /var/opt/RIC +COPY docker/ric-robot/README.md /var/opt/RIC COPY docker/ric-robot/lighttpd.conf /etc/lighttpd/lighttpd.conf COPY docker/ric-robot/authorization /etc/lighttpd/authorization diff --git a/ric_robot_suite/README.md b/ric_robot_suite/docker/ric-robot/README.md similarity index 100% rename from ric_robot_suite/README.md rename to ric_robot_suite/docker/ric-robot/README.md diff --git a/ric_robot_suite/build.sh b/ric_robot_suite/docker/ric-robot/build.sh similarity index 100% rename from ric_robot_suite/build.sh rename to ric_robot_suite/docker/ric-robot/build.sh diff --git a/ric_robot_suite/docgen.py b/ric_robot_suite/docker/ric-robot/docgen.py similarity index 100% rename from ric_robot_suite/docgen.py rename to ric_robot_suite/docker/ric-robot/docgen.py diff --git a/ric_robot_suite/eteshare/config/integration_robot_properties.py b/ric_robot_suite/docker/ric-robot/eteshare/config/integration_robot_properties.py similarity index 100% rename from ric_robot_suite/eteshare/config/integration_robot_properties.py rename to ric_robot_suite/docker/ric-robot/eteshare/config/integration_robot_properties.py diff --git a/ric_robot_suite/eteshare/config/vm_properties.py b/ric_robot_suite/docker/ric-robot/eteshare/config/vm_properties.py similarity index 100% rename from ric_robot_suite/eteshare/config/vm_properties.py rename to ric_robot_suite/docker/ric-robot/eteshare/config/vm_properties.py diff --git a/ric_robot_suite/eteshare/health/index.php b/ric_robot_suite/docker/ric-robot/eteshare/health/index.php similarity index 100% rename from ric_robot_suite/eteshare/health/index.php rename to ric_robot_suite/docker/ric-robot/eteshare/health/index.php diff --git a/ric_robot_suite/eteshare/nodeb/setup/index.php b/ric_robot_suite/docker/ric-robot/eteshare/nodeb/setup/index.php similarity index 100% rename from ric_robot_suite/eteshare/nodeb/setup/index.php rename to ric_robot_suite/docker/ric-robot/eteshare/nodeb/setup/index.php diff --git a/ric_robot_suite/eteshare/ric/v1/xapps/DemoXapp1/101/index.php b/ric_robot_suite/docker/ric-robot/eteshare/ric/v1/xapps/DemoXapp1/101/index.php similarity index 100% rename from ric_robot_suite/eteshare/ric/v1/xapps/DemoXapp1/101/index.php rename to ric_robot_suite/docker/ric-robot/eteshare/ric/v1/xapps/DemoXapp1/101/index.php diff --git a/ric_robot_suite/eteshare/ric/v1/xapps/DemoXapp1/index.php b/ric_robot_suite/docker/ric-robot/eteshare/ric/v1/xapps/DemoXapp1/index.php similarity index 100% rename from ric_robot_suite/eteshare/ric/v1/xapps/DemoXapp1/index.php rename to ric_robot_suite/docker/ric-robot/eteshare/ric/v1/xapps/DemoXapp1/index.php diff --git a/ric_robot_suite/eteshare/ric/v1/xapps/index.php b/ric_robot_suite/docker/ric-robot/eteshare/ric/v1/xapps/index.php similarity index 100% rename from ric_robot_suite/eteshare/ric/v1/xapps/index.php rename to ric_robot_suite/docker/ric-robot/eteshare/ric/v1/xapps/index.php diff --git a/ric_robot_suite/html/index.html b/ric_robot_suite/docker/ric-robot/html/index.html similarity index 100% rename from ric_robot_suite/html/index.html rename to ric_robot_suite/docker/ric-robot/html/index.html diff --git a/ric_robot_suite/red.xml b/ric_robot_suite/docker/ric-robot/red.xml similarity index 100% rename from ric_robot_suite/red.xml rename to ric_robot_suite/docker/ric-robot/red.xml diff --git a/ric_robot_suite/runTags.sh b/ric_robot_suite/docker/ric-robot/runTags.sh similarity index 100% rename from ric_robot_suite/runTags.sh rename to ric_robot_suite/docker/ric-robot/runTags.sh diff --git a/ric_robot_suite/setup.sh b/ric_robot_suite/docker/ric-robot/setup.sh similarity index 100% rename from ric_robot_suite/setup.sh rename to ric_robot_suite/docker/ric-robot/setup.sh diff --git a/ric_robot_suite/version.properties b/ric_robot_suite/docker/ric-robot/version.properties similarity index 100% rename from ric_robot_suite/version.properties rename to ric_robot_suite/docker/ric-robot/version.properties diff --git a/ric_robot_suite/docker/robot-xapp/Dockerfile b/ric_robot_suite/docker/robot-xapp/Dockerfile new file mode 100644 index 0000000..03607a1 --- /dev/null +++ b/ric_robot_suite/docker/robot-xapp/Dockerfile @@ -0,0 +1,52 @@ +# ================================================================================== +# 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. +# ================================================================================== + +FROM alpine AS xapp-alpine-go-base + +RUN apk update && apk add build-base autoconf automake libtool cmake ninja pkgconfig git go + +WORKDIR /tmp +RUN git clone https://gerrit.o-ran-sc.org/r/ric-plt/lib/rmr +RUN mkdir /tmp/rmr/build +WORKDIR /tmp/rmr/build +RUN cmake .. -DPACK_EXTERNALS=1 -DDEV_PKG=1 +RUN make install + +RUN mkdir /tmp/robot-xapp +COPY xapp/src /tmp/robot-xapp +WORKDIR /tmp/robot-xapp +ENV CGO_CFLAGS "-I /usr/local/include -L/usr/local/lib" +RUN go build robot-xapp.go + +# +FROM alpine + +COPY --from=xapp-alpine-go-base /usr/local/lib64/libnng.so /usr/lib/libnng.so.1 +COPY --from=xapp-alpine-go-base /tmp/robot-xapp/robot-xapp /robot-xapp + +COPY xapp/local.rt /local.rt +COPY xapp/config-file.json /config-file.json +COPY xapp/schema.json /schema.json + +ENV DBAAS_SERVICE_HOST service-ricplt-dbaas-tcp.ricplt.svc.cluster.local +ENV DBAAS_SERVICE_PORT 6379 +ENV DBAAS_PORT_6379_TCP_ADDR service-ricplt-dbaas-tcp.ricplt.svc.cluster.local +ENV DBAAS_PORT_6379_TCP_PORT 6379 + +ENV RMR_SEED_RT /local.rt + +WORKDIR / +CMD [ "/robot-xapp", "-f", "/config-file.json" ] diff --git a/ric_robot_suite/helm/nanobot/README b/ric_robot_suite/helm/nanobot/README index 5813187..ce2b1e4 100644 --- a/ric_robot_suite/helm/nanobot/README +++ b/ric_robot_suite/helm/nanobot/README @@ -1,5 +1,4 @@ # 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. @@ -16,6 +15,31 @@ This directory contains a Helm chart for the minimal robot container and Kubernetes job. +USING THE CHART +To install the chart (which creates and runs a single Kubernetes job), +first copy the packaged Common-Template chart (from +https://gerrit.o-ran-sc.org/r/it/dep.git) into +ric_robot_suite/helm/nanobot/charts. Then, edit +ric_robot_suite/helm/nanobot/values.yaml to reflect your +environment and desired tests and run + +$ helm install [--values ] --namespace nanobot + +... in the ric_robot_suite/helm directory, where +is the recipe override file used when deploying the RIC, if any. The +output from the test will be in the job's log: + +kubectl logs --namespace job/nanobot-r0-ric-robot-run + +(the job name may be different if you have altered the values.yaml +file; use 'kubectl --namespace get jobs' to find it). + +The namespace selected will be where the job's service account is +created and the job runs. It may but need not be the same as the RIC +platform or xapp namespace —- the chart may be deployed in any +namespace. + +CUSTOMIZING AND EXTENDING THE CHART The job mounts its testsuites directory as a configmap, built from files in nanobot/configmap-src/testsuites. Template expansion is performed on files in that directory. @@ -46,18 +70,7 @@ Because both resources/ and properties/ are mounted at robot/resources, care should be taken to ensure that names do not collide across those directories, particularly between trees. - -To install the chart (which creates and runs a single Kubernetes job), -edit values.yaml to reflect your environment and desired tests and run - -$ helm install [--values ] --namespace nanobot - -... where is the recipe override file used when -deploying the RIC, if any. - -The namespace selected will be where the job's service account is -created and the job runs. It may but need not be the same as the RIC -platform or xapp namespace —- the chart may be deployed in any -namespace. - - +The job uses the nanobot docker image built by +ric_robot_suite/docker/nanobot/Dockerfile; see +ric_robot_suite/docker/nanobot/README for information about the +image. \ No newline at end of file diff --git a/ric_robot_suite/helm/nanobot/charts/.PLACEHOLDER b/ric_robot_suite/helm/nanobot/charts/.PLACEHOLDER new file mode 100644 index 0000000..e69de29 diff --git a/ric_robot_suite/helm/nanobot/configmap-src/public/properties/global_properties.robot b/ric_robot_suite/helm/nanobot/configmap-src/public/properties/global_properties.robot index 3c15f62..83e96bf 100644 --- a/ric_robot_suite/helm/nanobot/configmap-src/public/properties/global_properties.robot +++ b/ric_robot_suite/helm/nanobot/configmap-src/public/properties/global_properties.robot @@ -17,6 +17,7 @@ {{- $xappNS := include "common.namespace.xapp" . }} {{- $ricplt := printf "%s.svc.%s" $ricpltNS $domain }} {{- $release := default "r1" .Values.ric.robot.release }} +{{- $testxapp := default "robot-xapp" .Values.ric.robot.environment.xapp }} # *** Settings *** Documentation store all properties that can change or are used in multiple places here @@ -61,6 +62,14 @@ ${GLOBAL_INJECTED_RTMGR_USER} {{ .Values.ric.platform.components.rtmgr.u ${GLOBAL_INJECTED_RTMGR_PASSWORD} {{ .Values.ric.platform.components.rtmgr.password }} {{- end }} # +{{- if .Values.ric.platform.components.a1mediator }} +${GLOBAL_A1MEDIATOR_SERVER_PROTOCOL} {{ default "http" .Values.ric.platform.components.a1mediator.protocol }} +${GLOBAL_INJECTED_A1MEDIATOR_IP_ADDR} {{ printf "%s.%s" (include "common.servicename.a1mediator.http" .) $ricplt }} +${GLOBAL_A1MEDIATOR_SERVER_PORT} {{ include "common.serviceport.a1mediator.http" . }} +${GLOBAL_A1MEDIATOR_POLICY_ID} {{ default "6266268" .Values.ric.platform.components.a1mediator.policyID }} +${GLOBAL_A1MEDIATOR_TARGET_XAPP} {{ default $testxapp .Values.ric.platform.components.a1mediator.xappName }} +{{- end }} +# ${GLOBAL_INJECTED_DBAAS_IP_ADDR} {{ printf "%s.%s" (include "common.servicename.dbaas.tcp" .) $ricplt }} ${GLOBAL_DBAAS_SERVER_PORT} {{ include "common.serviceport.dbaas.tcp" . }} # diff --git a/ric_robot_suite/helm/nanobot/configmap-src/public/testsuites/a1mediator.robot b/ric_robot_suite/helm/nanobot/configmap-src/public/testsuites/a1mediator.robot new file mode 100644 index 0000000..69c2eeb --- /dev/null +++ b/ric_robot_suite/helm/nanobot/configmap-src/public/testsuites/a1mediator.robot @@ -0,0 +1,129 @@ +# Copyright (c) 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. + +*** Settings *** +Documentation Tests for the A1 Mediator + +Resource /robot/resources/global_properties.robot +Resource /robot/resources/a1mediator/a1mediator_interface.robot +Resource /robot/resources/appmgr/appmgr_interface.robot + +Library UUID + +*** Variables *** +${POLICY_ID} ${GLOBAL_A1MEDIATOR_POLICY_ID} +${TARGET_XAPP} ${GLOBAL_A1MEDIATOR_TARGET_XAPP} + +*** Test Cases *** +Deploy Target XApp If Necessary + [Tags] etetests ci_tests a1tests intrusive + ${err} ${xappStatus} = Run Keyword And Ignore Error + ... Get XApp By Name ${TARGET_XAPP} + Run Keyword If '${err}' == 'FAIL' + ... Deploy XApp ${TARGET_XAPP} + +Create Policy + [Tags] etetests ci_tests a1tests intrusive + ${policyProperty} = Create Dictionary + ... type=string + ... description=a message + ${policyProperties} = Create Dictionary + ... message=${policyProperty} + ${policyName} = Generate UUID + ${policyName} = Convert To String ${policyName} + Set Suite Variable ${policyName} + ${resp} = Create A1 Policy Type + ... ${POLICY_ID} + ... ${policyName} + ... ${policyName} + ... ${policyProperties} + Should Be True ${resp} + +Policy Should Exist + [Tags] etetests ci_tests a1tests + ${resp} = Retrieve A1 Policy ${POLICY_ID} + Should Be True ${resp} + Should Be Equal ${resp.json()}[name] ${policyName} + +Create Policy Instance + [Tags] etetests ci_tests a1tests intrusive + ${instanceName} = Generate UUID + ${instanceName} = Convert To String ${instanceName} + Set Suite Variable ${instanceName} + ${instanceMessage} = Generate UUID + ${instanceMessage} = Convert To String ${instanceMessage} + ${instanceProperties} = Create Dictionary + ... message=${instanceMessage} + Set Suite Variable ${instanceMessage} + ${resp} = Instantiate A1 Policy + ... ${POLICY_ID} + ... ${instanceName} + ... ${instanceProperties} + Should Be True ${resp} + +Policy Should Have Instances + [Tags] etetests ci_tests a1tests + ${resp} = Retrieve A1 Instance ${POLICY_ID} + Should Be True ${resp} + Should Not Be Empty ${resp.json()} + +Instance Should Exist + [Tags] etetests ci_tests a1tests + ${resp} = Retrieve A1 Instance ${POLICY_ID} ${instanceName} + Should Be True ${resp} + Should Be Equal ${resp.json()}[message] ${instanceMessage} + +Instance Should Be IN EFFECT + [Tags] etetests ci_tests a1tests + Wait Until Keyword Succeeds 3x 5s Status Is IN EFFECT + +Delete Policy Instance + [Tags] etetests ci_tests a1tests intrusive + ${resp} = Delete A1 Instance ${POLICY_ID} ${instanceName} + Should Be True ${resp} + +Instance Should Not Exist + [Tags] etetests ci_tests a1tests + Wait Until Keyword Succeeds 3x 5s Instance Has Been Deleted + +Delete Policy + [Tags] etetests ci_tests a1tests intrusive + ${resp} = Delete A1 Policy ${POLICY_ID} + Should Be True ${resp} + +Policy Should Not Exist + [Tags] etetests ci_tests a1tests + Wait Until Keyword Succeeds 3x 5s Policy Has Been Deleted + +Undeploy Target XApp + [Tags] etetests ci_tests a1tests intrusive + Undeploy XApp ${TARGET_XAPP} + +*** Keywords *** +Status Is IN EFFECT + ${resp} = Retrieve A1 Instance Status + ... ${POLICY_ID} + ... ${instanceName} + Should Be True ${resp} + Should Be Equal ${resp.json()}[instance_status] IN EFFECT + +Instance Has Been Deleted + ${resp} = Retrieve A1 Instance + ... ${POLICY_ID} + ... ${instanceName} + Should Be Equal As Strings ${resp.status_code} 404 + +Policy Has Been Deleted + ${resp} = Retrieve A1 Policy ${POLICY_ID} + Should Be Equal As Strings ${resp.status_code} 404 diff --git a/ric_robot_suite/helm/nanobot/values.yaml b/ric_robot_suite/helm/nanobot/values.yaml index 353f069..78f622e 100644 --- a/ric_robot_suite/helm/nanobot/values.yaml +++ b/ric_robot_suite/helm/nanobot/values.yaml @@ -55,6 +55,8 @@ ric: releaseName: r0 components: a1mediator: + xappName: robot-xapp + policyID: "6266268" appmgr: user: test password: test @@ -131,7 +133,7 @@ ric: # This should be an xapp which is "onboarded" to # the appmgr but not deployed at the time the test # is initiated. - xapp: xapp-std + xapp: robot-xapp gNodeB: # the details of a test gNodeB, for [E/X]2 tests. # This can be either a real gnodeb or a simulator, diff --git a/ric_robot_suite/helm/README.md b/ric_robot_suite/helm/ric-robot/README.md similarity index 100% rename from ric_robot_suite/helm/README.md rename to ric_robot_suite/helm/ric-robot/README.md diff --git a/ric_robot_suite/helm/robot_install.sh b/ric_robot_suite/helm/ric-robot/robot_install.sh similarity index 100% rename from ric_robot_suite/helm/robot_install.sh rename to ric_robot_suite/helm/ric-robot/robot_install.sh diff --git a/ric_robot_suite/robot/resources/a1mediator/a1mediator_interface.robot b/ric_robot_suite/robot/resources/a1mediator/a1mediator_interface.robot new file mode 100644 index 0000000..059596f --- /dev/null +++ b/ric_robot_suite/robot/resources/a1mediator/a1mediator_interface.robot @@ -0,0 +1,118 @@ +*** Settings *** +Documentation Keywords for interacting with the A1 interface, including policy creation, instantiaton, and deletion + +Library RequestsLibrary + +Resource /robot/resources/global_properties.robot + +*** Variables *** +${A1MEDIATOR_BASE_PATH} /a1-p/policytypes +${A1MEDIATOR_ENDPOINT} ${GLOBAL_A1MEDIATOR_SERVER_PROTOCOL}://${GLOBAL_INJECTED_A1MEDIATOR_IP_ADDR}:${GLOBAL_A1MEDIATOR_SERVER_PORT} + +*** Keywords *** +Create A1 Policy Type + [Documentation] Create a new policy via the A1 Mediator. + [Arguments] ${type} ${name} ${description} ${properties} ${required}=@{EMPTY} + ${typeID} = Convert To Integer ${type} + Should Be True ${type} > 0 Policy type must be an integer > 0 + ${createSchema} = Create Dictionary + ... $schema=http://json-schema.org/draft-07/schema# + ... type=object + ... properties=${properties} + ... required=@{required} + ${createBody} = Create Dictionary + ... name=${name} + ... policy_type_id=${typeID} + ... description=${description} + ... create_schema=${createSchema} + ${createJSON} = Evaluate json.dumps(&{createBody}) json,uuid + ${resp} = Run A1Mediator PUT Request /${type} body=${createJSON} + [Return] ${resp} + +Instantiate A1 Policy + [Documentation] Create a new instance of an A1 policy + [Arguments] ${type} ${instance} ${properties}=${EMPTY} + ${typeID} = Convert To Integer ${type} + Should Be True ${type} > 0 Policy type must be an integer > 0 + ${instanceJSON} = Evaluate json.dumps(&{properties}) json,uuid + ${resp} = Run A1Mediator PUT Request /${type}/policies/${instance} body=${instanceJSON} + [Return] ${resp} + +Delete A1 Instance + [Documentation] Remove an A1 policy instance + [Arguments] ${type} ${instance} + ${typeID} = Convert To Integer ${type} + Should Be True ${type} > 0 Policy type must be an integer > 0 + ${resp} = Run A1Mediator DELETE Request /${type}/policies/${instance} + [Return] ${resp} + +Delete A1 Policy + [Documentation] Remove an A1 policy type + [Arguments] ${type} + ${typeID} = Convert To Integer ${type} + Should Be True ${type} > 0 Policy type must be an integer > 0 + ${resp} = Run A1Mediator DELETE Request /${type} + [Return] ${resp} + +Retrieve A1 Policy + [Documentation] Get a defined policy from A1 + [Arguments] ${type} + ${typeID} = Convert To Integer ${type} + Should Be True ${type} > 0 Policy type must be an integer > 0 + ${resp} = Run A1Mediator GET Request /${type} + [Return] ${resp} + +Retrieve A1 Instance + [Documentation] Get a defined policy from A1. If no instance is specified, retrieve all instances. + [Arguments] ${type} ${instance}=${EMPTY} + ${typeID} = Convert To Integer ${type} + Should Be True ${type} > 0 Policy type must be an integer > 0 + ${resp} = Run Keyword If "${instance}" != "${EMPTY}" + ... Run A1Mediator GET Request /${type}/policies/${instance} + ... ELSE + ... Run A1Mediator GET Request /${type}/policies + [Return] ${resp} + +Retrieve A1 Instance Status + [Documentation] Get policy instance status + [Arguments] ${type} ${instance}=${EMPTY} + ${typeID} = Convert To Integer ${type} + Should Be True ${type} > 0 Policy type must be an integer > 0 + ${resp} = Run A1Mediator GET Request /${type}/policies/${instance}/status + [Return] ${resp} + +Run A1mediator GET Request + [Documentation] Make an HTTP GET request against the XApp manager + [Arguments] ${path}=${EMPTY} + ${session} = Create Session roboA1mediatorGet ${A1MEDIATOR_ENDPOINT} + ${headers} = Create Dictionary Accept=application/json Content-Type=application/json + ${resp} = Get Request roboA1mediatorGet ${A1MEDIATOR_BASE_PATH}${path} headers=${headers} + [Return] ${resp} + +Run A1mediator PUT Request + [Documentation] Make an HTTP PUT request against the XApp manager + [Arguments] ${path}=${EMPTY} ${body}=${EMPTY} + ${session} = Create Session roboA1mediatorPut ${A1MEDIATOR_ENDPOINT} + ${headers} = Create Dictionary Accept=application/json Content-Type=application/json + ${resp} = PUT Request roboA1mediatorPut ${A1MEDIATOR_BASE_PATH}${path} + ... headers=${headers} + ... data=${body} + [Return] ${resp} + +Run A1mediator POST Request + [Documentation] Make an HTTP POST request against the XApp manager + [Arguments] ${path}=${EMPTY} ${body}=${EMPTY} + ${session} = Create Session roboA1mediatorPost ${A1MEDIATOR_ENDPOINT} + ${headers} = Create Dictionary Accept=application/json Content-Type=application/json + ${resp} = POST Request roboA1mediatorPost ${A1MEDIATOR_BASE_PATH}${path} + ... headers=${headers} + ... data=${body} + [Return] ${resp} + +Run A1mediator DELETE Request + [Documentation] Make an HTTP DELETE request against the XApp manager + [Arguments] ${path} + ${session} = Create Session roboA1mediatorDelete ${A1MEDIATOR_ENDPOINT} + ${headers} = Create Dictionary Accept=application/json Content-Type=application/json + ${resp} = Delete Request roboA1mediatorDelete ${A1MEDIATOR_BASE_PATH}${path} headers=${headers} + [Return] ${resp} diff --git a/ric_robot_suite/xapp/README b/ric_robot_suite/xapp/README new file mode 100644 index 0000000..6130572 --- /dev/null +++ b/ric_robot_suite/xapp/README @@ -0,0 +1,13 @@ +This directory contains an XApp used to support RIC testing; its +primary purpose is things like RMR message handling which aren't +easily managed in Robot itself, and to provide a way to test XApp +deployment and point-to-point message flow without depending on the +functionality of any particular XApp. + +To build the XApp container, do + +$ doocker build -f docker/robot-xapp/Dockerfile . + +from the ric_robot_suite directory. The XApp can be deployed via the +RIC Application Manager using the schema.json and config.json files in +this directory. diff --git a/ric_robot_suite/xapp/config-file.json b/ric_robot_suite/xapp/config-file.json new file mode 100644 index 0000000..26aae59 --- /dev/null +++ b/ric_robot_suite/xapp/config-file.json @@ -0,0 +1,80 @@ +{ + "local": { "host": ":8080" }, + "logger": { "level": 5 }, + "db": { + "host": "service-ricplt-dbaas-tcp.ricplt.svc.cluster.local", + "port": 6379, + "namespaces": ["robotXApp", "rnib"] + + }, + "rmr": { + "protPort": "tcp:4560", + "maxSize": 10000, + "numWorkers": 1, + "rxMessages": [ "A1_POLICY_REQ" ], + "txMessages": [ "A1_POLICY_RESP", "A1_POLICY_QUERY" ], + "policies": [ 3608368 ] + }, + "metrics": [ + { "objectName": "RobotXAppCounters", + "objectInstance": "PolicyCreates", + "name": "PolicyCreates", + "description": "A1 policies created", + "type": "counter" }, + { "objectName": "RobotXAppCounters", + "objectInstance": "DuplicatePolicyCreates", + "name": "DuplicatePolicyCreates", + "description": "A1 CREATE requests received for existing policy instances", + "type": "counter" }, + { "objectName": "RobotXAppCounters", + "objectInstance": "PolicyUpdates", + "name": "PolicyUpdates", + "description": "A1 policies updateded", + "type": "counter" }, + { "objectName": "RobotXAppCounters", + "objectInstance": "NonexistentPolicyUpdates", + "name": "NonexistentPolicyUpdates", + "description": "A1 UPDATE requests received for nonexistent policy instances", + "type": "counter" }, + { "objectName": "RobotXAppCounters", + "objectInstance": "PolicyDeletes", + "name": "PolicyDeletes", + "description": "A1 policies deleted", + "type": "counter" }, + { "objectName": "RobotXAppCounters", + "objectInstance": "NonexistentPolicyDeletes", + "name": "NonexistentPolicyDeletes", + "description": "A1 DELETE requests received for nonexistent policy instances", + "type": "counter" }, + { "objectName": "RobotXAppCounters", + "objectInstance": "dbStores", + "name": "dbStores", + "description": "SDL store requests", + "type": "counter" }, + { "objectName": "RobotXAppCounters", + "objectInstance": "dbStoreFailures", + "name": "dbStoreFailures", + "description": "SDL store request failures", + "type": "counter" }, + { "objectName": "RobotXAppCounters", + "objectInstance": "dbDeletes", + "name": "dbDeletes", + "description": "SDL delete requests", + "type": "counter" }, + { "objectName": "RobotXAppCounters", + "objectInstance": "dbDeleteFailures", + "name": "dbDeleteFailures", + "description": "SDL delete request failures", + "type": "counter" }, + { "objectName": "RobotXAppCounters", + "objectInstance": "MessagesReceived", + "name": "MessagesReceived", + "description": "Total RMR messages received", + "type": "counter" }, + { "objectName": "RobotXAppCounters", + "objectInstance": "MessagesSent", + "name": "MessagesSent", + "description": "Total RMR messages sent", + "type": "counter" } + ] +} diff --git a/ric_robot_suite/xapp/local.rt b/ric_robot_suite/xapp/local.rt new file mode 100644 index 0000000..8833ba6 --- /dev/null +++ b/ric_robot_suite/xapp/local.rt @@ -0,0 +1,5 @@ +newrt|start +rte|20001|service-ricplt-a1mediator-rmr.ricplt:4561;service-ricplt-a1mediator-rmr.ricplt:4562 +mse|20011|3608368|service-ricplt-a1mediator-rmr.ricplt:4561;service-ricplt-a1mediator-rmr.ricplt:4562 +mse|20011|6266268|service-ricplt-a1mediator-rmr.ricplt:4561;service-ricplt-a1mediator-rmr.ricplt:4562 +newrt|end diff --git a/ric_robot_suite/xapp/schema.json b/ric_robot_suite/xapp/schema.json new file mode 100644 index 0000000..ebfad88 --- /dev/null +++ b/ric_robot_suite/xapp/schema.json @@ -0,0 +1,357 @@ +{ + "definitions": {}, + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://example.com/root.json", + "type": "object", + "title": "The Root Schema", + "required": [ + "local", + "logger", + "rmr", + "db", + "controls", + "metrics" + ], + "properties": { + "local": { + "$id": "#/properties/local", + "type": "object", + "title": "The Local Schema", + "required": [ + "host" + ], + "properties": { + "host": { + "$id": "#/properties/local/properties/host", + "type": "string", + "title": "The Host Schema", + "default": "", + "examples": [ + ":8080" + ], + "pattern": "^(.*)$" + } + } + }, + "logger": { + "$id": "#/properties/logger", + "type": "object", + "title": "The Logger Schema", + "required": [ + "level" + ], + "properties": { + "level": { + "$id": "#/properties/logger/properties/level", + "type": "integer", + "title": "The Level Schema", + "default": 0, + "examples": [ + 3 + ] + } + } + }, + "rmr": { + "$id": "#/properties/rmr", + "type": "object", + "title": "The Rmr Schema", + "required": [ + "protPort", + "maxSize", + "numWorkers", + "rxMessages", + "txMessages" + ], + "properties": { + "protPort": { + "$id": "#/properties/rmr/properties/protPort", + "type": "string", + "title": "The Protport Schema", + "default": "", + "examples": [ + "tcp:4560" + ], + "pattern": "^(.*)$" + }, + "maxSize": { + "$id": "#/properties/rmr/properties/maxSize", + "type": "integer", + "title": "The Maxsize Schema", + "default": 0, + "examples": [ + 2072 + ] + }, + "numWorkers": { + "$id": "#/properties/rmr/properties/numWorkers", + "type": "integer", + "title": "The Numworkers Schema", + "default": 0, + "examples": [ + 1 + ] + }, + "rxMessages": { + "$id": "#/properties/rmr/properties/rxMessages", + "type": "array", + "title": "The Rxmessages Schema", + "items": { + "$id": "#/properties/rmr/properties/rxMessages/items", + "type": "string", + "title": "The Items Schema", + "default": "", + "examples": [ + "RIC_SUB_RESP", + "RIC_SUB_FAILURE", + "RIC_SUB_DEL_RESP", + "RIC_SUB_DEL_FAILURE", + "RIC_INDICATION" + ], + "pattern": "^(.*)$" + } + }, + "txMessages": { + "$id": "#/properties/rmr/properties/txMessages", + "type": "array", + "title": "The Txmessages Schema", + "items": { + "$id": "#/properties/rmr/properties/txMessages/items", + "type": "string", + "title": "The Items Schema", + "default": "", + "examples": [ + "RIC_SUB_REQ", + "RIC_SUB_DEL_REQ", + "RIC_SGNB_ADDITION_REQ", + "RIC_SGNB_ADDITION_ACK", + "RIC_SGNB_ADDITION_REJECT", + "RIC_SGNB_MOD_REQUEST", + "RIC_SGNB_MOD_REQUEST_ACK", + "RIC_SGNB_MOD_REQUEST_REJECT", + "RIC_SGNB_MOD_REQUIRED", + "RIC_SGNB_MOD_CONFIRM", + "RIC_SGNB_MOD_REFUSE", + "RIC_SGNB_RECONF_COMPLETE", + "RIC_SGNB_RELEASE_REQUEST", + "RIC_SGNB_RELEASE_CONFIRM", + "RIC_SGNB_RELEASE_REQUIRED", + "RIC_SGNB_RELEASE_REQUEST_ACK", + "RIC_SECONDARY_RAT_DATA_USAGE_REPORT", + "RIC_SN_STATUS_TRANSFER", + "RIC_RRC_TRANSFER", + "RIC_UE_CONTEXT_RELEASE" + ], + "pattern": "^(.*)$" + } + } + } + }, + "db": { + "$id": "#/properties/db", + "type": "object", + "title": "The Db Schema", + "required": [ + "host", + "port", + "namespaces" + ], + "properties": { + "host": { + "$id": "#/properties/db/properties/host", + "type": "string", + "title": "The Host Schema", + "default": "", + "examples": [ + "localhost" + ], + "pattern": "^(.*)$" + }, + "port": { + "$id": "#/properties/db/properties/port", + "type": "integer", + "title": "The Port Schema", + "default": 0, + "examples": [ + 6379 + ] + }, + "namespaces": { + "$id": "#/properties/db/properties/namespaces", + "type": "array", + "title": "The Namespaces Schema", + "items": { + "$id": "#/properties/db/properties/namespaces/items", + "type": "string", + "title": "The Items Schema", + "default": "", + "examples": [ + "sdl", + "rnib" + ], + "pattern": "^(.*)$" + } + } + } + }, + "controls": { + "$id": "#/properties/controls", + "type": "object", + "title": "The Controls Schema", + "required": [ + "active", + "requestorId", + "ranFunctionId", + "ricActionId", + "interfaceId" + ], + "properties": { + "active": { + "$id": "#/properties/controls/properties/active", + "type": "boolean", + "title": "The Active Schema", + "default": false, + "examples": [ + true + ] + }, + "requestorId": { + "$id": "#/properties/controls/properties/requestorId", + "type": "integer", + "title": "The Requestorid Schema", + "default": 0, + "examples": [ + 66 + ] + }, + "ranFunctionId": { + "$id": "#/properties/controls/properties/ranFunctionId", + "type": "integer", + "title": "The Ranfunctionid Schema", + "default": 0, + "examples": [ + 1 + ] + }, + "ricActionId": { + "$id": "#/properties/controls/properties/ricActionId", + "type": "integer", + "title": "The Ricactionid Schema", + "default": 0, + "examples": [ + 0 + ] + }, + "interfaceId": { + "$id": "#/properties/controls/properties/interfaceId", + "type": "object", + "title": "The Interfaceid Schema", + "required": [ + "globalENBId" + ], + "properties": { + "globalENBId": { + "$id": "#/properties/controls/properties/interfaceId/properties/globalENBId", + "type": "object", + "title": "The Globalenbid Schema", + "required": [ + "plmnId", + "eNBId" + ], + "properties": { + "plmnId": { + "$id": "#/properties/controls/properties/interfaceId/properties/globalENBId/properties/plmnId", + "type": "string", + "title": "The Plmnid Schema", + "default": "", + "examples": [ + "310150" + ], + "pattern": "^(.*)$" + }, + "eNBId": { + "$id": "#/properties/controls/properties/interfaceId/properties/globalENBId/properties/eNBId", + "type": "integer", + "title": "The Enbid Schema", + "default": 0, + "examples": [ + 202251 + ] + } + } + } + } + } + } + }, + "metrics": { + "$id": "#/properties/metrics", + "type": "array", + "title": "The Metrics Schema", + "items": { + "$id": "#/properties/metrics/items", + "type": "object", + "title": "The Items Schema", + "required": [ + "objectName", + "objectInstance", + "name", + "type", + "description" + ], + "properties": { + "objectName": { + "$id": "#/properties/metrics/items/properties/objectName", + "type": "string", + "title": "The Objectname Schema", + "default": "", + "examples": [ + "UEEventStreamingCounters" + ], + "pattern": "^(.*)$" + }, + "objectInstance": { + "$id": "#/properties/metrics/items/properties/objectInstance", + "type": "string", + "title": "The Objectinstance Schema", + "default": "", + "examples": [ + "SgNBAdditionRequest" + ], + "pattern": "^(.*)$" + }, + "name": { + "$id": "#/properties/metrics/items/properties/name", + "type": "string", + "title": "The Name Schema", + "default": "", + "examples": [ + "SgNBAdditionRequest" + ], + "pattern": "^(.*)$" + }, + "type": { + "$id": "#/properties/metrics/items/properties/type", + "type": "string", + "title": "The Type Schema", + "default": "", + "examples": [ + "counter" + ], + "pattern": "^(.*)$" + }, + "description": { + "$id": "#/properties/metrics/items/properties/description", + "type": "string", + "title": "The Description Schema", + "default": "", + "examples": [ + "The total number of SG addition request events processed" + ], + "pattern": "^(.*)$" + } + } + } + } + } +} \ No newline at end of file diff --git a/ric_robot_suite/xapp/src/go.mod b/ric_robot_suite/xapp/src/go.mod new file mode 100644 index 0000000..cb23942 --- /dev/null +++ b/ric_robot_suite/xapp/src/go.mod @@ -0,0 +1,11 @@ +go 1.12 + +module gerrit.o-ran-sc.org/r/it/test/ric_robot_suite/xapp + +require ( + gerrit.o-ran-sc.org/r/ric-plt/xapp-frame v0.0.20 +) + +replace gerrit.o-ran-sc.org/r/ric-plt/xapp-frame => gerrit.o-ran-sc.org/r/ric-plt/xapp-frame.git v0.0.21 +replace gerrit.o-ran-sc.org/r/ric-plt/sdlgo => gerrit.o-ran-sc.org/r/ric-plt/sdlgo.git v0.5.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/ric_robot_suite/xapp/src/robot-xapp.go b/ric_robot_suite/xapp/src/robot-xapp.go new file mode 100755 index 0000000..b2ae40a --- /dev/null +++ b/ric_robot_suite/xapp/src/robot-xapp.go @@ -0,0 +1,165 @@ +/* +================================================================================== + Copyright (c) 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. +================================================================================== +*/ +package main + +import "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp" +import "encoding/json" +import "strconv" + + +type robotPolicy struct { + Message string `json:"message"` +} + +type A1Policy struct { + Operation string `json:"operation"` + Type int `json:"policy_type_id"` + Instance string `json:"policy_instance_id"` + Payload robotPolicy `json:"payload"` +} + +type A1Response struct { + Type int `json:"policy_type_id"` + Instance string `json:"policy_instance_id"` + Handler string `json:"handler_id"` + Status string `json:"status"` +} + +type robotXApp struct { + metrics map[string]xapp.Counter +} + +func sdlKey(t int, i string) (key string) { + return strconv.Itoa(t) +"|"+ i +} + +func (r robotXApp) SendA1Response (t int, i string, handler string, status string) { + /* A1_POLICY_RESP, _ := xapp.Rmr.GetRicMessageId("A1_POLICY_RESP") */ + A1_POLICY_RESP := 20011 + msg, _ := json.Marshal(A1Response { + Type: t, + Instance: i, + Handler: handler, + Status: status, + }) + + xapp.Logger.Debug("Outgoing A1 Response %s (length %d)", string(msg), len(msg)) + /* fixme: check response for errors */ + xapp.Rmr.SendMsg(&xapp.RMRParams { + Mtype: A1_POLICY_RESP, + Payload: msg, + PayloadLen: len(msg), + Xid: "", + SubId: t, + }) + r.metrics["MessagesSent"].Inc() +} + +func (r robotXApp) CreatePolicy(t int, i string, policy robotPolicy) { + k := sdlKey(t, i) + err := xapp.Sdl.Store(k, policy.Message) + if err == nil { + xapp.Logger.Debug("Created instance %s of policy %d", i, t) + r.metrics["PolicyCreates"].Inc() + r.metrics["dbStores"].Inc() + r.SendA1Response(t, i, "robot", "OK") + } else { + xapp.Logger.Error("Failed to create DB record for instance %s of policy %d: %v", i, t, err) + r.metrics["dbStoreFailures"].Inc() + r.SendA1Response(t, i, "robot", "ERROR") + } +} + +func (r robotXApp) DeletePolicy(t int, i string) { + k := sdlKey(t, i) + + policies, _ := xapp.Sdl.Read(k) + existingPolicy, _ := policies[k] + + if existingPolicy != nil { + err := xapp.Sdl.Delete([]string{k}) + if err == nil { + xapp.Logger.Debug("Deleted instance %s of policy %d, old value: %s", i, t, existingPolicy) + r.metrics["PolicyDeletes"].Inc() + r.metrics["dbDeletes"].Inc() + r.SendA1Response(t, i, "robot", "DELETED") + } else { + xapp.Logger.Error("Failed to delete DB record for instance %s of policy %d: %v", i, t, err) + r.metrics["dbDeleteFailures"].Inc() + r.SendA1Response(t, i, "robot", "ERROR") + } + } else { + xapp.Logger.Error("Attempt to delete nonexistent instance %s of policy %d", i, t) + r.metrics["NonexistentPolicyDeletes"].Inc() + r.SendA1Response(t, i, "robot", "ERROR") + } +} + +func (r robotXApp) Consume(msg *xapp.RMRParams) (err error) { + /* this is returning 0. will investigate and fix someday. */ + /* A1_POLICY_REQ, _ := xapp.Rmr.GetRicMessageId("A1_POLICY_REQ") */ + A1_POLICY_REQ := 20010 + + xapp.Logger.Debug("Message received - type=%d, Src=%s (%s), payload=%s", + msg.Mtype, xapp.Rmr.GetRicMessageName(msg.Mtype), msg.Src, string(msg.Payload)) + + /* this is bogus right now, but we'll eventually support more than one message + also, xapps really should handle messages in a separate goroutine, but there's + no real need in this one as we're not latency bound */ + if msg.Mtype == A1_POLICY_REQ { + var a1Msg A1Policy + err := json.Unmarshal(msg.Payload, &a1Msg) + + xapp.Logger.Debug("... Policy request - err=%v|op=%s|type=%d|instance=%s", + err, a1Msg.Operation, a1Msg.Type, a1Msg.Instance) + switch a1Msg.Operation { + case "CREATE": + go r.CreatePolicy(a1Msg.Type, a1Msg.Instance, a1Msg.Payload) + case "DELETE": + go r.DeletePolicy(a1Msg.Type, a1Msg.Instance) + } + } + return nil +} + +func main() { + counters := []xapp.CounterOpts { + { Name: "PolicyCreates", Help: "A1 policies created" }, + { Name: "DuplicatePolicyCreates", + Help: "A1 CREATE requests received for existing policy instances" }, + { Name: "PolicyUpdates", Help: "A1 policies updateded" }, + { Name: "NonexistentPolicyUpdates", + Help: "A1 UPDATE requests received for nonexistent policy instances" }, + { Name: "PolicyDeletes", Help: "A1 policies deleted" }, + { Name: "NonexistentPolicyDeletes", + Help: "A1 DELETE requests received for nonexistent policy instances" }, + { Name: "dbStores", + Help: "SDL store requests" }, + { Name: "dbStoreFailures", + Help: "SDL store request failures" }, + { Name: "dbDeletes", + Help: "SDL delete requests" }, + { Name: "dbDeleteFailures", + Help: "SDL delete request failures" }, + { Name: "MessagesReceived", + Help: "Total RMR messages received" }, + { Name: "MessagesSent", + Help: "Total RMR messages sent" }, + } + xapp.Run(robotXApp{ metrics: xapp.Metric.RegisterCounterGroup(counters, "robotXApp")}) +} -- 2.16.6