[VOL-3703] Performance Monitoring Attributes test
Currently test suite contains three test cases:
1. Test of pm data with default values
2. Test of pm data with user values
3. Test of pm data for disabled devices
Test 1 runs about 35 minutes.
Test 2 runs about 2 minutes.
Test 3 runs about 4 minutes
The suite is designed in a generic way. It collects all values to check by itself.
Further validation data can be passed via yaml file like ../data/pm-data.yaml
To have a determined state of devices a Sanity Check will be executed first.
Actually commands 'voltctl device pmconfig group list ...' printout the intvals without time unit.
Therefore some workarounds still included in this patch!
Change-Id: If115e85471221c321e8764cc890af583090189b2
diff --git a/Makefile b/Makefile
index 68773e4..9823039 100755
--- a/Makefile
+++ b/Makefile
@@ -50,6 +50,7 @@
ROBOT_DMI_SINGLE_BBSIM_FILE ?= $(ROOT_DIR)/tests/data/dmi-components-bbsim.yaml
ROBOT_DMI_SINGLE_ADTRAN_FILE ?= $(ROOT_DIR)/tests/data/dmi-components-adtran.yaml
ROBOT_SW_UPGRADE_FILE ?= $(ROOT_DIR)/tests/data/software-upgrade.yaml
+ROBOT_PM_DATA_FILE ?= $(ROOT_DIR)/tests/data/pm-data.yaml
# for backwards compatibility
sanity-kind: sanity-single-kind
@@ -335,6 +336,46 @@
cd tests/dmi-interface ;\
robot -V $(ROBOT_CONFIG_FILE) $(ROBOT_MISC_ARGS) $(ROBOT_FILE)
+# target to invoke single ONU pm data scenarios in ATT workflow
+voltha-pm-data-single-kind-att: ROBOT_MISC_ARGS += -v workflow:ATT
+voltha-pm-data-single-kind-att: ROBOT_CONFIG_FILE := $(ROBOT_SANITY_SINGLE_PON_FILE)
+voltha-pm-data-single-kind-att: voltha-pm-data-tests
+
+# target to invoke single ONU pm data scenarios in DT workflow
+voltha-pm-data-single-kind-dt: ROBOT_MISC_ARGS += -v workflow:DT
+voltha-pm-data-single-kind-dt: ROBOT_CONFIG_FILE := $(ROBOT_SANITY_DT_SINGLE_PON_FILE)
+voltha-pm-data-single-kind-dt: voltha-pm-data-tests
+
+# target to invoke single ONU pm data scenarios in TT workflow
+voltha-pm-data-single-tt: ROBOT_MISC_ARGS += -v workflow:TT
+voltha-pm-data-single-tt: ROBOT_CONFIG_FILE := $(ROBOT_SANITY_TT_SINGLE_PON_FILE)
+voltha-pm-data-single-tt: voltha-pm-data-tests
+
+# target to invoke multiple OLTs pm data scenarios in ATT workflow
+voltha-pm-data-multiolt-kind-att: ROBOT_MISC_ARGS += -v workflow:ATT
+voltha-pm-data-multiolt-kind-att: ROBOT_CONFIG_FILE := $(ROBOT_SANITY_MULTIPLE_OLT_FILE)
+voltha-pm-data-multiolt-kind-att: voltha-pm-data-tests
+
+# target to invoke multiple OLTs pm data scenarios in DT workflow
+voltha-pm-data-multiolt-kind-dt: ROBOT_MISC_ARGS += -v workflow:DT
+voltha-pm-data-multiolt-kind-dt: ROBOT_CONFIG_FILE := $(ROBOT_SANITY_DT_MULTIPLE_OLT_FILE)
+voltha-pm-data-multiolt-kind-dt: voltha-pm-data-tests
+
+# target to invoke multiple OLTs pm data scenarios in TT workflow
+voltha-pm-data-multiolt-kind-tt: ROBOT_MISC_ARGS += -v workflow:TT
+voltha-pm-data-multiolt-kind-tt: ROBOT_CONFIG_FILE := $(ROBOT_SANITY_TT_MULTIPLE_OLT_FILE)
+voltha-pm-data-multiolt-kind-tt: voltha-pm-data-tests
+
+voltha-pm-data-tests: ROBOT_MISC_ARGS += -i functional -e PowerSwitch $(ROBOT_DEBUG_LOG_OPT)
+voltha-pm-data-tests: ROBOT_PM_CONFIG_FILE := $(ROBOT_PM_DATA_FILE)
+voltha-pm-data-tests: ROBOT_FILE := Voltha_ONUPMTests.robot
+voltha-pm-data-tests: voltha-pm-data-test
+
+voltha-pm-data-test: vst_venv
+ source ./$</bin/activate ; set -u ;\
+ cd tests/pm-data ;\
+ robot -V $(ROBOT_CONFIG_FILE) -V $(ROBOT_PM_CONFIG_FILE) $(ROBOT_MISC_ARGS) $(ROBOT_FILE)
+
# ONOS Apps to test for Software Upgrade need to be passed in the 'onos_apps_under_test' variable in format:
# <app-name>,<version>,<oar-url>*<app-name>,<version>,<oar-url>*
onos-app-upgrade-test: ROBOT_MISC_ARGS += -e notready -i functional
diff --git a/libraries/onu_utilities.robot b/libraries/onu_utilities.robot
index 478bc56..dba9bc9 100755
--- a/libraries/onu_utilities.robot
+++ b/libraries/onu_utilities.robot
@@ -386,14 +386,17 @@
Wait for Ports in ONOS for all OLTs
[Documentation] Waits untill a certain number of ports are enabled in all OLTs
- [Arguments] ${onos_ssh_connection} ${count} ${filter} ${max_wait_time}=10m
+ [Arguments] ${onos_ssh_connection} ${count} ${filter} ${max_wait_time}=10m ${determine_number}=False
FOR ${J} IN RANGE 0 ${num_olts}
${olt_serial_number}= Set Variable ${list_olts}[${J}][sn]
${onu_count}= Set Variable ${list_olts}[${J}][onucount]
${of_id}= Wait Until Keyword Succeeds ${timeout} 15s Validate OLT Device in ONOS
... ${olt_serial_number}
Set Global Variable ${of_id}
- ${count2check} Set Variable If ${count}==${num_all_onus} ${onu_count} ${count}
+ ${count2check}= Set Variable If ${count}==${num_all_onus} ${onu_count} ${count}
+ # if flag determine_number is set to True, always determine the number of real ONUs (overwrite previous value)
+ ${count2check}= Run Keyword If ${determine_number} Determine Number Of ONU ${olt_serial_number}
+ ... ELSE Set Variable ${count2check}
Wait for Ports in ONOS ${onos_ssh_connection} ${count2check} ${of_id} BBSM ${max_wait_time}
END
diff --git a/libraries/pm_utilities.robot b/libraries/pm_utilities.robot
new file mode 100755
index 0000000..2fad74d
--- /dev/null
+++ b/libraries/pm_utilities.robot
@@ -0,0 +1,673 @@
+# Copyright 2021 - present Open Networking Foundation
+#
+# 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 Library for various pm data (metrics) test utilities
+Library String
+Library DateTime
+Library Process
+Library Collections
+Library RequestsLibrary
+Library OperatingSystem
+Library CORDRobot
+Library ImportResource resources=CORDRobot
+Library utility.py WITH NAME utility
+Resource ./voltctl.robot
+
+*** Variables ***
+# operators for value validations, needed for a better reading only
+${gt} > # greater than
+${ge} >= # greater equal
+${lt} < # less than
+${le} <= # less equal
+${eq} == # equal
+${ne} != # not equal
+
+*** Keywords ***
+#################################################################
+# pre test keywords
+#################################################################
+Create Metric Dictionary
+ [Documentation] Create metric dict of metrics to test/validate
+ ... Created dictionary has to be set to suite variable!!!
+ ${metric_dict}= Create Dictionary
+ FOR ${I} IN RANGE 0 ${num_all_onus}
+ ${src}= Set Variable ${hosts.src[${I}]}
+ ${onu_device_id}= Get Device ID From SN ${src['onu']}
+ Continue For Loop If '${onu_device_id}' in ${metric_dict}
+ # read available metric groups
+ ${group_list} ${interval_dict}= Read Group List ${onu_device_id}
+ # read available groupmetrics
+ ${groupmetrics_dict}= Get Available Groupmetrics per Device ${group_list} ${onu_device_id}
+ ${device_dict}= Create Dictionary MetricData ${groupmetrics_dict} GroupList ${group_list}
+ Set To Dictionary ${metric_dict} ${onu_device_id} ${device_dict}
+ Prepare Interval and Validation Data ${metric_dict} ${onu_device_id} ${interval_dict}
+ END
+ log ${metric_dict}
+ [return] ${metric_dict}
+
+Get Available Groupmetrics per Device
+ [Documentation] Delivers avaiable groupmetrics incl. validation data of onu
+ [Arguments] ${group_list} ${onu_device_id}
+ ${groupmetric_dict} Create Dictionary
+ FOR ${Item} IN @{group_list}
+ ${item_dict}= Read Group Metric Dict ${onu_device_id} ${Item}
+ ${item_dict}= Set Validation Operation ${item_dict} ${Item}
+ ${item_dict}= Set User Validation Operation ${item_dict} ${Item}
+ ${item_dict}= Set User Precondition Operation for Availability ${item_dict} ${Item}
+ ${metric}= Create Dictionary GroupMetrics=${item_dict}
+ ${dict}= Create Dictionary ${Item}=${metric}
+ Set To Dictionary ${groupmetric_dict} ${Item}=${metric}
+ END
+ [return] ${groupmetric_dict}
+
+Set Validation Operation
+ [Documentation] Sets the validation operation per metric parameter
+ [Arguments] ${item_dict} ${group}
+ FOR ${item} IN @{item_dict.keys()}
+ ${type}= Get From Dictionary ${item_dict["${item}"]} type
+ ${item_dict}= Run Keyword If
+ ... '${type}'=='COUNTER' Set Validation Operation For Counter ${item_dict} ${item} ${group}
+ ... ELSE IF '${type}'=='CONTEXT' Set Validation Operation For Context ${item_dict} ${item} ${group}
+ ... ELSE IF '${type}'=='GAUGE' Set Validation Operation For Gauge ${item_dict} ${item} ${group}
+ ... ELSE Run Keyword And Continue On Failure FAIL Type (${type}) is unknown!
+ END
+ [return] ${item_dict}
+
+Set Validation Operation For Counter
+ [Documentation] Sets the validation operation for a counter
+ [Arguments] ${item_dict} ${metric} ${group}
+ # will be overwritten by user operation and value if available
+ ${first}= Create Dictionary operator=${ge} operand=0
+ # for real POD it must be >= previous counter value, in case of BBSim usage we got random values, so check >= 0
+ ${successor}= Run Keyword If ${has_dataplane} Create Dictionary operator=${ge} operand=previous
+ ... ELSE Create Dictionary operator=${ge} operand=0
+ ${ValidationOperation}= Create Dictionary first=${first} successor=${successor}
+ Set To Dictionary ${item_dict['${metric}']} ValidationOperation=${ValidationOperation}
+ [return] ${item_dict}
+
+Set Validation Operation For Context
+ [Documentation] Sets the validation operation for a context
+ [Arguments] ${item_dict} ${metric} ${group}
+ # will be overwritten by user operation and value if available
+ ${first}= Create Dictionary operator=${ge} operand=0
+ ${successor}= Create Dictionary operator=${eq} operand=previous
+ ${ValidationOperation}= Create Dictionary first=${first} successor=${successor}
+ Set To Dictionary ${item_dict['${metric}']} ValidationOperation=${ValidationOperation}
+ [return] ${item_dict}
+
+Set Validation Operation For Gauge
+ [Documentation] Sets the validation operation for a gauge
+ [Arguments] ${item_dict} ${metric} ${group}
+ # will be overwritten by user operation and value if available
+ ${first}= Create Dictionary operator=${ge} operand=0
+ ${successor}= Create Dictionary operator=${ge} operand=0
+ ${ValidationOperation}= Create Dictionary first=${first} successor=${successor}
+ Set To Dictionary ${item_dict['${metric}']} ValidationOperation=${ValidationOperation}
+ [return] ${item_dict}
+
+Set User Validation Operation
+ [Documentation] Sets the user validation operation and value per metric parameter if available
+ [Arguments] ${item_dict} ${group}
+ ${variables}= Get variables no_decoration=Yes
+ Return From Keyword If not "pm_user_validation_data" in $variables ${item_dict}
+ Return From Keyword If not '${group}' in ${pm_user_validation_data} ${item_dict}
+ FOR ${item} IN @{item_dict.keys()}
+ Continue For Loop If not '${item}' in ${pm_user_validation_data['${group}']}
+ ${operator}= Get From Dictionary ${pm_user_validation_data['${group}']['${item}']} firstoperator
+ ${operand}= Get From Dictionary ${pm_user_validation_data['${group}']['${item}']} firstvalue
+ ${first}= Create Dictionary operator=${operator} operand=${operand}
+ ${operator}= Get From Dictionary ${pm_user_validation_data['${group}']['${item}']} successoroperator
+ ${operand}= Get From Dictionary ${pm_user_validation_data['${group}']['${item}']} successorvalue
+ ${successor}= Create Dictionary operator=${operator} operand=${operand}
+ ${ValidationOperation}= Create Dictionary first=${first} successor=${successor}
+ Set To Dictionary ${item_dict['${item}']} ValidationOperation=${ValidationOperation}
+ END
+ [return] ${item_dict}
+
+Set User Precondition Operation for Availability
+ [Documentation] Sets the user precondition operation, value and element per metric parameter if available
+ [Arguments] ${item_dict} ${group}
+ ${variables}= Get variables no_decoration=Yes
+ Return From Keyword If not "pm_user_precondition_data" in $variables ${item_dict}
+ Return From Keyword If not '${group}' in ${pm_user_precondition_data} ${item_dict}
+ FOR ${item} IN @{item_dict.keys()}
+ Continue For Loop If not '${item}' in ${pm_user_precondition_data['${group}']}
+ ${operator}= Get From Dictionary ${pm_user_precondition_data['${group}']['${item}']} operator
+ ${operand}= Get From Dictionary ${pm_user_precondition_data['${group}']['${item}']} value
+ ${element}= Get From Dictionary ${pm_user_precondition_data['${group}']['${item}']} precondelement
+ ${precond}= Create Dictionary operator=${operator} operand=${operand} element=${element}
+ Set To Dictionary ${item_dict['${item}']} Precondition=${precond}
+ END
+ [return] ${item_dict}
+
+Prepare Interval and Validation Data
+ [Documentation] Prepares interval and validation data of onu
+ [Arguments] ${METRIC_DICT} ${onu_device_id} ${interval_dict}
+ ${list}= Get From Dictionary ${METRIC_DICT['${onu_device_id}']} GroupList
+ FOR ${Item} IN @{list}
+ ${metric}= Get From Dictionary ${METRIC_DICT['${onu_device_id}']['MetricData']} ${Item}
+ Set To Dictionary ${metric} NumberOfChecks=0
+ ${default_interval}= Get From Dictionary ${interval_dict} ${Item}
+ # convert interval in seconds and remove unit
+ ${default_interval}= Validate Time Unit ${default_interval} False
+ ${intervals} Create Dictionary default=${default_interval} user=-1 current=${default_interval}
+ Set To Dictionary ${metric} Intervals=${intervals}
+ Set To Dictionary ${METRIC_DICT['${onu_device_id}']['MetricData']} ${Item}=${metric}
+ END
+
+Prepare Group Interval List per Device Id
+ [Documentation] Prepares group-interval list per device id
+ [Arguments] ${dev_id} ${user}=False ${group}=${EMPTY}
+ ${list}= Get From Dictionary ${METRIC_DICT['${dev_id}']} GroupList
+ ${group_interval_list} Create List
+ FOR ${Item} IN @{list}
+ ${val}= Run Keyword If ${user}
+ ... Get From Dictionary ${METRIC_DICT['${dev_id}']['MetricData']['${Item}']['Intervals']} user
+ ... ELSE Get From Dictionary ${METRIC_DICT['${dev_id}']['MetricData']['${Item}']['Intervals']} default
+ ${dict}= Create Dictionary group=${Item} interval=${val}
+ Run Keyword If '${group}'=='${EMPTY}' or '${group}'=='${Item}' Append To List ${group_interval_list} ${dict}
+ END
+ [return] ${group_interval_list}
+
+Get Longest Interval per Onu
+ [Documentation] Delivers longest group interval per device id
+ [Arguments] ${dev_id} ${user}=False
+ ${list}= Get From Dictionary ${METRIC_DICT['${dev_id}']} GroupList
+ ${longest_interval}= Set Variable 0
+ FOR ${Item} IN @{list}
+ ${val}= Run Keyword If ${user}
+ ... Get From Dictionary ${METRIC_DICT['${dev_id}']['MetricData']['${Item}']['Intervals']} user
+ ... ELSE Get From Dictionary ${METRIC_DICT['${dev_id}']['MetricData']['${Item}']['Intervals']} default
+ ${longest_interval}= Set Variable If ${val} > ${longest_interval} ${val} ${longest_interval}
+ END
+ [return] ${longest_interval}
+
+Get Longest Interval
+ [Documentation] Delivers longest interval over all devices
+ [Arguments] ${user}=False
+ ${longest_interval}= Set Variable 0
+ ${onu_list} Create List
+ FOR ${INDEX} IN RANGE 0 ${num_all_onus}
+ ${onu_device_id}= Get Device ID From SN ${hosts.src[${INDEX}].onu}
+ ${onu_id}= Get Index From List ${onu_list} ${onu_device_id}
+ Continue For Loop If -1 != ${onu_id}
+ Continue For Loop If not '${onu_device_id}' in ${METRIC_DICT}
+ ${onu_longest_interval}= Run Keyword If '${onu_device_id}'!='${EMPTY}'
+ ... Get Longest Interval per Onu ${onu_device_id} ${user}
+ ${longest_interval}= Set Variable If ${onu_longest_interval} > ${longest_interval} ${onu_longest_interval}
+ ... ${longest_interval}
+ END
+ [return] ${longest_interval}
+
+Determine Collection Interval
+ [Documentation] Delivers collection interval over all devices
+ [Arguments] ${user}=False
+ ${longest_interval}= Get Longest Interval user=${user}
+ ${collect_interval}= evaluate ((${longest_interval}*2)+(${longest_interval}*0.2))
+ ${collect_interval}= Validate Time Unit ${collect_interval}
+ [return] ${collect_interval}
+
+Set Group Interval per Onu
+ [Documentation] Sets group user interval in METRIC_DICT per device id
+ [Arguments] ${device_id} ${group} ${val}
+ # convert interval in seconds and remove unit
+ ${val}= Validate Time Unit ${val} False
+ Run Keyword If '${group}' in ${METRIC_DICT['${device_id}']['MetricData']}
+ ... Set To Dictionary ${METRIC_DICT['${device_id}']['MetricData']['${group}']['Intervals']} user=${val}
+
+Set Group Interval All Onu
+ [Documentation] Sets group user interval in METRIC_DICT
+ [Arguments] ${group} ${val}
+ ${onu_list} Create List
+ FOR ${INDEX} IN RANGE 0 ${num_all_onus}
+ ${onu_device_id}= Get Device ID From SN ${hosts.src[${INDEX}].onu}
+ ${onu_id}= Get Index From List ${onu_list} ${onu_device_id}
+ Continue For Loop If -1 != ${onu_id}
+ Continue For Loop If not '${onu_device_id}' in ${METRIC_DICT}
+ Set Group Interval per Onu ${onu_device_id} ${group} ${val}
+ END
+
+Activate And Validate Interval All Onu
+ [Documentation] Activates and validates group user interval taken from METRIC_DICT
+ [Arguments] ${user}=False ${group}=${EMPTY}
+ ${onu_list} Create List
+ FOR ${INDEX} IN RANGE 0 ${num_all_onus}
+ ${onu_device_id}= Get Device ID From SN ${hosts.src[${INDEX}].onu}
+ ${onu_id}= Get Index From List ${onu_list} ${onu_device_id}
+ Continue For Loop If -1 != ${onu_id}
+ Continue For Loop If not '${onu_device_id}' in ${METRIC_DICT}
+ ${list}= Prepare Group Interval List per Device Id ${onu_device_id} ${user} ${group}
+ Activate And Validate Metrics Interval per Onu ${onu_device_id} ${list}
+ END
+
+Activate And Validate Metrics Interval per Onu
+ [Documentation] Activates and validates interval of pm data per onu
+ [Arguments] ${device_id} ${group_list}
+ FOR ${Item} IN @{group_list}
+ Continue For Loop If not '${device_id}' in ${METRIC_DICT}
+ ${group}= Get From Dictionary ${Item} group
+ ${val}= Get From Dictionary ${Item} interval
+ Continue For Loop If not '${group}' in ${METRIC_DICT['${device_id}']['MetricData']}
+ Continue For Loop If '${val}'=='-1'
+ # set the unit to sec
+ ${val_with_unit}= Validate Time Unit ${val}
+ Set and Validate Group Interval ${device_id} ${val_with_unit} ${group}
+ # update current interval in METRICS_DICT
+ Set To Dictionary ${METRIC_DICT['${device_id}']['MetricData']['${group}']['Intervals']} current=${val}
+ END
+
+Set Validation Operation per Onu
+ [Documentation] Sets group validation data in METRIC_DICT per device id for passed metric element
+ [Arguments] ${device_id} ${group} ${metric_element} ${validation_dict}
+ Run Keyword If '${group}' in ${METRIC_DICT['${device_id}']['MetricData']}
+ ... Set To Dictionary ${METRIC_DICT['${device_id}']['MetricData']['${group}']['GroupMetrics']['${metric_element}']}
+ ... ValidationOperation=${validation_dict}
+
+Set Validation Operation All Onu
+ [Documentation] Sets group validation data in METRIC_DICT all devices and passed metric element
+ [Arguments] ${group} ${metric_element} ${validation_dict}
+ ${onu_list} Create List
+ FOR ${INDEX} IN RANGE 0 ${num_all_onus}
+ ${onu_device_id}= Get Device ID From SN ${hosts.src[${INDEX}].onu}
+ ${onu_id}= Get Index From List ${onu_list} ${onu_device_id}
+ Continue For Loop If -1 != ${onu_id}
+ Continue For Loop If not '${onu_device_id}' in ${METRIC_DICT}
+ Set Validation Operation per Onu ${onu_device_id} ${group} ${metric_element} ${validation_dict}
+ END
+
+Set Validation Operation Passed Onu
+ [Documentation] Sets group validation data in METRIC_DICT for passed devices and passed metric element
+ ... Passed dictionary has to be format <device_id>:<Validation Dictionary>
+ ... Keyword 'Get Validation Operation All Onu' delivers such a dictionary.
+ [Arguments] ${group} ${metric_element} ${validation_dict_with_device_id}
+ FOR ${item} IN @{validation_dict_with_device_id.keys()}
+ Continue For Loop If not '${item}' in ${METRIC_DICT}
+ ${validation_dict}= Get From Dictionary ${validation_dict_with_device_id} ${item}
+ Set Validation Operation per Onu ${item} ${group} ${metric_element} ${validation_dict}
+ END
+
+Get Validation Operation per Onu
+ [Documentation] Delivers group validation data in METRIC_DICT per device id for passed metric element
+ [Arguments] ${device_id} ${group} ${metric_element}
+ ${validation_dict}= Run Keyword If '${group}' in ${METRIC_DICT['${device_id}']['MetricData']}
+ ... Get From Dictionary ${METRIC_DICT['${device_id}']['MetricData']['${group}']['GroupMetrics']['${metric_element}']}
+ ... ValidationOperation
+ [return] ${validation_dict}
+
+Get Validation Operation All Onu
+ [Documentation] Delivers group validation data in METRIC_DICT all devices for passed metric element
+ [Arguments] ${group} ${metric_element}
+ ${validation_dict_with_device} Create Dictionary
+ ${onu_list} Create List
+ FOR ${INDEX} IN RANGE 0 ${num_all_onus}
+ ${onu_device_id}= Get Device ID From SN ${hosts.src[${INDEX}].onu}
+ ${onu_id}= Get Index From List ${onu_list} ${onu_device_id}
+ Continue For Loop If -1 != ${onu_id}
+ Continue For Loop If not '${onu_device_id}' in ${METRIC_DICT}
+ ${validation_dict}= Get Validation Operation per Onu ${onu_device_id} ${group} ${metric_element}
+ Set To Dictionary ${validation_dict_with_device} ${onu_device_id}=${validation_dict}
+ END
+ [return] ${validation_dict_with_device}
+
+#################################################################
+# test keywords
+#################################################################
+Collect and Validate PM Data
+ [Documentation] Collecta PM-data from kafka and validates metrics
+ [Arguments] ${collect_interval} ${clear}=True ${user}=False ${group}=${EMPTY}
+ ${Kafka_Records}= Get Metrics ${collect_interval} clear=${clear}
+ ${RecordsLength}= Get Length ${Kafka_Records}
+ FOR ${Index} IN RANGE 0 ${RecordsLength}
+ ${metric}= Set Variable ${Kafka_Records[${Index}]}
+ ${message}= Get From Dictionary ${metric} message
+ ${event}= volthatools.Events Decode Event ${message} return_default=true
+ Continue For Loop If not 'kpi_event2' in ${event}
+ Validate Raised Timestamp ${event}
+ ${slice}= Get Slice Data From Event ${event}
+ Validate Raised Timestamp ${event}
+ Validate Slice Data ${slice} ${Kafka_Records}
+ Set Previous Record ${Index} ${slice}
+ END
+ Validate Number of Checks ${collect_interval} user=${user} group=${group}
+
+Get Metrics
+ [Documentation] Delivers metrics from kafka
+ [Arguments] ${collect_interval} ${clear}=True
+ Run Keyword If ${clear} kafka.Records Clear
+ Sleep ${collect_interval}
+ ${Kafka_Records}= kafka.Records Get voltha.events
+ [return] ${Kafka_Records}
+
+Get Title From Metadata
+ [Documentation] Delivers the title of pm data from metadata
+ [Arguments] ${metadata}
+ ${title}= Get From Dictionary ${metadata} title
+ [return] ${title}
+
+Get Device_Id From Metadata
+ [Documentation] Delivers the device-id of pm data from metadata
+ [Arguments] ${metadata}
+ ${device_id}= Get From Dictionary ${metadata} device_id
+ [return] ${device_id}
+
+Get Timestamp From Metadata
+ [Documentation] Delivers the device-id of pm data from metadata
+ [Arguments] ${metadata}
+ ${timestamp}= Get From Dictionary ${metadata} ts
+ [return] ${timestamp}
+
+Get Slice Data From Event
+ [Documentation] Delivers the slice data of pm data from event
+ [Arguments] ${event}
+ ${kpi_event2}= Get From Dictionary ${event} kpi_event2
+ ${slice_data}= Get From Dictionary ${kpi_event2} slice_data
+ [return] ${slice_data}
+
+Validate Raised Timestamp
+ [Documentation] Validates raisedTs with kpi_event2 ts
+ ... It is assumed kpi_event2 ts and raisedTs from event header should be the same
+ [Arguments] ${event}
+ ${raisedTs}= Get From Dictionary ${event['header']['raised_ts']} seconds
+ ${reportedTs}= Get From Dictionary ${event['header']['reported_ts']} seconds
+ ${kpiEvent2Ts}= Get From Dictionary ${event['kpi_event2']} ts
+ # ${raisedTs} and ${reportedTs} looks like '2021-04-28 06:56:54.000000', convert it to seconds of epoch
+ ${raisedTsSec}= Convert Date ${raisedTs} result_format=epoch exclude_millis=yes
+ ${reportedTsSec}= Convert Date ${reportedTs} result_format=epoch exclude_millis=yes
+ Run Keyword And Continue On Failure Should Be Equal ${raisedTsSec} ${kpiEvent2Ts}
+ ${result}= utility.validate ${raisedTsSec} <= ${reportedTsSec}
+ ${msg}= Catenate raisedTs (${raisedTs}) must be earlier or equal than reportedTs (${reportedTs})!
+ Run Keyword Unless ${result} Run Keyword And Continue On Failure FAIL ${msg}
+
+Validate Slice Data
+ [Documentation] Validates passed slice data
+ [Arguments] ${slice} ${metric_records}
+ ${result}= Set Variable True
+ ${checked}= Set Variable False
+ ${device_id}= Set Variable ${EMPTY}
+ ${title}= Set Variable ${EMPTY}
+ ${SliceLength}= Get Length ${slice}
+ Validate Slice Metrics Integrity ${slice}
+ FOR ${Index} IN RANGE 0 ${SliceLength}
+ ${metadata}= Get From Dictionary ${slice[${Index}]} metadata
+ ${metrics}= Get From Dictionary ${slice[${Index}]} metrics
+ ${title}= Get Title From Metadata ${metadata}
+ ${device_id}= Get Device_Id From Metadata ${metadata}
+ Continue For Loop If not '${device_id}' in ${METRIC_DICT}
+ ${timestamp}= Get Timestamp From Metadata ${metadata}
+ ${prevSliceIndex}= Run Keyword If 'PreviousRecord' in ${METRIC_DICT['${device_id}']['MetricData']['${title}']}
+ ... Get Previous Slice Index ${device_id} ${title} ${metric_records} ${metrics}
+ ... ELSE Set Variable 0
+ Continue For Loop If ${prevSliceIndex}==-1
+ Run Keyword If 'PreviousRecord' in ${METRIC_DICT['${device_id}']['MetricData']['${title}']}
+ ... Validate Timestamp ${device_id} ${title} ${timestamp} ${metric_records} ${prevSliceIndex}
+ ${hasprev}= Set Variable If 'PreviousRecord' in ${METRIC_DICT['${device_id}']['MetricData']['${title}']}
+ ... True False
+ Validate Metrics Data ${device_id} ${title} ${metrics} ${hasprev} ${metric_records} ${prevSliceIndex}
+ Validate Completeness of Metrics Data ${device_id} ${title} ${metrics}
+ ${checked}= Set Variable True
+ END
+ # increase number of checks, only once per slice
+ Run Keyword If ${checked} Increase Number of Checks ${device_id} ${title}
+
+Validate Slice Metrics Integrity
+ [Documentation] Valitdates the inegrity of the passed slice.
+ ... The pair 'entity_id' and 'class_id' has tor appear only once per slice!
+ ... In case of metric group UNI_Status parameter 'uni_port_no' appends to check!
+ [Arguments] ${slice}
+ ${prev_values} Create List
+ ${SliceLength}= Get Length ${slice}
+ FOR ${Index} IN RANGE 0 ${SliceLength}
+ ${metadata}= Get From Dictionary ${slice[${Index}]} metadata
+ ${metrics}= Get From Dictionary ${slice[${Index}]} metrics
+ ${title}= Get Title From Metadata ${metadata}
+ # get entity-id and class_id if available
+ # class_id identifier differs in case of metric group UNI_Status, 'me_class_id' instead simple 'class_id'
+ ${class_id_name}= Run Keyword If '${title}'=='UNI_Status' Set Variable me_class_id
+ ... ELSE Set Variable class_id
+ Continue For Loop If not 'entity_id' in ${metrics}
+ Continue For Loop If not '${class_id_name}' in ${metrics}
+ ${entity_id}= Get From Dictionary ${metrics} entity_id
+ ${class_id}= Get From Dictionary ${metrics} ${class_id_name}
+ # additional handling for metric group UNI_Status, uni_port_no has to be matched too
+ ${uni_port_no}= Run Keyword If '${title}'=='UNI_Status' Get From Dictionary ${metrics} uni_port_no
+ ... ELSE Set Variable ${Index}
+ ${current_values}= Create Dictionary entity_id=${entity_id} class_id=${class_id} uni_port_no=${uni_port_no}
+ Run Keyword And Continue On Failure Should Not Contain ${prev_values} ${current_values}
+ Append To List ${prev_values} ${current_values}
+ END
+
+Get Previous Slice Index
+ [Documentation] Delivers the slice index of previous metrics.
+ ... Previous slice index will be identified by matching entity_ids.
+ ... In case of UNI_Status the me_class_id has to be matched too!
+ [Arguments] ${device_id} ${title} ${metric_records} ${metrics}
+ ${prevSliceIndex}= Set Variable 0
+ # get entity-id and class_id if available
+ # class_id identifier differs in case of metric group UNI_Status, 'me_class_id' instead simple 'class_id'
+ ${class_id_name}= Run Keyword If '${title}'=='UNI_Status' Set Variable me_class_id
+ ... ELSE Set Variable class_id
+ Return From Keyword If not 'entity_id' in ${metrics} ${prevSliceIndex}
+ Return From Keyword If not '${class_id_name}' in ${metrics} ${prevSliceIndex}
+ ${entity_id}= Get From Dictionary ${metrics} entity_id
+ ${class_id}= Get From Dictionary ${metrics} ${class_id_name}
+ # get previous entity-id
+ ${prev_index}= Set Variable ${METRIC_DICT['${device_id}']['MetricData']['${title}']['PreviousRecord']}
+ ${pre_record}= Set Variable ${metric_records[${prev_index}]}
+ ${message}= Get From Dictionary ${pre_record} message
+ ${event}= volthatools.Events Decode Event ${message} return_default=true
+ ${prev_slice}= Get Slice Data From Event ${event}
+ ${prevSliceLength}= Get Length ${prev_slice}
+ ${matched}= Set Variable False
+ FOR ${Index} IN RANGE 0 ${prevSliceLength}
+ ${prevmetrics}= Get From Dictionary ${prev_slice[${Index}]} metrics
+ ${prev_entity_id}= Get From Dictionary ${prevmetrics} entity_id
+ ${prev_class_id}= Get From Dictionary ${prevmetrics} ${class_id_name}
+ ${matched}= Set Variable If (${entity_id}==${prev_entity_id})and(${${class_id}}==${prev_class_id}) True False
+ ${prevSliceIndex}= Set Variable If ${matched} ${Index} -1
+ Exit For Loop If ${matched}
+ END
+ Run Keyword And Continue On Failure Run Keyword Unless ${matched} FAIL
+ ... Could not find previous metrics for ${title} entity_id ${entity_id} of device ${device_id}!
+ [return] ${prevSliceIndex}
+
+Validate Timestamp
+ [Documentation] Validates passed timestamp with timestamp of previous metrics
+ [Arguments] ${device_id} ${title} ${timestamp} ${metric_records} ${prevSliceIndex}
+ # get previous timestamp
+ ${prev_timestamp}= Get Previous Timestamp ${METRIC_DICT['${device_id}']['MetricData']['${title}']['PreviousRecord']}
+ ... ${metric_records} ${prevSliceIndex}
+ ${interval}= Get From Dictionary ${METRIC_DICT['${device_id}']['MetricData']['${title}']['Intervals']}
+ ... current
+ ${interval}= Convert To Integer ${interval}
+ ${check_value}= Evaluate abs(${prev_timestamp}+${interval}-${timestamp})
+ Run Keyword And Continue On Failure Run Keyword Unless ${0} <= ${check_value} <= ${4} FAIL
+ ... Wrong interval for ${title} of device ${device_id}!
+
+Get Validation Operation
+ [Documentation] Delivers the stored validation operation of passed metrics
+ [Arguments] ${dev_id} ${title} ${item} ${has_previous}
+ ${w_wo_prev}= Set Variable If ${has_previous} successor first
+ ${validation_operator}= Get From Dictionary
+ ... ${METRIC_DICT['${dev_id}']['MetricData']['${title}']['GroupMetrics']['${item}']['ValidationOperation']['${w_wo_prev}']}
+ ... operator
+ ${validation_operand}= Get From Dictionary
+ ... ${METRIC_DICT['${dev_id}']['MetricData']['${title}']['GroupMetrics']['${item}']['ValidationOperation']['${w_wo_prev}']}
+ ... operand
+ [return] ${validation_operator} ${validation_operand}
+
+Get Previous Value
+ [Documentation] Delivers the previous value
+ [Arguments] ${device_id} ${title} ${item} ${metric_records} ${prevSliceIndex}
+ ${prev_index}= Set Variable ${METRIC_DICT['${device_id}']['MetricData']['${title}']['PreviousRecord']}
+ ${pre_record}= Set Variable ${metric_records[${prev_index}]}
+ ${message}= Get From Dictionary ${pre_record} message
+ ${event}= volthatools.Events Decode Event ${message} return_default=true
+ ${slice}= Get Slice Data From Event ${event}
+ ${metrics}= Get From Dictionary ${slice[${prevSliceIndex}]} metrics
+ ${prev_value}= Get From Dictionary ${metrics} ${item}
+ [return] ${prev_value}
+
+Validate Metrics Data
+ [Documentation] Validates passed metrics
+ [Arguments] ${device_id} ${title} ${metrics} ${has_previous} ${metric_records} ${prevSliceIndex}
+ FOR ${item} IN @{metrics.keys()}
+ ${operation} ${validation_value}= Get Validation Operation ${device_id} ${title} ${item} ${has_previous}
+ # get previous value in case of ${has_previous}==True and ${validation_value}==previous
+ ${validation_value}= Run Keyword If ${has_previous} and '${validation_value}'=='previous' Get Previous Value
+ ... ${device_id} ${title} ${item} ${metric_records} ${prevSliceIndex}
+ ... ELSE Set Variable ${validation_value}
+ ${current_value}= Get From Dictionary ${metrics} ${item}
+ ${result}= utility.validate ${current_value} ${operation} ${validation_value}
+ ${msg}= Catenate Received value (${current_value}) from device (${device_id}) of group (${title}) for '${item}'
+ ... does not match!
+ ... Expected: <value> ${operation} ${validation_value}
+ Run Keyword Unless ${result} Run Keyword And Continue On Failure FAIL ${msg}
+ END
+
+Validate Precondition for Availability
+ [Documentation] Validates passed metrics for stored precondition
+ [Arguments] ${device_id} ${title} ${metrics} ${item}
+ ${precond}= Get From Dictionary ${METRIC_DICT['${device_id}']['MetricData']['${title}']['GroupMetrics']['${item}']}
+ ... Precondition
+ ${operation}= Get From Dictionary ${precond} operator
+ ${validation_value}= Get From Dictionary ${precond} operand
+ ${element}= Get From Dictionary ${precond} element
+ ${current_value}= Get From Dictionary ${metrics} ${element}
+ ${result}= utility.validate ${current_value} ${operation} ${validation_value}
+ [return] ${result}
+
+Validate Completeness of Metrics Data
+ [Documentation] Validates passed metrics of completness
+ [Arguments] ${device_id} ${title} ${metrics}
+ # get validation data
+ ${validation_data}= Set Variable ${METRIC_DICT['${device_id}']['MetricData']['${title}']['GroupMetrics']}
+ FOR ${item} IN @{validation_data.keys()}
+ ${precondfullfilled}= Run Keyword If
+ ... 'Precondition' in ${METRIC_DICT['${device_id}']['MetricData']['${title}']['GroupMetrics']['${item}']}
+ ... Validate Precondition for Availability ${device_id} ${title} ${metrics} ${item}
+ ... ELSE Set Variable True
+ Run Keyword If (not '${item}' in ${metrics}) and ${precondfullfilled} Run Keyword And Continue On Failure FAIL
+ ... Missing metric (${item}) from device (${device_id}) of group (${title})
+ END
+
+Get Previous Timestamp
+ [Documentation] Deliveres the timestamp of the passed metrics record
+ [Arguments] ${prev_index} ${metric_records} ${prevSliceIndex}
+ ${metric}= Set Variable ${metric_records[${prev_index}]}
+ ${message}= Get From Dictionary ${metric} message
+ ${event}= volthatools.Events Decode Event ${message} return_default=true
+ ${slice}= Get Slice Data From Event ${event}
+ ${metadata}= Get From Dictionary ${slice[${prevSliceIndex}]} metadata
+ ${timestamp}= Get Timestamp From Metadata ${metadata}
+ [return] ${timestamp}
+
+Set Previous Record
+ [Documentation] Sets the previous record in METRIC_DICT for next validation
+ [Arguments] ${Index} ${slice}
+ # use first slice for further handling
+ ${metadata}= Get From Dictionary ${slice[${0}]} metadata
+ ${title}= Get Title From Metadata ${metadata}
+ ${device_id}= Get Device_Id From Metadata ${metadata}
+ Return From Keyword If not '${device_id}' in ${METRIC_DICT}
+ ${metric}= Get From Dictionary ${METRIC_DICT['${device_id}']['MetricData']} ${title}
+ Set To Dictionary ${metric} PreviousRecord=${Index}
+ Set To Dictionary ${METRIC_DICT['${device_id}']['MetricData']} ${title}=${metric}
+
+Increase Number of Checks
+ [Documentation] Increases the NumberOfChecks value in METRIC_DICT for passed device id and title
+ [Arguments] ${device_id} ${title}
+ ${checks}= Get From Dictionary ${METRIC_DICT['${device_id}']['MetricData']['${title}']} NumberOfChecks
+ ${checks}= evaluate ${checks} + 1
+ Set To Dictionary ${METRIC_DICT['${device_id}']['MetricData']['${title}']} NumberOfChecks=${checks}
+
+Validate Number of Checks
+ [Documentation] Validates the NumberOfChecks value in METRIC_DICT, must be at least >=2
+ [Arguments] ${collect_interval} ${user}=False ${group}=${EMPTY}
+ ${onu_list} Create List
+ FOR ${INDEX} IN RANGE 0 ${num_all_onus}
+ ${onu_device_id}= Get Device ID From SN ${hosts.src[${INDEX}].onu}
+ ${onu_id}= Get Index From List ${onu_list} ${onu_device_id}
+ Continue For Loop If -1 != ${onu_id}
+ ${list}= Prepare Group Interval List per Device Id ${onu_device_id} ${user} ${group}
+ Validate Number of Checks per Onu ${onu_device_id} ${list} ${collect_interval}
+ END
+
+Validate Number of Checks per Onu
+ [Documentation] Validates the NumberOfChecks value per ONU, must be at least >=2
+ ... Collecting of metrics will be calculated that at least each group has to be checked twice!
+ ... Correct value per group and its interval will be calculated!
+ [Arguments] ${device_id} ${list} ${collect_interval}
+ FOR ${Item} IN @{list}
+ ${group}= Get From Dictionary ${Item} group
+ ${val}= Get From Dictionary ${Item} interval
+ # use interval value to skip groups, which should not be checked!
+ Continue For Loop If '${val}'=='-1'
+ ${checks}= Get From Dictionary ${METRIC_DICT['${device_id}']['MetricData']['${group}']} NumberOfChecks
+ # remove time unit if available
+ ${collect_interval}= Validate Time Unit ${collect_interval} False
+ ${expected_checks}= evaluate ${collect_interval}/${val}
+ # remove float format (Validate Time Unit will this done:-))
+ ${expected_checks}= Validate Time Unit ${expected_checks} False
+ Run Keyword And Continue On Failure Run Keyword Unless ${expected_checks} <= ${checks} FAIL
+ ... Wrong number of pm-data (${checks}) for ${group} of device ${device_id}!
+ END
+
+#################################################################
+# Post test keywords
+#################################################################
+
+Remove Previous Record
+ [Documentation] Removes the previous record in METRIC_DICT
+ [Arguments] ${device_id} ${title}
+ Remove From Dictionary ${METRIC_DICT['${device_id}']['MetricData']['${title}']} PreviousRecord
+
+Reset Number Of Checks
+ [Documentation] Removes the previous record in METRIC_DICT
+ [Arguments] ${device_id} ${title}
+ Set To Dictionary ${METRIC_DICT['${device_id}']['MetricData']['${title}']} NumberOfChecks=0
+
+Clean Metric Dictionary per Onu
+ [Documentation] Cleans METRIC_DICT per onu device id
+ [Arguments] ${device_id}
+ FOR ${Item} IN @{METRIC_DICT['${device_id}']['GroupList']}
+ Remove Previous Record ${device_id} ${Item}
+ Reset Number Of Checks ${device_id} ${Item}
+ END
+
+Clean Metric Dictionary
+ [Documentation] Cleans METRIC_DICT
+ ${onu_list} Create List
+ FOR ${INDEX} IN RANGE 0 ${num_all_onus}
+ ${onu_device_id}= Get Device ID From SN ${hosts.src[${INDEX}].onu}
+ ${onu_id}= Get Index From List ${onu_list} ${onu_device_id}
+ Continue For Loop If -1 != ${onu_id}
+ Clean Metric Dictionary per Onu ${onu_device_id}
+ END
+ log ${METRIC_DICT}
+
+
+#################################################################
+# Helper keywords
+#################################################################
+
+Validate Time Unit
+ [Documentation] Converts the passed value in seconds and return it w/o unit
+ ... Conversion to string is needed to remove float format!
+ [Arguments] ${val} ${unit}=True
+ ${seconds}= Convert Time ${val}
+ ${seconds}= Convert To String ${seconds}
+ ${seconds}= Get Substring ${seconds} 0 -2
+ ${seconds}= Set Variable If ${unit} ${seconds}s ${seconds}
+ [return] ${seconds}
diff --git a/libraries/utility.py b/libraries/utility.py
index 6faec6e..9da6a98 100755
--- a/libraries/utility.py
+++ b/libraries/utility.py
@@ -17,6 +17,7 @@
from __future__ import print_function
import inspect
import os
+import operator
# global definition of keys (find in given 'inventory_data')
_NAME = 'name'
@@ -103,3 +104,35 @@
def decode(data):
decoded_data = data
print(unique(), str(decoded_data))
+
+
+# Compares two values using a given operator. The values are converted to float first so that
+# numbers as strings are also accepted. Returns True or False.
+# operator: ==, !=, <, <=, >, >=
+# Example:
+# | ${result} | Compare | 100 | > | 5 | # True |
+def compare(value1, op, value2):
+ ops = {"==": operator.eq,
+ "!=": operator.ne,
+ "<": operator.lt,
+ "<=": operator.le,
+ ">": operator.gt,
+ ">=": operator.ge}
+ return ops[op](float(value1), float(value2))
+
+
+# Validates two values using a given operator.
+# The values are converted to float first so that numbers as strings are also accepted.
+# Second value has to be a list in case of operator is 'in' or 'range'
+# Returns True or False.
+# operator: in, range, ==, !=, <, <=, >, >=
+# Example:
+# | ${result} | validate | 100 | > | 5 | # True |
+# | ${result} | validate | 11 | in | ['11','264','329'] | # True |
+# | ${result} | validate | 1 | range | ['0','1'] | # True |
+def validate(value1, op, value2):
+ if op == "in":
+ return (float(value1) in [float(i) for i in value2])
+ if op == "range":
+ return ((compare(value1, ">=", value2[0])) and (compare(value1, "<=", value2[1])))
+ return compare(value1, op, value2)
diff --git a/libraries/voltctl.robot b/libraries/voltctl.robot
index 79c61e3..4df47bf 100755
--- a/libraries/voltctl.robot
+++ b/libraries/voltctl.robot
@@ -833,3 +833,103 @@
Should Be Equal '${downloadstate}' '${download_state}' ONU Device ${id} Image downloadState does not match
Should Be Equal '${imagestate}' '${image_state}' ONU Device ${id} Image imageState does not match
Should Be Equal '${reason}' '${expected_reason}' ONU Device ${id} Image reason does not match
+
+
+# pm-data relevant keywords
+Read Default Interval From Pmconfig
+ [Documentation] Reads default interval from pm config
+ [Arguments] ${device_id}
+ ${rc} ${result}= Run and Return Rc and Output voltctl device pmconfig get ${device_id}
+ Should Be Equal As Integers ${rc} 0
+ log ${result}
+ @{words}= Split String ${result}
+ ${interval}= Get From List ${words} 3
+ log ${interval}
+ [return] ${interval}
+
+Read Group Interval From Pmconfig
+ [Documentation] Reads default interval from pm config
+ [Arguments] ${device_id} ${group}
+ ${rc} ${result}= Run and Return Rc and Output voltctl device pmconfig group list ${device_id} | grep ${group}
+ Should Be Equal As Integers ${rc} 0
+ log ${result}
+ @{words}= Split String ${result}
+ ${interval}= Get From List ${words} -1
+ log ${interval}
+ [return] ${interval}
+
+Set and Validate Default Interval
+ [Documentation] Sets and validates default interval of pm data
+ [Arguments] ${device_id} ${interval}
+ ${rc} ${result}= Run and Return Rc and Output voltctl device pmconfig frequency set ${device_id} ${interval}
+ Should Be Equal As Integers ${rc} 0
+ log ${result}
+ # workaround until unit will be printed out in voltctl - remove unit
+ ${interval}= Get Substring ${interval} 0 -1
+ Should Contain ${result} ${interval}
+
+Set and Validate Group Interval
+ [Documentation] Sets and validates group interval of pm data
+ [Arguments] ${device_id} ${interval} ${group}
+ ${rc} ${result}= Run and Return Rc and Output voltctl device pmconfig group set ${device_id} ${group} ${interval}
+ Should Be Equal As Integers ${rc} 0
+ ${rc} ${result}= Run and Return Rc and Output voltctl device pmconfig group list ${device_id} | grep ${group}
+ Should Be Equal As Integers ${rc} 0
+ log ${result}
+ # workaround until unit will be printed out in voltctl - remove unit
+ ${interval}= Get Substring ${interval} 0 -1
+ Should Contain ${result} ${interval}
+
+Read Group List
+ [Documentation] Reads metric group list of given device
+ [Arguments] ${device_id}
+ ${rc} ${result}= Run and Return Rc and Output voltctl device pmconfig group list ${device_id} | grep -v GROUPNAME
+ Should Be Equal As Integers ${rc} 0
+ ${group_list} Create List
+ ${interval_dict} Create Dictionary
+ @{output}= Split String ${result} \n
+ FOR ${Line} IN @{output}
+ @{words}= Split String ${Line}
+ ${group}= Set Variable ${words[0]}
+ ${interval}= Set Variable ${words[2]}
+ Append To List ${group_list} ${group}
+ Set To Dictionary ${interval_dict} ${group}=${interval}
+ END
+ [return] ${group_list} ${interval_dict}
+
+Read Group Metric List
+ [Documentation] Reads group metric list of given device and group
+ [Arguments] ${device_id} ${group}
+ ${cmd}= Catenate voltctl device pmconfig groupmetric list ${device_id} ${group} | grep -v SAMPLEFREQ
+ ${rc} ${result}= Run and Return Rc and Output ${cmd}
+ Should Be Equal As Integers ${rc} 0
+ ${groupmetric_list} Create List
+ @{output}= Split String ${result} \n
+ FOR ${Line} IN @{output}
+ @{words}= Split String ${Line}
+ ${name}= Set Variable ${words[0]}
+ ${type}= Set Variable ${words[1]}
+ ${enabled}= Set Variable ${words[2]}
+ ${subdict}= Create Dictionary type=${type} enabled=${enabled}
+ ${dict}= Create Dictionary ${name}=${subdict}
+ Append To List ${groupmetric_list} ${dict}
+ END
+ [return] ${groupmetric_list}
+
+Read Group Metric Dict
+ [Documentation] Reads group metric list of given device and group
+ [Arguments] ${device_id} ${group}
+ ${cmd}= Catenate voltctl device pmconfig groupmetric list ${device_id} ${group} | grep -v SAMPLEFREQ
+ ${rc} ${result}= Run and Return Rc and Output ${cmd}
+ Should Be Equal As Integers ${rc} 0
+ ${groupmetric_dict} Create Dictionary
+ @{output}= Split String ${result} \n
+ FOR ${Line} IN @{output}
+ @{words}= Split String ${Line}
+ ${name}= Set Variable ${words[0]}
+ ${type}= Set Variable ${words[1]}
+ ${enabled}= Set Variable ${words[2]}
+ ${subdict}= Create Dictionary type=${type} enabled=${enabled}
+ Set To Dictionary ${groupmetric_dict} ${name}=${subdict}
+ END
+ [return] ${groupmetric_dict}
diff --git a/requirements.txt b/requirements.txt
old mode 100644
new mode 100755
index 39b5499..b53f166
--- a/requirements.txt
+++ b/requirements.txt
@@ -13,6 +13,9 @@
virtualenv
matplotlib==3.0.3
requests==2.24.0
-grpc-robot>=2.0.0
-kafka-robot>=2.0.0
-device-management-interface>=0.9.4
\ No newline at end of file
+grpc-robot>=2.7.0
+kafka-robot>=2.3.2
+voltha-protos>=4.0.16
+protobuf>=3.15.7
+protobuf3-to-dict>=0.1.5
+device-management-interface>=0.13.0
\ No newline at end of file
diff --git a/tests/data/pm-data.yaml b/tests/data/pm-data.yaml
new file mode 100755
index 0000000..2203531
--- /dev/null
+++ b/tests/data/pm-data.yaml
@@ -0,0 +1,72 @@
+---
+
+# Copyright 2021-present Open Networking Foundation
+#
+# 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.
+
+# Automated deployment configuration for kind-voltha running BBSim
+
+# Pm user validation and precondition data for validation of received
+# pm-data elements from open-onu-go-adapter.
+# This file contains special validation data, which differs from default
+# validation data.
+# Following operators are valid: in, range, ==, !=, <, <=, >, >=
+# In case of operator is 'in' or 'range' second value has to be a list (array)
+
+pm_user_validation_data:
+ UNI_Status:
+ me_class_id:
+ firstoperator: in
+ firstvalue:
+ - 11
+ - 264
+ - 329
+ successoroperator: ==
+ successorvalue: previous
+ oper_status:
+ firstoperator: range
+ firstvalue:
+ - 0
+ - 1
+ successoroperator: range
+ successorvalue:
+ - 0
+ - 1
+ uni_admin_state:
+ firstoperator: range
+ firstvalue:
+ - 0
+ - 1
+ successoroperator: range
+ successorvalue:
+ - 0
+ - 1
+ sensed_type:
+ firstoperator: range
+ firstvalue:
+ - 0
+ - 255
+ successoroperator: ==
+ successorvalue: previous
+pm_user_precondition_data:
+ UNI_Status:
+ sensed_type:
+ operator: ==
+ value: 11
+ precondelement: me_class_id
+ oper_status:
+ operator: in
+ value:
+ - 11
+ - 329
+ precondelement: me_class_id
diff --git a/tests/pm-data/Voltha_ONUPMTests.robot b/tests/pm-data/Voltha_ONUPMTests.robot
new file mode 100755
index 0000000..1623557
--- /dev/null
+++ b/tests/pm-data/Voltha_ONUPMTests.robot
@@ -0,0 +1,215 @@
+# Copyright 2021 - present Open Networking Foundation
+#
+# 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 Test of open ONU go adapter PM data
+... in case of kafka pod runs in k8s cluster - kafka has to deploy with following EXTRA_HELM_FLAGS
+... --set externalAccess.enabled=true,
+... --set externalAccess.service.type=NodePort,
+... --set externalAccess.service.nodePorts[0]=${KAFKA_PORT},
+... --set externalAccess.service.domain=${KAFKA_IP}
+... with e.g. service.domain=10.0.02.15 or 127.0.0.1 and service.nodePorts[0]=30201!
+Suite Setup Setup Suite
+Suite Teardown Teardown Suite
+Test Setup Setup
+Test Teardown Teardown
+Library Collections
+Library String
+Library OperatingSystem
+Library XML
+Library RequestsLibrary
+Library ../../libraries/DependencyLibrary.py
+Resource ../../libraries/onos.robot
+Resource ../../libraries/voltctl.robot
+Resource ../../libraries/voltha.robot
+Resource ../../libraries/utils.robot
+Resource ../../libraries/k8s.robot
+Resource ../../libraries/onu_utilities.robot
+Resource ../../libraries/bbsim.robot
+Resource ../../libraries/pm_utilities.robot
+Resource ../../variables/variables.robot
+
+Library kafka_robot.KafkaClient log_level=DEBUG WITH NAME kafka
+Library grpc_robot.VolthaTools WITH NAME volthatools
+
+
+*** Variables ***
+${namespace} voltha
+${timeout} 60s
+${of_id} 0
+${logical_id} 0
+${has_dataplane} True
+${external_libs} True
+${teardown_device} True
+${scripts} ../../scripts
+# Per-test logging on failure is turned off by default; set this variable to enable
+${container_log_dir} ${None}
+
+# logging flag to enable Collect Logs, can be passed via the command line too
+# example: -v logging:True
+${logging} False
+# determines the environment workflow: DT, TT or ATT (default)
+# example: -v workflow:DT
+${workflow} ATT
+# when voltha is running in k8s port forwarding is needed
+# example: -v PORT_FORWARDING:False
+${PORT_FORWARDING} True
+# kafka ip e.g. ip of master host where k8s is running
+# example: -v KAFKA_IP:10.0.2.15
+${KAFKA_IP} 127.0.0.1
+# kafka port: port of kafka nodeport
+# example: -v KAFKA_PORT:30201
+${KAFKA_PORT} 30201
+# kafka service port: service port of kafka nodeport
+# example: -v KAFKA_SVC_PORT:9094
+${KAFKA_SVC_PORT} 9094
+# onu pm data default interval
+# example: -v ONU_DEFAULT_INTERVAL:50s
+${ONU_DEFAULT_INTERVAL} 300s
+# onu pm data group PON_Optical interval
+# example: -v ONU_PON_OPTICAL_INTERVAL:50s
+${ONU_PON_OPTICAL_INTERVAL} 35s
+# onu pm data group UNI_Status interval
+# example: -v ONU_UNI_STATUS_INTERVAL:50s
+${ONU_UNI_STATUS_INTERVAL} 20s
+
+*** Test Cases ***
+Check Default Metrics All ONUs
+ [Documentation] Validates the ONU Go adapter pm date resp. Metrics with dafault values
+ [Tags] functional CheckDefaultMetricsAllOnus
+ [Setup] Start Logging CheckDefaultMetricsAllOnus
+ ${collect_interval}= Determine Collection Interval
+ Collect and Validate PM Data ${collect_interval}
+ [Teardown] Run Keywords Clean Metric Dictionary
+ ... AND Run Keyword If ${logging} Collect Logs
+ ... AND Stop Logging CheckDefaultMetricsAllOnus
+
+Check User Onu Metrics
+ [Documentation] Validates the ONU Go adapter pm date resp. Metrics with user values
+ ... Currently only the intvals of metric groups UNI_Status and PON_Optical will be set to user values.
+ [Tags] functional CheckUserOnuMetrics
+ [Setup] Start Logging CheckUserOnuMetrics
+ # set user values for intervals
+ Set Group Interval All Onu UNI_Status ${ONU_UNI_STATUS_INTERVAL}
+ Set Group Interval All Onu PON_Optical ${ONU_PON_OPTICAL_INTERVAL}
+ ${collect_interval}= Determine Collection Interval user=True
+ # activate user interval values
+ Activate And Validate Interval All Onu user=True
+ Collect and Validate PM Data ${collect_interval} user=True
+ # (re-)activate default interval values
+ Set Group Interval All Onu UNI_Status -1
+ Set Group Interval All Onu PON_Optical -1
+ Activate And Validate Interval All Onu
+ [Teardown] Run Keywords Clean Metric Dictionary
+ ... AND Run Keyword If ${logging} Collect Logs
+ ... AND Stop Logging CheckUserOnuMetrics
+
+Check User Onu Metrics Disabled Device
+ [Documentation] Validates the ONU Go adapter pm date resp. Metrics with user values for disabled device
+ ... Currently only the intvals of metric groups UNI_Status will be set to user values and validated.
+ ... First enable status will be validated, then all devices will be disabled and status will be validated.
+ [Tags] functional CheckUserOnuMetricsDisabledDevice
+ [Setup] Start Logging CheckUserOnuMetricsDisabledDevice
+ # set user values for intervals
+ Set Group Interval All Onu UNI_Status ${ONU_UNI_STATUS_INTERVAL}
+ ${collect_interval}= Determine Collection Interval user=True
+ # activate user interval values
+ Activate And Validate Interval All Onu user=True
+ # read and store currents validation data
+ ${group}= Set Variable UNI_Status
+ ${oper_state}= Set Variable oper_status
+ ${admin_state}= Set Variable uni_admin_state
+ ${prev_validation_data_oper_state}= Get Validation Operation All Onu ${group} ${oper_state}
+ ${prev_validation_data_admin_state}= Get Validation Operation All Onu ${group} ${admin_state}
+ # change the validation data for oper_status and uni_admin_state of metric group UNI_Status
+ ${enable}= Set Variable 0
+ ${disable}= Set Variable 1
+ ${enabled_check}= Create Dictionary operator=${eq} operand=${enable}
+ ${disabled_check}= Create Dictionary operator=${eq} operand=${disable}
+ ${ValidationEnabled}= Create Dictionary first=${enabled_check} successor=${enabled_check}
+ ${ValidationDisabled}= Create Dictionary first=${disabled_check} successor=${disabled_check}
+ # validate enabled status
+ Set Validation Operation All Onu ${group} ${oper_state} ${ValidationEnabled}
+ Set Validation Operation All Onu ${group} ${admin_state} ${ValidationEnabled}
+ Collect and Validate PM Data ${collect_interval} user=True
+ Clean Metric Dictionary
+ # validate disabled status
+ Set Validation Operation All Onu ${group} ${oper_state} ${ValidationDisabled}
+ Set Validation Operation All Onu ${group} ${admin_state} ${ValidationDisabled}
+ # disable (all) onu devices
+ Disable Onu Device
+ ${alternativeonustates}= Create List omci-flows-deleted tech-profile-config-delete-success
+ Current State Test All Onus omci-admin-lock alternativeonustate=${alternativeonustates}
+ Log Ports
+ #check no port is enabled in ONOS
+ Wait for Ports in ONOS for all OLTs ${onos_ssh_connection} 0 BBSM
+ Collect and Validate PM Data ${collect_interval} user=True
+ Clean Metric Dictionary
+ # enable (all) onu devices
+ Enable Onu Device
+ ${alternativeonustates}= Create List onu-reenabled
+ Current State Test All Onus omci-flows-pushed alternativeonustate=${alternativeonustates}
+ Log Ports onlyenabled=True
+ #check that all the UNI ports show up in ONOS again
+ Wait for Ports in ONOS for all OLTs ${onos_ssh_connection} ${num_all_onus} BBSM determine_number=True
+ # validate enabled status (again)
+ Set Validation Operation All Onu ${group} ${oper_state} ${ValidationEnabled}
+ Set Validation Operation All Onu ${group} ${admin_state} ${ValidationEnabled}
+ Collect and Validate PM Data ${collect_interval} user=True
+ # (re-)set previous validation data
+ Set Validation Operation Passed Onu ${group} ${oper_state} ${prev_validation_data_oper_state}
+ Set Validation Operation Passed Onu ${group} ${admin_state} ${prev_validation_data_admin_state}
+ # (re-)activate default interval values
+ Set Group Interval All Onu UNI_Status -1
+ Activate And Validate Interval All Onu
+ [Teardown] Run Keywords Clean Metric Dictionary
+ ... AND Run Keyword If ${logging} Collect Logs
+ ... AND Stop Logging CheckUserOnuMetricsDisabledDevice
+
+
+*** Keywords ***
+Setup Suite
+ [Documentation] Set up the test suite
+ Common Test Suite Setup
+ ${onos_ssh_connection} Open ONOS SSH Connection ${ONOS_SSH_IP} ${ONOS_SSH_PORT}
+ Set Suite Variable ${onos_ssh_connection}
+ ${switch_type}= Get Variable Value ${web_power_switch.type}
+ Run Keyword If "${switch_type}"!="" Set Global Variable ${powerswitch_type} ${switch_type}
+ # start port forwarding if needed (when voltha runs in k8s)
+ ${portFwdHandle} = Run Keyword If ${PORT_FORWARDING} Start Process
+ ... kubectl port-forward --address 0.0.0.0 --namespace default svc/kafka-0-external ${KAFKA_PORT}:${KAFKA_SVC_PORT} &
+ ... shell=true
+ Set Suite Variable ${portFwdHandle}
+ Sleep 5s
+ # open connection to read kafka bus
+ Wait Until Keyword Succeeds 3x 5s
+ ... kafka.Connection Open ${KAFKA_IP} ${KAFKA_PORT} voltha.events timestamp_from=0
+ # enable OLT(s) and bring up ONU(s)
+ Setup
+ Run Keyword If "${workflow}"=="DT" Perform Sanity Test DT
+ ... ELSE IF "${workflow}"=="TT" Perform Sanity Tests TT
+ ... ELSE Perform Sanity Test
+ # prepare pm data matrix for validation
+ ${METRIC_DICT}= Create Metric Dictionary
+ Set Suite Variable ${METRIC_DICT}
+
+Teardown Suite
+ [Documentation] tear down the test suite
+ # close connection to kafka
+ kafka.Connection Close
+ # stop port forwarding if started
+ Run Keyword If ${PORT_FORWARDING} Terminate Process ${portFwdHandle} kill=true
+ # call common suite teardown
+ utils.Teardown Suite
+ Close All ONOS SSH Connections