IMPL: MC Xapp tests 21/1321/2
authordave kormann <davek@research.att.com>
Fri, 1 Nov 2019 15:59:43 +0000 (11:59 -0400)
committerdave kormann <davek@research.att.com>
Mon, 4 Nov 2019 19:01:06 +0000 (14:01 -0500)
This change adds tests and supporting code for the measurement campaign (MC)
XApp.

The change also makes some cleanups to the robot files, moving utility keywords
to a separate resource file and adding some missing license headers.

signed-off-by: dave kormann <dk3239@att.com>
Change-Id: I5ae1a41157f29fc95545aa18bad127b6c9efb968

17 files changed:
ric_robot_suite/helm/nanobot/configmap-src/public/properties/global_properties.robot
ric_robot_suite/helm/nanobot/configmap-src/public/properties/mcxapp_properties.robot [new file with mode: 0644]
ric_robot_suite/helm/nanobot/configmap-src/public/testsuites/ete.robot
ric_robot_suite/helm/nanobot/configmap-src/public/testsuites/health-check.robot
ric_robot_suite/helm/nanobot/configmap-src/public/testsuites/mcxapp.robot [new file with mode: 0644]
ric_robot_suite/helm/nanobot/configmap-src/public/testsuites/ricdeployment.robot
ric_robot_suite/helm/nanobot/configmap-src/public/testsuites/update_ric_page.robot
ric_robot_suite/helm/nanobot/templates/job-ric-robot-run.yaml
ric_robot_suite/helm/nanobot/values.yaml
ric_robot_suite/ric-python-utils/ricutils/KubernetesEntity.py
ric_robot_suite/robot/resources/appmgr/appmgr_interface.robot
ric_robot_suite/robot/resources/ric/placeholder_for_files [deleted file]
ric_robot_suite/robot/resources/ric/ric_utils.robot [new file with mode: 0644]
ric_robot_suite/robot/resources/xapps/mcxapp_properties.robot [new file with mode: 0644]
ric_robot_suite/robot/resources/xapps/placeholder_for_files [deleted file]
ric_robot_suite/robot/testsuites/mcxapp.robot [new file with mode: 0644]
ric_robot_suite/robot/testsuites/ricdeployment.robot

index 58f22dd..3c15f62 100644 (file)
@@ -1,3 +1,17 @@
+#   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.
+
 {{- $domain := default "cluster.local" .Values.ric.cluster.domain }}
 {{- $ricpltNS := include "common.namespace.platform" . }}
 {{- $xappNS := include "common.namespace.xapp" . }}
@@ -21,24 +35,31 @@ Documentation        store all properties that can change or are used in multipl
 ${GLOBAL_APPLICATION_ID}              {{ default "r0" .Values.ric.robot.release | printf "nanobot-%s" }}
 ${GLOBAL_BUILD_NUMBER}                {{ default "0" .Values.ric.platform.build  }}
 ${GLOBAL_RICPLT_NAMESPACE}            {{ $ricpltNS  }}
+${GLOBAL_XAPP_NAMESPACE}              {{ $xappNS  }}
 #
+{{- if .Values.ric.platform.components.appmgr }}
 ${GLOBAL_APPMGR_SERVER_PROTOCOL}      {{ default "http" .Values.ric.platform.components.appmgr.protocol  }}
 ${GLOBAL_INJECTED_APPMGR_IP_ADDR}     {{ printf "%s.%s" (include "common.servicename.appmgr.http" .) $ricplt  }}
 ${GLOBAL_APPMGR_SERVER_PORT}          {{ include "common.serviceport.appmgr.http" .  }}
 ${GLOBAL_INJECTED_APPMGR_USER}        {{ .Values.ric.platform.components.appmgr.user  }}
 ${GLOBAL_INJECTED_APPMGR_PASSWORD}    {{ .Values.ric.platform.components.appmgr.password  }}
+{{- end }}
 #
+{{- if .Values.ric.platform.components.e2mgr }}
 ${GLOBAL_E2MGR_SERVER_PROTOCOL}       {{ default "http" .Values.ric.platform.components.e2mgr.protocol  }}
 ${GLOBAL_INJECTED_E2MGR_IP_ADDR}      {{ printf "%s.%s" (include "common.servicename.e2mgr.http" .) $ricplt  }}
 ${GLOBAL_E2MGR_SERVER_PORT}           {{ include "common.serviceport.e2mgr.http" .  }}
 ${GLOBAL_INJECTED_E2MGR_USER}         {{ .Values.ric.platform.components.e2mgr.user  }}
 ${GLOBAL_INJECTED_E2MGR_PASSWORD}     {{ .Values.ric.platform.components.e2mgr.password  }}
+{{- end }}
 #
+{{- if .Values.ric.platform.components.rtmgr }}
 ${GLOBAL_RTMGR_SERVER_PROTOCOL}       {{ default "http" .Values.ric.platform.components.rtmgr.protocol  }}
 ${GLOBAL_INJECTED_RTMGR_IP_ADDR}      {{ printf "%s.%s" (include "common.servicename.rtmgr.http" .) $ricplt  }}
 ${GLOBAL_RTMGR_SERVER_PORT}           {{ include "common.serviceport.e2mgr.http" .  }}
 ${GLOBAL_INJECTED_RTMGR_USER}         {{ .Values.ric.platform.components.rtmgr.user  }}
 ${GLOBAL_INJECTED_RTMGR_PASSWORD}     {{ .Values.ric.platform.components.rtmgr.password  }}
+{{- end }}
 #
 ${GLOBAL_INJECTED_DBAAS_IP_ADDR}      {{ printf "%s.%s" (include "common.servicename.dbaas.tcp" .) $ricplt  }}
 ${GLOBAL_DBAAS_SERVER_PORT}           {{ include "common.serviceport.dbaas.tcp" .  }}
@@ -47,4 +68,8 @@ ${GLOBAL_TEST_XAPP}                   {{ default "xapp-std" .Values.ric.robot.en
 #
 ${GLOBAL_TEST_NODEB_NAME}             {{ default "AAAA456789" .Values.ric.robot.environment.gNodeB.name }}
 ${GLOBAL_TEST_NODEB_ADDRESS}          {{ default "10.0.0.3"   .Values.ric.robot.environment.gNodeB.address }}
-${GLOBAL_TEST_NODEB_PORT}             {{ default "36421"      .Values.ric.robot.environment.gNodeB.port }}
\ No newline at end of file
+${GLOBAL_TEST_NODEB_PORT}             {{ default "36421"      .Values.ric.robot.environment.gNodeB.port }}
+#
+${GLOBAL_DASH_SERVER_PROTOCOL}        {{ default "http"       .Values.ric.robot.environment.dashboard.protocol }}
+${GLOBAL_DASH_SERVER_PORT}            {{ default "31080"      .Values.ric.robot.environment.dashboard.port }}
+${GLOBAL_INJECTED_DASH_IP_ADDR}       {{ default "127.0.0.1"  .Values.ric.robot.environment.dashboard.port }}
diff --git a/ric_robot_suite/helm/nanobot/configmap-src/public/properties/mcxapp_properties.robot b/ric_robot_suite/helm/nanobot/configmap-src/public/properties/mcxapp_properties.robot
new file mode 100644 (file)
index 0000000..6d69dd9
--- /dev/null
@@ -0,0 +1,23 @@
+#   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        This file contains configuration specific to the MC xapp.
+
+*** Variables ***
+${GLOBAL_MCXAPP_LISTENER_NAME}        {{ default "mc-xapp-listener" .Values.ric.xapp.mcxapp.listener.containername }}
+${GLOBAL_MCXAPP_WRITER_NAME}          {{ default "mc-xapp-writer" .Values.ric.xapp.mcxapp.writer.containername }}
+@{GLOBAL_MCXAPP_WRITER_STATISTICS}    {{- range .Values.ric.xapp.mcxapp.writer.statistics }}
+...                                   {{.}}
+                                      {{- end }}
index 8f8b282..35d12a3 100644 (file)
@@ -1,3 +1,17 @@
+#   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    Executes the End To End Test cases
 ...
index a8faacf..5e5fdba 100644 (file)
@@ -1,20 +1,42 @@
+#   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     Testing RIC components are available via calls.
-...
-...               Testing RIC components are available via calls.
-Test Timeout      10 second
-Resource          ../resources/appmgr/appmgr_interface.robot
-Resource          ../resources/e2mgr/e2mgr_interface.robot
-Resource          ../resources/rtmgr/rtmgr_interface.robot
+Documentation      Run basic health checks for all known components which have one
 
-*** Test Cases ***
-Basic AppMgr Health Check
-    [Tags]    health    
-    Run AppMgr Health Check
-Basic E2Mgr Health Check
-    [Tags]    health    
-    Run E2Mgr Health Check
-Basic RtMgr Health Check
-    [Tags]    health    
-    Run RtMgr Health Check
+Resource           /robot/resources/global_properties.robot
 
+Resource           /robot/resources/ric/ric_utils.robot
+
+*** Test Cases ***
+Basic Component Health Checks
+  [Documentation]  For any defined RIC component with a health check keyword,
+  ...              Run that keyword.  "Health check" keywords have names of the
+  ...              form "Run ${component} Health Check".
+  # This could have been entirely implemented in Helm; however, I wanted to
+  # allow for the possibility that it would be used (with some modification)
+  # by the ric-robot, which does not perform template expansion on testsuites.
+  [Tags]  health
+  Set Test Variable    ${finalStatus}  PASS
+  :FOR   ${component}  IN              @{GLOBAL_RICPLT_COMPONENTS}
+  \  Run Keyword And Ignore Error
+  ...   Import Resource                /robot/resources/${component}/${component}_interface.robot
+  \  ${healthCheck} =  Set Variable    Run ${component} Health Check
+  \  ${status} =       Run Keyword If Present                   ${healthCheck}
+  \  ${finalStatus} =  Set Variable If   '${status}' == 'FAIL'  FAIL  ${finalStatus}
+  \  Run Keyword If    '${status}' == 'FAIL'
+  ...                  Log  ${component} is unhealthy
+  Run Keyword If       '${finalStatus}' == 'FAIL'
+  ...                  Fail  One or more Health Checks failed
diff --git a/ric_robot_suite/helm/nanobot/configmap-src/public/testsuites/mcxapp.robot b/ric_robot_suite/helm/nanobot/configmap-src/public/testsuites/mcxapp.robot
new file mode 100644 (file)
index 0000000..756ff23
--- /dev/null
@@ -0,0 +1,82 @@
+#   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 Measurement Campaign XApp
+
+
+Resource       /robot/resources/global_properties.robot
+Resource       /robot/resources/mcxapp_properties.robot
+
+Resource       /robot/resources/ric/ric_utils.robot
+
+Library        Collections
+Library        KubernetesEntity          ${GLOBAL_XAPP_NAMESPACE}
+
+*** Variables ***
+${listenerContainerName}    mc-xapp-listener
+${listenerStatRegex}        ^[0-9]+\\s*\\[STAT\\]\\s*\\(mcl\\)
+${recentListenerDrops}      .*last 60s.*drops\\s*=\\s*([1-9][0-9]*)
+${recentListenerErrors}     .*last 60s.*errs\\s*=\\s*([1-9][0-9]*)
+${writerVesSuccesses}       .*successful\\s+ves\\s+posts\\s*-\\s*([1-9][0-9]*)
+${writerVesErrors}          .*failed\\s+ves\\s+posts\\s*-\\s*([1-9][0-9]*)
+
+*** Test Cases ***
+XApp Should Be Available
+  [Tags]  etetests  xapptests  mcxapptests
+  ${deploymentName} =  Get From Dictionary  ${GLOBAL_RICPLT_XAPPS}  mcxapp
+  Set Suite Variable   ${deploymentName}
+  ${deploy} =          Deployment           ${deploymentName}
+  ${status} =          Most Recent Availability Condition           @{deploy.status.conditions}
+  Should Be Equal As Strings   ${status}  True  ignore_case=True
+
+Listener Should Not Be Dropping Messages
+  [Tags]  etetests  xapptests  mcxapptests
+  ${log} =  Most Recent Container Logs  ${deploymentName}
+  ...                                   ${GLOBAL_MCXAPP_LISTENER_NAME}
+  ...                                   ${listenerStatRegex}
+  Should Not Contain Match             ${log}                   regexp=${recentListenerDrops}
+  
+Listener Should Not Be Producing Errors
+  [Tags]  etetests  xapptests  mcxapptests
+  ${log} =  Most Recent Container Logs  ${deploymentName}
+  ...                                   ${GLOBAL_MCXAPP_LISTENER_NAME}
+  ...                                   ${listenerStatRegex}
+  Should Not Contain Match             ${log}                   regexp=${recentListenerErrors}
+
+Writer Should Be Successfully Sending Statistics
+  [Tags]  etetests  xapptests  mcxapptests
+  Set Test Variable  ${finalStatus}  PASS
+  :FOR  ${stat}  IN  @{GLOBAL_MCXAPP_WRITER_STATISTICS}
+  \  ${statRE} =        Regexp Escape  ${stat}
+  \  ${log} =           Most Recent Container Logs    ${deploymentName}
+  ...                   ${GLOBAL_MCXAPP_WRITER_NAME}
+  ...                   ^${statRE}:\\s+successful\\s+ves\\s+posts\\.*
+  \  ${status}  ${u} =  Run Keyword And Ignore Error
+  ...                   Should Contain Match  ${log}  regexp=${writerVesSuccesses}
+  \  ${finalStatus} =   Set Variable If  "${status}" == "FAIL"
+  ...                   FAIL
+  ...                   ${finalStatus}
+  \  Run Keyword If     "${status}" == "FAIL"
+  ...                   Log  No messages have been sent to VES for ${stat}
+  \  ${status}  ${u} =  Run Keyword And Ignore Error
+  ...                   Should Not Contain Match  ${log}  regexp=${writerVesErrors}
+  \  ${finalStatus} =   Set Variable If  "${status}" == "FAIL"
+  ...                   FAIL
+  ...                   ${finalStatus}
+  \  Run Keyword If     "${status}" == "FAIL"
+  ...                   Log  ${stat} is producing errors logging to VES
+  Run Keyword If        "${finalStatus}" == "FAIL"
+  ...                   Fail  One or more statistics is not being succesfully logged
+
index 3417675..85920f7 100644 (file)
@@ -18,6 +18,8 @@ Documentation   Tests for the existence and functionality of RIC components
 
 Resource       /robot/resources/global_properties.robot
 
+Resource       /robot/resources/ric/ric_utils.robot
+
 Library  KubernetesEntity  ${GLOBAL_RICPLT_NAMESPACE}
 Library  Collections
 Library  String
@@ -30,13 +32,3 @@ Ensure RIC components are deployed and available
   \  ${deploy} =          Deployment           ${deploymentName}
   \  ${status} =          Most Recent Availability Condition                @{deploy.status.conditions}
   \  Should Be Equal As Strings  ${status}  True  ignore_case=True  msg=${Component} is not available
-
-*** Keywords ***
-Most Recent Availability Condition
-  # this makes the probably-unsafe assumption that the conditions are ordered
-  # temporally.
-  [Arguments]  @{Conditions}
-  ${status} =  Set Variable  'False'
-  :FOR  ${Condition}  IN  @{Conditions}
-  \  ${status} =  Set Variable If  '${Condition.type}' == 'Available'  ${Condition.status}  ${status}
-  [Return]  ${status}
index a2efd1c..5fab634 100644 (file)
@@ -1,3 +1,17 @@
+#   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    Initializes RIC Test Web Page and Password
 
index 2ca4ffb..c476853 100644 (file)
@@ -72,7 +72,7 @@ metadata:
   namespace: {{ $xappNamespace }}
 rules:
 - apiGroups: [""]
-  resources: ["pods", "services"]
+  resources: ["pods", "pods/log", "services"]
   verbs: ["get", "list"]
 - apiGroups: ["apps"]
   resources: ["deployments", "daemonsets", "replicasets", "statefulsets"]
@@ -103,10 +103,11 @@ metadata:
   name: {{ $jobName }}-ric-robot-run
   namespace: {{ .Release.Namespace }}
 spec:
+  backoffLimit:  {{ default 0 .Values.ric.robot.job.backoffLimit }}
   template:
     spec:
       serviceAccountName: {{ $serviceAccountName }}
-      restartPolicy: Never
+      restartPolicy: {{ default "Never" .Values.ric.robot.job.restartPolicy }}
       initContainers:
         - name: {{ $jobName }}-generate-robot-kubeconfig
           {{ with .Values.images.ric.robot.job.init }}
index 0abfda0..16ac5a6 100644 (file)
@@ -54,20 +54,37 @@ ric:
   # platform source tree
   releaseName: r0
   components:
+   a1mediator:
    appmgr:
     user: test
     password: test
-   rtmgr:
-    user: test
-    password: test
+   dbaas:
    e2mgr:
     user: test
     password: test
    e2term:
-   a1mediator:
+   rtmgr:
+    user: test
+    password: test
+   submgr:
  xapp:
-  anr:
-   servicename: anr
+  mcxapp:
+   listener:
+    containername: mc-xapp-listener
+   writer:
+    containername: mc-xapp-writer
+    # statistics sent to ves by the writer.  the tests
+    # will check that these are actually being sent
+    statistics:
+    - mc_connected_cnt
+    - mc_connection_stats
+    - mc_connects_cnt
+    - mc_disconnects_cnt
+    - mc_unique_ue_cnt
+    - rrcx_pdf_serv_cell
+    - rrcx_pdf_serv_gnb
+    - rrcx_stats_serv_cell
+    - rrcx_stats_serv_gnb
  robot:
   release: r1
   #
@@ -96,7 +113,7 @@ ric:
    # set this to a true value to cause
    # the job to fail (and, typically, be resubmitted)
    # when tests fail
-   failOnTestFail: false
+   failOnTestFail: true
    serviceAccount:
     # by default, the job will create
     # a temporary serviceaccount and
@@ -123,3 +140,6 @@ ric:
      name: AAAA456789
      address: 10.0.0.3
      port: 36421
+    dashboard:
+     address: 10.1.0.1
+     port: 31080
\ No newline at end of file
index 7b2535a..8944167 100644 (file)
@@ -17,6 +17,7 @@ from kubernetes import client, config
 import string
 import random
 import time
+import sys
 
 # This library provides a massively-simplified interface to the kubernetes
 # API library to reduce bloat in robot tests.
@@ -82,3 +83,19 @@ class KubernetesEntity(object):
    timeout -= 1
   raise TimeoutError('Kubernetes timeout waiting for ' + name + ' to become available')
 
+ def RetrievePodsForDeployment(self, name):
+  # return the pod names associated with a deployment
+  d = self.Deployment(name)
+  labels = d.spec.selector.match_labels
+  pods = self._k8sCore.list_namespaced_pod(self._ns,
+                                           label_selector=",".join(map(lambda k: k + "=" + labels[k], 
+                                                                       labels)))
+  return map(lambda i: i.metadata.name, pods.items)
+ def RetrieveLogForPod(self, pod, container='', tail=sys.maxsize):
+  # not really an "entity" thing per se, but.
+  # kinda want to include timestamps, but i don't have a use case for them.
+  return self._k8sCore.read_namespaced_pod_log(namespace=self._ns,
+                                               name=pod,
+                                               container=container,
+                                               tail_lines=tail).split('\n')[0:-1]
index 3f14604..c346e95 100644 (file)
@@ -5,6 +5,8 @@ Library        RequestsLibrary
 
 Resource       ../global_properties.robot
 
+Resource       ../ric/ric_utils.robot
+
 *** Variables ***
 ${APPMGR_BASE_PATH}  /ric/v1/xapps
 ${APPMGR_ENDPOINT}   ${GLOBAL_APPMGR_SERVER_PROTOCOL}://${GLOBAL_INJECTED_APPMGR_IP_ADDR}:${GLOBAL_APPMGR_SERVER_PORT}
@@ -104,20 +106,3 @@ Run AppMgr DELETE Request
      ${headers} =     Create Dictionary  Accept=application/json         Content-Type=application/json
      ${resp} =        Delete Request     roboAppmgrDelete                ${APPMGR_BASE_PATH}${path}  headers=${headers}
      [Return]         ${resp}
-
-
-# a few useful list routines that should probably live elsewhere
-Pluck
-     [Documentation]  Get the values of a specific key from a list of dictionaries
-     [Arguments]      ${k}      ${l}
-     @{names} =       Evaluate  filter(lambda v: v != None, [i.get('${k}', None) for i in ${l}])
-     [Return]         ${names}
-
-Subtract From List
-     [Documentation]  Remove the elements of the second argument from the first
-     [Arguments]      ${x}  ${y}
-     ${diff} =        Run Keyword If  ${y}
-     ...              Evaluate  filter(lambda v: v not in ${y}, ${x})
-     ...              ELSE
-     ...              Set Variable    ${x}
-     [Return]         ${diff}
diff --git a/ric_robot_suite/robot/resources/ric/placeholder_for_files b/ric_robot_suite/robot/resources/ric/placeholder_for_files
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/ric_robot_suite/robot/resources/ric/ric_utils.robot b/ric_robot_suite/robot/resources/ric/ric_utils.robot
new file mode 100644 (file)
index 0000000..30ed937
--- /dev/null
@@ -0,0 +1,90 @@
+#   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  A library of utility keywords that may be useful
+...            in various test suites.
+
+Library        Collections
+
+*** Keywords ***
+#
+# Kubernetes Utilities
+Most Recent Availability Condition
+  # this makes the probably-unsafe assumption that the conditions are ordered
+  # temporally.
+  [Arguments]  @{Conditions}
+  ${status} =  Set Variable  'False'
+  :FOR  ${Condition}  IN  @{Conditions}
+  \  ${status} =  Set Variable If  '${Condition.type}' == 'Available'  ${Condition.status}  ${status}
+  [Return]  ${status}
+
+Most Recent Container Logs
+  [Arguments]  ${deployment}  ${container}=${EMPTY}  ${regex}=${EMPTY}
+  ${pods} =            Retrieve Pods For Deployment  ${deployment}
+  ${logs} =            Create List
+  :FOR  ${pod}  IN  @{pods}
+  \  ${log} =   Retrieve Log For Pod     ${pod}             ${container}
+  \  Should Not Be Empty        ${log}   No log entries for ${pod}/${container}
+  \  ${line} =  Run Keyword If           "${regex}" != "${EMPTY}"
+  ...                                    Most Recent Match  ${log}  ${regex}
+  ...           ELSE
+  ...                                    Get From List      ${log}  -1
+  \  Append To List             ${logs}  ${line}
+  [Return]                      ${logs}
+
+#
+# Robot metatools
+Run Keyword If Present
+  [Documentation]  Run the given keyword, ignoring errors and returning status, if it exists.
+  ...              Returns "PASS" if the keyword does not exist.
+  [Arguments]            ${kw}
+  ${exists}  ${u} =      Run Keyword And Ignore Error
+  ...                    Keyword Should Exist          ${kw}
+  ${status}  ${u} =      Run Keyword If                '${exists}' == 'PASS'
+  ...                    Run Keyword And Ignore Error  ${kw}
+  ...                    ELSE
+  ...                    Set Variable   PASS  unused
+  [Return]               ${status}
+
+#
+# Data manipulation
+Toggle Flag
+    [Documentation]      Apply "not" to a boolean flag
+    [Arguments]          ${flag}
+    ${bFlag} =           Convert To Boolean  ${flag}
+    ${bFlag} =           Set Variable If     ${bFlag}  ${false}  ${true}
+    [Return]             ${bFlag}
+
+Most Recent Match
+    [Arguments]    ${list}        ${regex}
+    ${matches} =   Get Matches    ${list}     regexp=${regex}
+    Should Not Be Empty           ${matches}  No log entries matching ${regex}
+    ${match} =     Get From List  ${matches}  -1
+    [Return]       ${match}
+  
+Pluck
+     [Documentation]  Get the values of a specific key from a list of dictionaries
+     [Arguments]      ${k}      ${l}
+     @{names} =       Evaluate  filter(lambda v: v != None, [i.get('${k}', None) for i in ${l}])
+     [Return]         ${names}
+
+Subtract From List
+     [Documentation]  Remove the elements of the second argument from the first
+     [Arguments]      ${x}  ${y}
+     ${diff} =        Run Keyword If  ${y}
+     ...              Evaluate  filter(lambda v: v not in ${y}, ${x})
+     ...              ELSE
+     ...              Set Variable    ${x}
+     [Return]         ${diff}
diff --git a/ric_robot_suite/robot/resources/xapps/mcxapp_properties.robot b/ric_robot_suite/robot/resources/xapps/mcxapp_properties.robot
new file mode 100644 (file)
index 0000000..c9ca587
--- /dev/null
@@ -0,0 +1,30 @@
+#   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        This file contains configuration specific to the MC xapp.
+
+*** Variables ***
+${GLOBAL_MCXAPP_LISTENER_NAME}        mc-xapp-listener
+${GLOBAL_MCXAPP_WRITER_NAME}          mc-xapp-writer
+@{GLOBAL_MCXAPP_WRITER_STATISTICS}    
+...                                   mc_connected_cnt
+...                                   mc_connection_stats
+...                                   mc_connects_cnt
+...                                   mc_disconnects_cnt
+...                                   mc_unique_ue_cnt
+...                                   rrcx_pdf_serv_cell
+...                                   rrcx_pdf_serv_gnb
+...                                   rrcx_stats_serv_cell
+...                                   rrcx_stats_serv_gnb
diff --git a/ric_robot_suite/robot/resources/xapps/placeholder_for_files b/ric_robot_suite/robot/resources/xapps/placeholder_for_files
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/ric_robot_suite/robot/testsuites/mcxapp.robot b/ric_robot_suite/robot/testsuites/mcxapp.robot
new file mode 100644 (file)
index 0000000..dd85e45
--- /dev/null
@@ -0,0 +1,103 @@
+#   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 Measurement Campaign XApp
+
+Resource       ../resources/xapps/mcxapp_properties.robot
+Resource       ../resources/global_properties.robot
+
+Library        Collections
+Library        KubernetesEntity          ${GLOBAL_XAPP_NAMESPACE}
+
+*** Variables ***
+${deploymentName} =         ${GLOBAL_XAPP_NAMESPACE}-mcxapp
+${listenerContainerName} =  mc-xapp-listener
+${listenerStatRegex} =      ^[0-9]+\\s*\\[STAT\\]\\s*\\(mcl\\)
+${recentListenerDrops} =    last 60s.*drops\\s*=\\s*[1-9]
+${recentListenerErrors} =   last 60s.*errs\\s*=\\s*[1-9]
+
+*** Test Cases ***
+MC XApp Should Be Available
+  [Tags]  etetests  xapptests  mcxapptests
+  ${deploy} =          Deployment           ${deploymentName}
+  ${status} =          Most Recent Availability Condition           @{deploy.status.conditions}
+  Should Be Equal As Strings   ${status}  True  ignore_case=True
+
+MC XApp Listener Should Not Be Dropping Messages
+  [Tags]  etetests  xapptests  mcxapptests
+  ${log} =  Most Recent Container Log  ${listenerContainerName}  ${listenerStatRegex}
+  Should Not Contain Match             ${log}                    ${recentListenerDrops}
+  
+MC XApp Listener Should Not Be Producing Errors
+  [Tags]  etetests  xapptests  mcxapptests
+  ${log} =  Most Recent Container Log  ${listenerContainerName}  ${listenerStatRegex}
+  Should Not Contain Match             ${log}                    ${recentListenerErrors}
+
+Writer Should Be Successfully Sending Statistics
+  [Tags]  etetests  xapptests  mcxapptests
+  Set Test Variable  ${finalStatus}  PASS
+  :FOR  ${stat}  IN  @{GLOBAL_MCXAPP_WRITER_STATISTICS}
+  \  ${statRE} =        Regexp Escape  ${stat}
+  \  ${log} =           Most Recent Container Log
+  ...                   ${GLOBAL_MCXAPP_WRITER_NAME}
+  ...                   ^${statRE}:\\s+successful\\s+ves\\s+posts\\.*
+  \  ${status}  ${u} =  Run Keyword And Ignore Error
+  ...                   Should Contain Match  ${log}  regexp=${writerVesSuccesses}
+  \  ${finalStatus} =   Set Variable If  "${status}" == "FAIL"
+  ...                   FAIL
+  ...                   ${finalStatus}
+  \  Run Keyword If     "${status}" == "FAIL"
+  ...                   Log  No messages have been sent to VES for ${stat}
+  \  ${status}  ${u} =  Run Keyword And Ignore Error
+  ...                   Should Not Contain Match  ${log}  regexp=${writerVesErrors}
+  \  ${finalStatus} =   Set Variable If  "${status}" == "FAIL"
+  ...                   FAIL
+  ...                   ${finalStatus}
+  \  Run Keyword If     "${status}" == "FAIL"
+  ...                   Log  ${stat} is producing errors logging to VES
+  Run Keyword If        "${finalStatus}" == "FAIL"
+  ...                   Fail  One or more statistics is not being succesfully logged
+
+*** Keywords ***
+Most Recent Availability Condition
+  # this makes the probably-unsafe assumption that the conditions are ordered
+  # temporally.
+  [Arguments]  @{Conditions}
+  ${status} =  Set Variable  'False'
+  :FOR  ${Condition}  IN  @{Conditions}
+  \  ${status} =  Set Variable If  '${Condition.type}' == 'Available'  ${Condition.status}  ${status}
+  [Return]  ${status}
+
+Most Recent Match
+  [Arguments]    ${list}        ${regex}
+  ${matches} =   Get Matches    ${list}     regexp=${regex}
+  Should Not Be Empty           ${matches}  No log entries matching ${regex}
+  ${match} =     Get From List  ${matches}  -1
+  [Return]       ${match}
+  
+Most Recent Container Log
+  [Arguments]   ${container}=${EMPTY}  ${regex}=${EMPTY}
+  ${pods} =            Retrieve Pods For Deployment  ${deploymentName}
+  ${logs} =            Create List
+  :FOR  ${pod}  IN  @{pods}
+  \  ${log} =   Retrieve Log For Pod     ${pod}             ${container}
+  \  Should Not Be Empty        ${log}   No log entries for ${pod}/${container}
+  \  ${line} =  Run Keyword If           "${regex}" != "${EMPTY}"
+  ...                                    Most Recent Match  ${log}  ${regex}
+  ...           ELSE
+  ...                                    Get From List      ${log}  -1
+  \  Append To List             ${logs}  ${line}
+  [Return]                      ${logs}
+
index 5bede0a..db952c2 100644 (file)
@@ -16,7 +16,9 @@
 *** Settings ***
 Documentation   Tests for the existence and functionality of RIC components
 
-Resource       /robot/resources/global_properties.robot
+Resource       ../global_properties.robot
+
+Resource       ../ric/ric_utils.robot
 
 Library  KubernetesEntity  ${GLOBAL_RICPLT_NAMESPACE}
 Library  Collections
@@ -30,13 +32,3 @@ Deployments
   \  ${deploy} =          Deployment           ${deploymentName}
   \  ${status} =          Most Recent Availability Condition                @{deploy.status.conditions}
   \  Should Be Equal As Strings  ${status}  True  ignore_case=True  msg=${Component} is not available
-
-*** Keywords ***
-Most Recent Availability Condition
-  # this makes the probably-unsafe assumption that the conditions are ordered
-  # temporally.
-  [Arguments]  @{Conditions}
-  ${status} =  Set Variable  'False'
-  :FOR  ${Condition}  IN  @{Conditions}
-  \  ${status} =  Set Variable If  '${Condition.type}' == 'Available'  ${Condition.status}  ${status}
-  [Return]  ${status}