Files from python version needed from testing. 66/10066/4
authornaman.gupta <naman.gupta@samsung.com>
Mon, 12 Dec 2022 11:33:19 +0000 (17:03 +0530)
committernaman.gupta <naman.gupta@samsung.com>
Mon, 12 Dec 2022 14:23:03 +0000 (19:53 +0530)
Adding back files from python version needed from testing.

Signed-off-by: naman.gupta <naman.gupta@samsung.com>
Change-Id: I42b5c87c41a2efc25ac1cd63bc53e969882de48d

37 files changed:
.gitattributes [new file with mode: 0644]
.gitignore [new file with mode: 0644]
.gitreview [new file with mode: 0644]
integration_tests/a1mediator/.helmignore [new file with mode: 0644]
integration_tests/a1mediator/Chart.yaml [new file with mode: 0644]
integration_tests/a1mediator/templates/_helpers.tpl [new file with mode: 0644]
integration_tests/a1mediator/templates/config.yaml [new file with mode: 0644]
integration_tests/a1mediator/templates/deployment.yaml [new file with mode: 0644]
integration_tests/a1mediator/templates/helper.yaml [new file with mode: 0644]
integration_tests/a1mediator/templates/service.yaml [new file with mode: 0644]
integration_tests/a1mediator/templates/tests/test-connection.yaml [new file with mode: 0644]
integration_tests/a1mediator/values.yaml [new file with mode: 0644]
integration_tests/dbaas-service/Chart.yaml [new file with mode: 0644]
integration_tests/dbaas-service/README [new file with mode: 0644]
integration_tests/dbaas-service/templates/deployment.yaml [new file with mode: 0644]
integration_tests/dbaas-service/templates/service.yaml [new file with mode: 0644]
integration_tests/dbaas-service/values.yaml [new file with mode: 0644]
integration_tests/getlogs.sh [new file with mode: 0644]
integration_tests/install_rmr.sh [new file with mode: 0644]
integration_tests/portforward.sh [new file with mode: 0644]
integration_tests/test_a1.tavern.yaml [new file with mode: 0644]
integration_tests/testreceiver/.helmignore [new file with mode: 0644]
integration_tests/testreceiver/Chart.yaml [new file with mode: 0644]
integration_tests/testreceiver/templates/_helpers.tpl [new file with mode: 0644]
integration_tests/testreceiver/templates/config.yaml [new file with mode: 0644]
integration_tests/testreceiver/templates/deployment.yaml [new file with mode: 0644]
integration_tests/testreceiver/templates/service.yaml [new file with mode: 0644]
integration_tests/testreceiver/values.yaml [new file with mode: 0644]
integration_tests/testxappcode/Dockerfile-delay-receiver [new file with mode: 0644]
integration_tests/testxappcode/Dockerfile-query-receiver [new file with mode: 0644]
integration_tests/testxappcode/Dockerfile-test-receiver [new file with mode: 0644]
integration_tests/testxappcode/delay-config-file.yaml [new file with mode: 0644]
integration_tests/testxappcode/go.mod [new file with mode: 0644]
integration_tests/testxappcode/query-config-file.yaml [new file with mode: 0644]
integration_tests/testxappcode/receiver.go [new file with mode: 0644]
integration_tests/testxappcode/test-config-file.yaml [new file with mode: 0644]
local.rt [new file with mode: 0644]

diff --git a/.gitattributes b/.gitattributes
new file mode 100644 (file)
index 0000000..d4628c1
--- /dev/null
@@ -0,0 +1,25 @@
+# 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
new file mode 100644 (file)
index 0000000..45f74f2
--- /dev/null
@@ -0,0 +1,115 @@
+# 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
new file mode 100644 (file)
index 0000000..91493c1
--- /dev/null
@@ -0,0 +1,5 @@
+[gerrit]
+host=gerrit.o-ran-sc.org
+port=29418
+project=ric-plt/a1/
+defaultbranch=master
diff --git a/integration_tests/a1mediator/.helmignore b/integration_tests/a1mediator/.helmignore
new file mode 100644 (file)
index 0000000..50af031
--- /dev/null
@@ -0,0 +1,22 @@
+# 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
new file mode 100644 (file)
index 0000000..1a4eb5b
--- /dev/null
@@ -0,0 +1,4 @@
+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
new file mode 100644 (file)
index 0000000..fa46bab
--- /dev/null
@@ -0,0 +1,45 @@
+{{/* 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
new file mode 100644 (file)
index 0000000..811c118
--- /dev/null
@@ -0,0 +1,12 @@
+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
new file mode 100644 (file)
index 0000000..45b1a45
--- /dev/null
@@ -0,0 +1,63 @@
+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
new file mode 100644 (file)
index 0000000..c036840
--- /dev/null
@@ -0,0 +1,3 @@
+{{- 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
new file mode 100644 (file)
index 0000000..3321aa6
--- /dev/null
@@ -0,0 +1,56 @@
+# 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
new file mode 100644 (file)
index 0000000..ca68368
--- /dev/null
@@ -0,0 +1,15 @@
+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
new file mode 100644 (file)
index 0000000..e8bd0d2
--- /dev/null
@@ -0,0 +1,31 @@
+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
new file mode 100644 (file)
index 0000000..8d183de
--- /dev/null
@@ -0,0 +1,25 @@
+#   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
new file mode 100644 (file)
index 0000000..c15a3e0
--- /dev/null
@@ -0,0 +1 @@
+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
new file mode 100644 (file)
index 0000000..8446b8e
--- /dev/null
@@ -0,0 +1,42 @@
+#   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
new file mode 100644 (file)
index 0000000..472aeac
--- /dev/null
@@ -0,0 +1,30 @@
+#   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
new file mode 100644 (file)
index 0000000..053ac96
--- /dev/null
@@ -0,0 +1,30 @@
+#   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
new file mode 100644 (file)
index 0000000..fbf2f1b
--- /dev/null
@@ -0,0 +1,20 @@
+#!/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
new file mode 100644 (file)
index 0000000..2e58f37
--- /dev/null
@@ -0,0 +1,11 @@
+#!/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 "<<<installing rmr devel headers>>>" \
+    && 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
new file mode 100644 (file)
index 0000000..17f56b7
--- /dev/null
@@ -0,0 +1,8 @@
+#!/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
new file mode 100644 (file)
index 0000000..4657fcc
--- /dev/null
@@ -0,0 +1,670 @@
+# 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
new file mode 100644 (file)
index 0000000..50af031
--- /dev/null
@@ -0,0 +1,22 @@
+# 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
new file mode 100644 (file)
index 0000000..917669d
--- /dev/null
@@ -0,0 +1,5 @@
+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
new file mode 100644 (file)
index 0000000..f35130c
--- /dev/null
@@ -0,0 +1,45 @@
+{{/* 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
new file mode 100644 (file)
index 0000000..dd4772b
--- /dev/null
@@ -0,0 +1,40 @@
+#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
new file mode 100644 (file)
index 0000000..af17968
--- /dev/null
@@ -0,0 +1,93 @@
+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
new file mode 100644 (file)
index 0000000..ecb90a0
--- /dev/null
@@ -0,0 +1,51 @@
+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
new file mode 100644 (file)
index 0000000..f045b63
--- /dev/null
@@ -0,0 +1,16 @@
+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
new file mode 100644 (file)
index 0000000..5c1c239
--- /dev/null
@@ -0,0 +1,51 @@
+# ==================================================================================
+#       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
new file mode 100644 (file)
index 0000000..a7dd178
--- /dev/null
@@ -0,0 +1,51 @@
+# ==================================================================================
+#       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
new file mode 100644 (file)
index 0000000..196f256
--- /dev/null
@@ -0,0 +1,51 @@
+# ==================================================================================
+#       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
new file mode 100644 (file)
index 0000000..8db59f7
--- /dev/null
@@ -0,0 +1,38 @@
+#   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
new file mode 100644 (file)
index 0000000..7e202d4
--- /dev/null
@@ -0,0 +1,15 @@
+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
new file mode 100644 (file)
index 0000000..871d166
--- /dev/null
@@ -0,0 +1,38 @@
+#   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
new file mode 100644 (file)
index 0000000..9012094
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+==================================================================================
+  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
new file mode 100644 (file)
index 0000000..cd59770
--- /dev/null
@@ -0,0 +1,38 @@
+#   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
new file mode 100644 (file)
index 0000000..02816b5
--- /dev/null
+++ b/local.rt
@@ -0,0 +1,4 @@
+# Trivial RMR route table
+newrt | start
+rte   | 1 | app10:4560
+newrt | end