[VOL-3804] Test for BBSim

Change-Id: I5dbecebb8f876379b34e60cb56388ea681866a03
diff --git a/Makefile b/Makefile
index 3c815c6..a5da809 100755
--- a/Makefile
+++ b/Makefile
@@ -179,6 +179,20 @@
 sanity-single-kind: ROBOT_CONFIG_FILE := $(ROBOT_SANITY_SINGLE_PON_FILE)
 sanity-single-kind: bbsim-kind
 
+sanity-bbsim-att: ROBOT_MISC_ARGS += -v logging:True -v workflow:ATT
+sanity-bbsim-att: sanity-bbsim
+
+sanity-bbsim-dt: ROBOT_MISC_ARGS += -v logging:True -v workflow:DT
+sanity-bbsim-dt: sanity-bbsim
+
+sanity-bbsim-tt: ROBOT_MISC_ARGS += -v logging:True -v workflow:TT
+sanity-bbsim-tt: sanity-bbsim
+
+sanity-bbsim: ROBOT_MISC_ARGS += -i bbsimSanity $(ROBOT_DEBUG_LOG_OPT)
+sanity-bbsim: ROBOT_CONFIG_FILE := $(ROBOT_SANITY_SINGLE_PON_FILE)
+sanity-bbsim: ROBOT_FILE := Voltha_BBSimTests.robot
+sanity-bbsim: voltha-bbsim-test
+
 rwcore-restart-single-kind: ROBOT_MISC_ARGS += -X -i functionalANDrwcore-restart $(ROBOT_DEBUG_LOG_OPT)
 rwcore-restart-single-kind: ROBOT_CONFIG_FILE := $(ROBOT_FAIL_SINGLE_PON_FILE)
 rwcore-restart-single-kind: ROBOT_FILE := Voltha_FailureScenarios.robot
@@ -320,6 +334,10 @@
 	cd tests/openonu-go-adapter ;\
 	robot -V $(ROBOT_CONFIG_FILE) $(ROBOT_MISC_ARGS) $(ROBOT_FILE)
 
+voltha-bbsim-test: vst_venv
+	source ./$</bin/activate ; set -u ;\
+	cd tests/bbsim ;\
+	robot -V $(ROBOT_CONFIG_FILE) $(ROBOT_MISC_ARGS) $(ROBOT_FILE)
 
 # self-test, lint, and setup targets
 
diff --git a/libraries/bbsim.robot b/libraries/bbsim.robot
new file mode 100644
index 0000000..12ce3a6
--- /dev/null
+++ b/libraries/bbsim.robot
@@ -0,0 +1,59 @@
+# Copyright 2017-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.
+# onos common functions
+
+*** Settings ***
+Documentation     Library for BBSimCtl interactions
+Resource          ./k8s.robot
+
+*** Keywords ***
+List ONUs
+    [Documentation]  Lists ONUs via BBSimctl
+    [Arguments]    ${namespace}    ${bbsim_pod_name}
+    ${onus}    ${rc}=    Exec Pod And Return Output And RC    ${namespace}    ${bbsim_pod_name}
+    ...    bbsimctl onu list
+    Log     ${onus}
+    Should Be Equal as Integers    ${rc}    0
+
+Restart Auth
+    [Documentation]  Restart Authentication on a BBSim ONU
+    [Arguments]    ${namespace}    ${bbsim_pod_name}    ${onu}
+    ${res}     ${rc}=    Exec Pod And Return Output And RC    ${namespace}    ${bbsim_pod_name}
+    ...    bbsimctl onu auth_restart ${onu}
+    Log     ${res}
+    Should Be Equal as Integers    ${rc}    0
+
+Restart DHCP
+    [Documentation]  Restart Dhcp on a BBSim ONU
+    [Arguments]    ${namespace}    ${bbsim_pod_name}    ${onu}
+    ${res}     ${rc}=    Exec Pod And Return Output And RC    ${namespace}    ${bbsim_pod_name}
+    ...    bbsimctl onu dhcp_restart ${onu}
+    Log     ${res}
+    Should Be Equal as Integers    ${rc}    0
+
+List Service
+    [Documentation]  Lists Service via BBSimctl
+    [Arguments]    ${namespace}    ${bbsim_pod_name}
+    ${service}    ${rc}=    Exec Pod And Return Output And RC    ${namespace}    ${bbsim_pod_name}
+    ...    bbsimctl service list
+    Log     ${service}
+    Should Be Equal as Integers    ${rc}    0
+
+JoinOrLeave Igmp
+    [Documentation]  Joins or Leaves Igmp on a BBSim ONU
+    [Arguments]    ${namespace}    ${bbsim_pod_name}    ${onu}    ${task}    ${group_address}=224.0.0.22
+    ${res}    ${rc}=    Exec Pod And Return Output And RC    ${namespace}    ${bbsim_pod_name}
+    ...    bbsimctl onu igmp ${onu} ${task} ${group_address}
+    Log     ${res}
+    Should Be Equal as Integers    ${rc}    0
diff --git a/libraries/k8s.robot b/libraries/k8s.robot
index 213246e..c8cfd69 100644
--- a/libraries/k8s.robot
+++ b/libraries/k8s.robot
@@ -68,13 +68,19 @@
     [Documentation]    Uses kubectl to execute a command in the pod and return the output
     ${rc}    ${exec_pod_name}=    Run and Return Rc and Output
     ...    kubectl -n ${namespace} get pods -l app.kubernetes.io/name=${name} -o name
+
+Exec Pod And Return Output And RC
+    [Arguments]    ${namespace}    ${name}    ${command}
+    [Documentation]    Uses kubectl to execute a command in the pod and return the output
+    ${rc}    ${exec_pod_name}=    Run and Return Rc and Output
+    ...    kubectl get pods -n ${namespace} | grep ${name} | awk 'NR==1{print $1}'
+
     Log    ${exec_pod_name}
     Should Not Be Empty    ${exec_pod_name}    Unable to parse pod name
     ${rc}    ${output}=    Run and Return Rc and Output
     ...    kubectl exec -i ${exec_pod_name} -n ${namespace} -- ${command}
     Log    ${output}
-    [return]    ${output}
-
+    [return]    ${output}   ${rc}
 
 Exec Pod Separate Stderr
     [Arguments]    ${namespace}    ${name}    ${command}
@@ -137,6 +143,14 @@
     Log    ${currentStatusofPod}
     Should Contain    ${currentStatusofPod}    ${expectedStatus}
 
+Get Pod Name By Label
+    [Arguments]    ${namespace}    ${label_key}   ${label_value}
+    [Documentation]  Return a pod name from a given label
+    ${rc}    ${pod_name}=    Run and Return Rc and Output
+        ...    kubectl get pods -n ${namespace} -l ${label_key}=${label_value} --no-headers | awk '{print $1}'
+    Should Not Be Empty    ${pod_name}    Pod not found
+    [return]  ${pod_name}
+
 Validate Pods Status By Label
     [Arguments]    ${namespace}    ${label_key}   ${label_value}    ${expectedStatus}
     [Documentation]    To run the kubectl command and check the status of all pods filter
diff --git a/libraries/onos.robot b/libraries/onos.robot
index 66904b9..1f785f5 100755
--- a/libraries/onos.robot
+++ b/libraries/onos.robot
@@ -419,6 +419,38 @@
     ${aaa_users}=    Execute ONOS CLI Command    ${ip}    ${port}    aaa-users | grep AUTHORIZED | grep ${onu_port}
     Should Not Be Empty    ${aaa_users}    ONU port ${onu_port} not found in aaa-users
 
+Verify ONU in Groups
+    [Arguments]    ${ip_onos}    ${port_onos}    ${deviceId}    ${onu_port}    ${group_exist}=True
+    [Documentation]    Verifies that the specified onu_port exists in groups output
+    ${result}=    Execute ONOS CLI Command    ${ip_onos}    ${port_onos}    groups -j
+    Log    Groups: ${result}
+    ${groups}=    To Json    ${result}
+    ${length}=    Get Length    ${groups}
+    ${buckets}=    Create List
+    ${matched}=    Set Variable    False
+    FOR    ${INDEX}    IN RANGE    0    ${length}
+        ${value}=    Get From List    ${groups}    ${INDEX}
+        ${devId}=    Get From Dictionary    ${value}    deviceId
+        ${bucket}=    Get From Dictionary    ${value}    buckets
+        Run Keyword If    '${devId}'=='${deviceId}'
+        ...    Append To List    ${buckets}    ${bucket}
+    END
+    ${bucket_len}=    Get Length    ${buckets}
+    FOR    ${INDEX1}    IN RANGE    0    ${bucket_len}
+        ${value}=    Get From List    ${buckets}    ${INDEX}
+        ${value_bucket}=    Get From List    ${value}    0
+        ${treatment}=    Get From Dictionary    ${value_bucket}    treatment
+        ${instructions}=    Get From Dictionary    ${treatment}    instructions
+        ${instructions_val}=    Get From List    ${instructions}    0
+        ${port}=    Get From Dictionary    ${instructions_val}    port
+        ${matched}=    Set Variable If    '${port}'=='${onu_port}'    True    False
+        Exit For Loop If    ${matched}
+    END
+    Run Keyword If    ${group_exist}
+    ...    Should Be True    ${matched}    No match for ${deviceId} and ${onu_port} found in ONOS groups
+    ...    ELSE
+    ...    Should Be True    '${matched}'=='False'    Match for ${deviceId} and ${onu_port} found in ONOS groups
+
 Assert Number of AAA-Users
     [Arguments]    ${onos_ssh_connection}    ${expected_onus}   ${deviceId}
     [Documentation]    Matches for number of aaa-users authorized based on number of onus
diff --git a/tests/bbsim/Voltha_BBSimTests.robot b/tests/bbsim/Voltha_BBSimTests.robot
new file mode 100644
index 0000000..a494b0f
--- /dev/null
+++ b/tests/bbsim/Voltha_BBSimTests.robot
@@ -0,0 +1,162 @@
+# Copyright 2020 - 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 ***
+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/bbsim.robot
+Resource          ../../variables/variables.robot
+
+*** Variables ***
+${NAMESPACE}      voltha
+${timeout}        60s
+${of_id}          0
+${logical_id}     0
+${has_dataplane}    False
+${external_libs}    True
+${teardown_device}    True
+${scripts}        ../../scripts
+# determines the environment workflow: DT, TT or ATT (default)
+# example: -v workflow:TT
+${workflow}    ATT
+# logging flag to enable Collect Logs, can be passed via the command line too
+# example: -v logging:True
+${logging}    False
+# Per-test logging on failure is turned off by default; set this variable to enable
+${container_log_dir}    ${None}
+# Number of times to perform ONU Igmp Join and Leave (valid only for TT)
+${igmp_join_leave_count}    1
+
+*** Test Cases ***
+
+Test Perform BBSim Sanity
+    [Documentation]    Validates the BBSim Functionality for ATT, DT and TT workflows
+    ...    Also Restart Auth (ATT), Restart Dhcp (ATT and TT), Igmp Join and Leave (TT)
+    ...    NOTE: Currently works only for single ONU in case of ATT
+    [Tags]    bbsimSanity
+    [Setup]   Run Keywords    Start Logging    BBSimSanity
+    ...    AND    Setup
+    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}
+        ${bbsim_rel}=    Catenate    SEPARATOR=    bbsim    ${J}
+        ${bbsim_pod}=    Get Pod Name By Label    ${NAMESPACE}    release     ${bbsim_rel}
+        Perform BBSim Sanity Test Per OLT    ${bbsim_pod}    ${of_id}    ${olt_serial_number}    ${onu_count}
+    END
+    [Teardown]    Run Keywords    Run Keyword If    ${logging}    Collect Logs
+    ...    AND    Stop Logging    BBSimSanity
+
+*** Keywords ***
+
+Perform ONU Igmp Join and Leave
+    [Documentation]    This keyword performs Igmp Leave and Join for ONU
+    [Arguments]    ${bbsim_pod}    ${of_id}    ${onu}    ${onu_port}
+    FOR    ${Z}    IN RANGE    0    ${igmp_join_leave_count}
+        List Service    ${NAMESPACE}    ${bbsim_pod}
+        JoinOrLeave Igmp    ${NAMESPACE}    ${bbsim_pod}    ${onu}    join
+        Sleep    2s
+        List Service    ${NAMESPACE}    ${bbsim_pod}
+        List ONUs    ${NAMESPACE}    ${bbsim_pod}
+        Run Keyword And Continue On Failure    Wait Until Keyword Succeeds    ${timeout}    2s
+        ...    Verify ONU in Groups    ${ONOS_SSH_IP}    ${ONOS_SSH_PORT}    ${of_id}    ${onu_port}
+        JoinOrLeave Igmp    ${NAMESPACE}    ${bbsim_pod}    ${onu}    leave
+        Sleep    2s
+        List Service    ${NAMESPACE}    ${bbsim_pod}
+        List ONUs    ${NAMESPACE}    ${bbsim_pod}
+        Run Keyword And Continue On Failure    Wait Until Keyword Succeeds    ${timeout}    2s
+        ...    Verify ONU in Groups    ${ONOS_SSH_IP}    ${ONOS_SSH_PORT}    ${of_id}    ${onu_port}    False
+    END
+
+Perform BBSim Sanity Test Per OLT
+    [Documentation]    Validates the BBSim Functionality for ATT, DT and TT workflows
+    ...    Also Restart Auth (ATT), Restart Dhcp (ATT and TT), Igmp Join and Leave (TT)
+    [Arguments]    ${bbsim_pod}    ${of_id}    ${olt_serial_number}   ${num_onus}
+    FOR    ${I}    IN RANGE    0    ${num_all_onus}
+        ${src}=    Set Variable    ${hosts.src[${I}]}
+        ${dst}=    Set Variable    ${hosts.dst[${I}]}
+        Continue For Loop If    "${olt_serial_number}"!="${src['olt']}"
+        ${onu_device_id}=    Get Device ID From SN    ${src['onu']}
+        ${onu_port}=    Run Keyword And Continue On Failure    Wait Until Keyword Succeeds    ${timeout}    2s
+        ...    Get ONU Port in ONOS    ${src['onu']}    ${of_id}
+        # Check ONU port is Enabled in ONOS
+        Run Keyword And Continue On Failure    Wait Until Keyword Succeeds   120s   2s
+        ...    Verify ONU Port Is Enabled   ${ONOS_SSH_IP}    ${ONOS_SSH_PORT}    ${src['onu']}
+        Run Keyword If    "${workflow}"=="ATT"
+        ...    Run Keywords
+        # Verify ONU in AAA-Users (valid only for ATT)
+        ...    Run Keyword And Continue On Failure    Wait Until Keyword Succeeds    ${timeout}    2
+        ...    Verify ONU in AAA-Users    ${ONOS_SSH_IP}    ${ONOS_SSH_PORT}    ${onu_port}
+        ...    AND    List ONUs    ${NAMESPACE}    ${bbsim_pod}
+        # Restart Auth and Verify (valid only for ATT)
+        ...    AND    Execute ONOS CLI Command on open connection     ${onos_ssh_connection}
+        ...    aaa-reset-all-devices
+        ...    AND    Restart Auth    ${NAMESPACE}    ${bbsim_pod}    ${src['onu']}
+        ...    AND    Run Keyword And Continue On Failure    Wait Until Keyword Succeeds    ${timeout}    2
+        ...    Verify ONU in AAA-Users    ${ONOS_SSH_IP}    ${ONOS_SSH_PORT}    ${onu_port}
+        ...    AND    List ONUs    ${NAMESPACE}    ${bbsim_pod}
+        # Add Subscriber
+        Run Keyword And Continue On Failure    Wait Until Keyword Succeeds    ${timeout}    2
+        ...    Execute ONOS CLI Command on open connection    ${onos_ssh_connection}
+        ...    volt-add-subscriber-access ${of_id} ${onu_port}
+        # Verify that no pending flows exist for the ONU port
+        Run Keyword And Continue On Failure    Wait Until Keyword Succeeds    ${timeout}    2s
+        ...    Verify No Pending Flows For ONU    ${ONOS_SSH_IP}    ${ONOS_SSH_PORT}    ${onu_port}
+        # Verify subscriber  dhcp allocations (valid only for ATT and TT)
+        Run Keyword If    "${workflow}"=="ATT" or "${workflow}"=="TT"
+        ...    Run Keywords
+        ...    Run Keyword And Continue On Failure    Wait Until Keyword Succeeds    ${timeout}    2s
+        ...    Validate Subscriber DHCP Allocation    ${ONOS_SSH_IP}    ${ONOS_SSH_PORT}    ${onu_port}
+        ...    AND    List ONUs    ${NAMESPACE}    ${bbsim_pod}
+        # Restart Dhcp and Verify (valid only for ATT and TT)
+        ...    AND    Execute ONOS CLI Command on open connection     ${onos_ssh_connection}
+        ...    dhcpl2relay-remove-allocation ${of_id} ${onu_port}
+        ...    AND    Restart DHCP    ${NAMESPACE}    ${bbsim_pod}    ${src['onu']}
+        ...    AND    Run Keyword And Continue On Failure    Wait Until Keyword Succeeds    ${timeout}    2s
+        ...    Validate Subscriber DHCP Allocation    ${ONOS_SSH_IP}    ${ONOS_SSH_PORT}    ${onu_port}
+        ...    AND    List ONUs    ${NAMESPACE}    ${bbsim_pod}
+        # Perform Igmp Join and Leave (valid only for TT)
+        Run Keyword If    "${workflow}"=="TT"
+        ...    Perform ONU Igmp Join and Leave    ${bbsim_pod}    ${of_id}    ${src['onu']}    ${onu_port}
+    END
+
+Setup Suite
+    [Documentation]    Set up the test suite
+    Common Test Suite Setup
+    #send igmp file to onos (valid only for TT)
+    ${onos_netcfg_file}=    Get Variable Value    ${onos_netcfg.file}
+    Run Keyword If    '${workflow}'=='TT' and '${has_dataplane}'=='False' and '${onos_netcfg_file}'!='${None}'
+    ...    Send File To Onos    ${onos_netcfg_file}    apps/
+    ${onos_ssh_connection}    Open ONOS SSH Connection    ${ONOS_SSH_IP}    ${ONOS_SSH_PORT}
+    Set Suite Variable    ${onos_ssh_connection}
+
+Teardown Suite
+    [Documentation]    Replaces the Suite Teardown in utils.robot.
+    ...    Cleans up and checks all ONU ports disabled in ONOS.
+    Run Keyword If    ${teardown_device}    Delete All Devices and Verify
+    Close All ONOS SSH Connections
diff --git a/tests/data/bbsim-kind.yaml b/tests/data/bbsim-kind.yaml
index 5326f68..cedde9f 100644
--- a/tests/data/bbsim-kind.yaml
+++ b/tests/data/bbsim-kind.yaml
@@ -42,3 +42,7 @@
 
   dst:
     - ip: null
+
+# To read only if workflow is "TT"
+onos_netcfg:
+  file: ${CURDIR}/../../data/onos-igmp.json
diff --git a/tests/data/onos-igmp.json b/tests/data/onos-igmp.json
new file mode 100644
index 0000000..76f4fa4
--- /dev/null
+++ b/tests/data/onos-igmp.json
@@ -0,0 +1,22 @@
+{
+    "org.opencord.igmpproxy" : {
+        "igmpproxy":{
+            "FastLeave":"true",
+            "LastQueryInterval":1,
+            "MaxResp":1,
+            "enableIgmpProvisioning":"false",
+            "globalConnectPointMode":"true",
+            "globalConnectPoint" : "of:0000000000000001/3",
+            "sourceDeviceAndPort":"of:0000000000000001/3",
+            "outgoingIgmpVlanId":550,
+            "outgoingIgmpInnerVlanId":55,
+            "outgoingIgmpWithV3":"true",
+            "IgmpCos":5,
+            "IgmpUniCos":5,
+            "PeriodicQuery":"true",
+            "KeepAliveInterval":60,
+            "KeepAliveCount":5,
+            "requestDsIgmpPackets":false
+        }
+    }
+}