[VOL-2054] Validate technology profile with pbit verification

Change-Id: I148c87681d11d1dcbb4739007acdd9ac1326db41
diff --git a/Makefile b/Makefile
index bb0a956..63e7ec3 100755
--- a/Makefile
+++ b/Makefile
@@ -21,7 +21,7 @@
 # Configuration and lists of files for linting/testing
 VERSION   ?= $(shell cat ./VERSION)
 LINT_ARGS ?= --verbose --configure LineTooLong:120 -e LineTooLong \
-             --configure TooManyTestSteps:40 -e TooManyTestSteps \
+             --configure TooManyTestSteps:50 -e TooManyTestSteps \
              --configure TooManyTestCases:50 -e TooManyTestCases \
              --configure TooFewTestSteps:1 \
              --configure TooFewKeywordSteps:1 \
diff --git a/README.md b/README.md
index c25d856..0a0c448 100644
--- a/README.md
+++ b/README.md
@@ -161,6 +161,29 @@
 check the link
 [here](https://github.com/ciena/kind-voltha/blob/master/README.md)
 
+### Dataplane test prerequisites
+
+The dataplane tests evaluate whether bandwidth and tech profiles are working as expected.
+These tests will only run on a physical pod.  In order to run them it is required to manually install
+some additional software on the POD hosts that emulate the RG and BNG.
+
+On the RG hosts:
+* Install `iperf3` version 3.7, available here: https://software.es.net/iperf/
+* Install `jq`: `sudo apt install jq`
+* Install `mausezahn`: `sudo apt install netsniff-ng`
+* Ensure that the following commands can be run as `sudo` with no password: `tcpdump`, `mausezahn`, `pkill`
+
+On the BNG host:
+* Install `iperf3` version 3.7, available here: https://software.es.net/iperf/
+* Run `iperf3` in server mode in the background: `iperf3 --server -D`
+* Install `jq`: `sudo apt install jq`
+* Install `mausezahn`: `sudo apt install netsniff-ng`
+* Ensure that the following commands can be run as `sudo` with no password: `tcpdump`, `mausezahn`, `pkill`
+
+In the POD's deployment config file, specify login information for the BNG host using the `noroot_ip`, `noroot_user`,
+and `noroot_pass` options.  See the [Tucson pod's config](https://github.com/opencord/pod-configs/blob/master/deployment-configs/tucson-pod.yaml)
+for an example.
+
 ## Functional Testcases
 
 All functional test cases are placed under `functional` folder.
diff --git a/libraries/utils.robot b/libraries/utils.robot
index 0791e22..39173e8 100644
--- a/libraries/utils.robot
+++ b/libraries/utils.robot
@@ -540,6 +540,26 @@
     SSHLibrary.Close Connection
     [Return]    ${stdout}    ${stderr}    ${rc}
 
+Start Remote Command
+    [Documentation]    SSH into a remote host and execute a command on the bare host or in a container.
+    ...    This replaces and simplifies the Login And Run Command On Remote System keyword in CORDRobot.
+    [Arguments]    ${cmd}    ${ip}    ${user}    ${pass}=${None}
+    ...    ${container_type}=${None}    ${container_name}=${None}
+    ${conn_id}=    SSHLibrary.Open Connection    ${ip}
+    Run Keyword If    '${pass}' != '${None}'
+    ...    SSHLibrary.Login    ${user}    ${pass}
+    ...    ELSE
+    ...    SSHLibrary.Login With Public Key    ${user}    %{HOME}/.ssh/id_rsa
+    ${namespace}=    Run Keyword If    '${container_type}' == 'K8S'    SSHLibrary.Execute Command
+    ...    kubectl get pods --all-namespaces | grep ${container_name} | awk '{print $1}'
+    Run Keyword If    '${container_type}' == 'LXC'
+    ...        SSHLibrary.Start Command    lxc exec ${container_name} -- ${cmd}
+    ...    ELSE IF    '${container_type}' == 'K8S'
+    ...        SSHLibrary.Start Command    kubectl -n ${namespace} exec ${container_name} -- ${cmd}
+    ...    ELSE
+    ...        SSHLibrary.Start Command    ${cmd}
+    SSHLibrary.Close Connection
+
 Run Iperf3 Test Client
     [Arguments]    ${src}    ${server}    ${args}
     [Documentation]    Login to ${src} and run the iperf3 client against ${server} using ${args}.
@@ -550,7 +570,6 @@
     ${object}=    Evaluate    json.loads(r'''${output}''')    json
     [Return]    ${object}
 
-
 RestoreONUs
     [Documentation]    Restore all connected ONUs
     [Arguments]    ${num_onus}
@@ -581,3 +600,30 @@
     Log To Console    ${output}
     ${output}=    Login And Run Command On Remote System    sudo ifconfig ${onu_ifname} 0
     ...    ${rg_ip}    ${rg_user}    ${rg_pass}    ${container_type}    ${container_name}
+
+Create traffic with each pbit and capture at other end
+    [Documentation]    Generates upstream traffic using Mausezahn tool
+    ...    with each pbit and validates reception at other end using tcpdump
+    [Arguments]    ${target_ip}    ${target_iface}    ${src_iface}
+    ...    ${packet_count}    ${packet_type}    ${target_port}    ${vlan}    ${tcpdump_filter}
+    ...    ${dst_ip}    ${dst_user}    ${dst_pass}    ${dst_container_type}    ${dst_container_name}
+    ...    ${src_ip}    ${src_user}    ${src_pass}    ${src_container_type}    ${src_container_name}
+    FOR    ${pbit}    IN RANGE    8
+        Execute Remote Command    sudo pkill -2 mausezahn
+        ...    ${src_ip}    ${src_user}    ${src_pass}    ${src_container_type}    ${src_container_name}
+        ${var1}=    Set Variable    sudo mausezahn ${src_iface} -B ${target_ip} -c ${packet_count}
+        ${var2}=    Set Variable    -t ${packet_type} "dp=${target_port}" -p 1472 -Q ${pbit}:${vlan}
+        ${cmd}=    Set Variable    ${var1} ${var2}
+        Start Remote Command    ${cmd}    ${src_ip}    ${src_user}    ${src_pass}
+        ...    ${src_container_type}    ${src_container_name}
+        ${output}    ${stderr}    ${rc}=    Execute Remote Command
+        ...    timeout 30 sudo tcpdump -l -U -c 30 -i ${target_iface} -e ${tcpdump_filter}
+        ...    ${dst_ip}    ${dst_user}    ${dst_pass}    ${dst_container_type}    ${dst_container_name}
+        Execute Remote Command    sudo pkill -2 mausezahn
+        ...    ${src_ip}    ${src_user}    ${src_pass}    ${src_container_type}    ${src_container_name}
+        # VOL-3262:  I'm seeing untagged downstream traffic at RG for pbit 0.  According to Girish this is
+        # incorrect behavior.  Simplify the following check when VOL-3262 is resolved.
+        Run Keyword If    ${pbit}==0 and "${tcpdump_filter}"=="udp"
+        ...    Should Match Regexp    ${output}    \\.${target_port}: UDP,
+        ...    ELSE    Should Match Regexp    ${output}    , p ${pbit},.*\\.${target_port}: UDP,
+    END
diff --git a/tests/functional/Voltha_PODTests.robot b/tests/functional/Voltha_PODTests.robot
index 8fb1fe2..741e764 100644
--- a/tests/functional/Voltha_PODTests.robot
+++ b/tests/functional/Voltha_PODTests.robot
@@ -579,6 +579,55 @@
         ...    The downstream bandwidth guarantee was not met (${pct_limit_dn}% of resv)
     END
 
+Validate parsing of data traffic through voltha using tech profile
+    [Documentation]    Assuming that test1 was executed where all the ONUs are authenticated/DHCP/pingable
+    ...    Prerequisite tools : Tcpdump and Mausezahn traffic generator on both RG and DHCP/BNG VMs
+    ...    Install jq tool to read json file, where test suite is being running
+    ...    Make sure 9999 port is enabled or forwarded for both upsteam and downstream direction
+    ...    This test sends UDP packets on port 9999 with pbits between 0 and 7 and validates that
+    ...    the pbits are preserved by the PON.
+    [Tags]    dataplane    TechProfile    VOL-2054
+    [Setup]    Start Logging    TechProfile
+    [Teardown]    Run Keywords    Collect Logs
+    ...           AND    Stop Logging    TechProfile
+    Pass Execution If   '${has_dataplane}'=='False'
+    ...    Skipping test: Technology profile validation can be done only in physical pod
+    FOR    ${I}    IN RANGE    0    ${num_onus}
+        ${src}=    Set Variable    ${hosts.src[${I}]}
+        ${dst}=    Set Variable    ${hosts.dst[${I}]}
+
+        ${bng_ip}=    Get Variable Value    ${dst['noroot_ip']}
+        ${bng_user}=    Get Variable Value    ${dst['noroot_user']}
+        ${bng_pass}=    Get Variable Value    ${dst['noroot_pass']}
+        Pass Execution If    not ("${bng_ip}" and "${bng_user}" and "${bng_pass}")
+        ...    Skipping test: credentials for BNG login required in deployment config
+
+        ${stdout}    ${stderr}    ${rc}=    Execute Remote Command    which mausezahn tcpdump
+        ...    ${src['ip']}    ${src['user']}    ${src['pass']}    ${src['container_type']}    ${src['container_name']}
+        Pass Execution If    ${rc} != 0    Skipping test: mausezahn / tcpdump not found on the RG
+        ${stdout}    ${stderr}    ${rc}=    Execute Remote Command    which mausezahn tcpdump
+        ...    ${bng_ip}    ${bng_user}    ${bng_pass}    ${dst['container_type']}    ${dst['container_name']}
+        Pass Execution If    ${rc} != 0    Skipping test: mausezahn / tcpdump not found on the BNG
+        Log    Upstream test
+        Run Keyword If    ${has_dataplane}    Wait Until Keyword Succeeds    ${timeout}    2s
+        ...    Create traffic with each pbit and capture at other end
+        ...    ${dst['dp_iface_ip_qinq']}    ${dst['dp_iface_name']}    ${src['dp_iface_name']}
+        ...    0    udp    9999    0    vlan
+        ...    ${bng_ip}    ${bng_user}    ${bng_pass}    ${dst['container_type']}    ${dst['container_name']}
+        ...    ${src['ip']}    ${src['user']}    ${src['pass']}    ${src['container_type']}    ${src['container_name']}
+        Log    Downstream test
+        ${rg_ip}    ${stderr}    ${rc}=    Execute Remote Command
+        ...    ifconfig ${src['dp_iface_name']} | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1 }'
+        ...    ${src['ip']}    ${src['user']}    ${src['pass']}    ${src['container_type']}    ${src['container_name']}
+        Should Be Equal As Integers    ${rc}    0    Could not get RG's IP address
+        Run Keyword If    ${has_dataplane}    Wait Until Keyword Succeeds    ${timeout}    2s
+        ...    Create traffic with each pbit and capture at other end
+        ...    ${rg_ip}    ${src['dp_iface_name']}    ${dst['dp_iface_name']}.${src['s_tag']}
+        ...    0    udp    9999    ${src['c_tag']}    udp
+        ...    ${src['ip']}    ${src['user']}    ${src['pass']}    ${src['container_type']}    ${src['container_name']}
+        ...    ${bng_ip}    ${bng_user}    ${bng_pass}    ${dst['container_type']}    ${dst['container_name']}
+    END
+
 *** Keywords ***
 Setup Suite
     [Documentation]    Set up the test suite
@@ -595,4 +644,3 @@
     Delete All Devices and Verify
     # Execute normal test Setup Keyword
     Setup
-