[VOL-2052] Bandwidth Profile Validation

Change-Id: Icdf6ab05435d5359158dabb704e5cecb7f7d4b37
diff --git a/libraries/onos.robot b/libraries/onos.robot
index 4d10af5..69ee7e2 100644
--- a/libraries/onos.robot
+++ b/libraries/onos.robot
@@ -361,3 +361,30 @@
     ${targetFlows}=     Calculate flows by workflow     ${workflow}    ${uni_count}    ${olt_count}     ${provisioned}
     Wait Until Keyword Succeeds     10m     5s      Count ADDED flows
     ...     ${onos_ip}    ${onos_port}  ${targetFlows}
+
+Get Bandwidth Details
+    [Arguments]    ${bandwidth_profile_name}
+    [Documentation]    Collects the bandwidth profile details for the given bandwidth profile and
+    ...    returns the limiting bandwidth
+    ${bandwidth_profile_values}=    Execute ONOS CLI Command    ${ONOS_SSH_IP}    ${ONOS_SSH_PORT}
+    ...    bandwidthprofile ${bandwidth_profile_name}
+    @{bandwidth_profile_array}=    Split String    ${bandwidth_profile_values}    ,
+    @{parameter_value_pair}=    Split String    ${bandwidth_profile_array[1]}    =
+    ${bandwidthparameter}=    Set Variable    ${parameter_value_pair[0]}
+    ${value}=    Set Variable    ${parameter_value_pair[1]}
+    ${cir_value}    Run Keyword If    '${bandwidthparameter}' == ' committedInformationRate'
+    ...    Set Variable    ${value}
+    @{parameter_value_pair}=    Split String    ${bandwidth_profile_array[2]}    =
+    ${bandwidthparameter}=    Set Variable    ${parameter_value_pair[0]}
+    ${value}=    Set Variable    ${parameter_value_pair[1]}
+    ${cbs_value}    Run Keyword If    '${bandwidthparameter}' == ' committedBurstSize'    Set Variable    ${value}
+    @{parameter_value_pair}=    Split String    ${bandwidth_profile_array[3]}    =
+    ${bandwidthparameter}=    Set Variable    ${parameter_value_pair[0]}
+    ${value}=    Set Variable    ${parameter_value_pair[1]}
+    ${eir_value}    Run Keyword If    '${bandwidthparameter}' == ' exceededInformationRate'   Set Variable    ${value}
+    @{parameter_value_pair}=    Split String    ${bandwidth_profile_array[4]}    =
+    ${bandwidthparameter}=    Set Variable    ${parameter_value_pair[0]}
+    ${value}=    Set Variable    ${parameter_value_pair[1]}
+    ${ebs_value}    Run Keyword If    '${bandwidthparameter}' == ' exceededBurstSize'    Set Variable    ${value}
+    ${limiting_BW}=    Evaluate    ${cir_value}+${eir_value}
+    [Return]    ${limiting_BW}
diff --git a/libraries/utils.robot b/libraries/utils.robot
index 258b465..8c9f655 100644
--- a/libraries/utils.robot
+++ b/libraries/utils.robot
@@ -529,4 +529,52 @@
         ...     Evaluate    (${olt_count} * 2)
         ...     ELSE
         ...     Evaluate    (${uni_count} * 6) + (${olt_count} * 1)
-    Return From Keyword     ${flow_count}
\ No newline at end of file
+    Return From Keyword     ${flow_count}
+
+Get Bandwidth Profile Name For Given Subscriber
+    [Arguments]    ${subscriber_id}   ${stream_type}=upstreamBandwidthProfile
+    [Documentation]    Keyword to get the bandwidth details of the given subscriber
+    ${bandwidth_profile_output}=    Execute ONOS CLI Command    ${ONOS_SSH_IP}    ${ONOS_SSH_PORT}
+    ...    volt-programmed-subscribers | grep ${subscriber_id}
+    @{bandwidth_profile_array}=    Split String    ${bandwidth_profile_output}    ,
+    Log    ${bandwidth_profile_array}
+    FOR    ${value}    IN    @{bandwidth_profile_array}
+        @{row_value}=    Split String    ${value}    =
+        ${bandwidth_profile_name}=    Set Variable If    '${row_value[0]}' == ' ${stream_type}'
+        ...    ${row_value[1]}
+        ${bandwidth_profile_name}=    Convert To String    ${bandwidth_profile_name}
+        Run Keyword If    "${bandwidth_profile_name}" != "None"    Exit For Loop
+    END
+    Log    ${bandwidth_profile_name}
+    [Return]    ${bandwidth_profile_name}
+
+Execute 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}'
+    ${output}=    Run Keyword If    '${container_type}' == 'LXC'
+    ...    SSHLibrary.Execute Command    lxc exec ${container_name} -- ${cmd}
+    ...    ELSE IF    '${container_type}' == 'K8S'
+    ...    SSHLibrary.Execute Command    kubectl -n ${namespace} exec ${container_name} -- ${cmd}
+    ...    ELSE
+    ...    SSHLibrary.Execute Command    ${cmd}
+    Log    ${output}
+    SSHLibrary.Close Connection
+    [Return]    ${output}
+
+Run Iperf3 Test Client
+    [Arguments]    ${src}    ${server}    ${args}
+    [Documentation]    Login to ${src} and run the iperf3 client against ${server} using ${args}.
+    ...    Return a Dictionary containing the results of the test.
+    ${output}=    Execute Remote Command    iperf3 -J -c ${server} ${args} | jq -M -c '.'
+    ...    ${src['ip']}    ${src['user']}    ${src['pass']}    ${src['container_type']}    ${src['container_name']}
+    ${object}=    Evaluate    json.loads(r'''${output}''')    json
+    [Return]    ${object}
diff --git a/tests/data/tucson-pod-sadis.json b/tests/data/tucson-pod-sadis.json
index 9f4fcc1..1ee05da 100644
--- a/tests/data/tucson-pod-sadis.json
+++ b/tests/data/tucson-pod-sadis.json
@@ -25,8 +25,8 @@
               "ponCTag": 11,
               "ponSTag": 11,
               "technologyProfileId": 64,
-              "upstreamBandwidthProfile": "User_Bandwidth1",
-              "downstreamBandwidthProfile": "User_Bandwidth1",
+              "upstreamBandwidthProfile": "Default",
+              "downstreamBandwidthProfile": "Default",
               "isDhcpRequired": true
             }]
           },
@@ -39,7 +39,7 @@
               "ponCTag": 12,
               "ponSTag": 11,
               "technologyProfileId": 64,
-              "upstreamBandwidthProfile": "User_Bandwidth2",
+              "upstreamBandwidthProfile": "User_Bandwidth1",
               "downstreamBandwidthProfile": "User_Bandwidth2",
               "isDhcpRequired": true
             }]
@@ -80,7 +80,7 @@
               {
                   "id": "User_Bandwidth1",
                   "air": 100000,
-                  "cbs": 10000,
+                  "cbs": 1000,
                   "cir": 30000,
                   "ebs": 1000,
                   "eir": 20000
@@ -97,9 +97,9 @@
                   "id": "User_Bandwidth3",
                   "air": 100000,
                   "cbs": 5000,
-                  "cir": 1000000,
+                  "cir": 500000,
                   "ebs": 5000,
-                  "eir": 1000000
+                  "eir": 500000
               }
 
           ]
diff --git a/tests/functional/Voltha_PODTests.robot b/tests/functional/Voltha_PODTests.robot
index 7671f1a..993b489 100644
--- a/tests/functional/Voltha_PODTests.robot
+++ b/tests/functional/Voltha_PODTests.robot
@@ -51,6 +51,12 @@
 ${teardown_device}    False
 ${scripts}        ../../scripts
 
+# For dataplane bandwidth testing
+${upper_margin_pct}      105     # Allow 5% over the limit
+${lower_margin_pct}      93      # Allow 7% under the limit
+${udp_rate_multiplier}   1.10    # Send UDP at bw profile limit * rate_multiplier
+${udp_packet_bytes}      1400    # UDP payload in bytes
+
 # Per-test logging on failure is turned off by default; set this variable to enable
 ${container_log_dir}    ${None}
 
@@ -445,6 +451,103 @@
     END
     Run Keyword and Ignore Error    Collect Logs
 
+Data plane verification using TCP
+    [Documentation]    Test bandwidth profile is met and not exceeded for each subscriber.
+    ...    Assumes iperf3 and jq installed on client and iperf -s running on DHCP server
+    [Tags]    dataplane    BW-profile-TCP    VOL-2052
+    [Setup]    None
+    [Teardown]    None
+    Pass Execution If   '${has_dataplane}'=='False'    Bandwidth profile validation can be done only in
+    ...    physical pod.  Skipping this test in BBSIM.
+    FOR    ${I}    IN RANGE    0    ${num_onus}
+        ${src}=    Set Variable    ${hosts.src[${I}]}
+        ${dst}=    Set Variable    ${hosts.dst[${I}]}
+        ${onu_port}=    Wait Until Keyword Succeeds    ${timeout}    2s    Get ONU Port in ONOS    ${src['onu']}
+        ...    ${of_id}
+        ${subscriber_id}=    Set Variable    ${of_id}/${onu_port}
+        ${bandwidth_profile_name}    Get Bandwidth Profile Name For Given Subscriber    ${subscriber_id}
+        ...    upstreamBandwidthProfile
+        ${limiting_bw_value_upstream}    Get Bandwidth Details    ${bandwidth_profile_name}
+        ${bandwidth_profile_name}    Get Bandwidth Profile Name For Given Subscriber    ${subscriber_id}
+        ...    downstreamBandwidthProfile
+        ${limiting_bw_value_dnstream}    Get Bandwidth Details    ${bandwidth_profile_name}
+
+        # Stream TCP packets from RG to server
+        ${updict}=    Run Iperf3 Test Client    ${src}    server=${dst['dp_iface_ip_qinq']}
+        ...    args=-t 30
+        ${actual_upstream_bw_used}=    Evaluate    ${updict['end']['sum_received']['bits_per_second']}/1000
+
+        # Stream TCP packets from server to RG
+        ${dndict}=    Run Iperf3 Test Client    ${src}    server=${dst['dp_iface_ip_qinq']}
+        ...    args=-R -t 30
+        ${actual_dnstream_bw_used}=    Evaluate    ${dndict['end']['sum_received']['bits_per_second']}/1000
+
+        ${pct_limit_up}=    Evaluate    100*${actual_upstream_bw_used}/${limiting_bw_value_upstream}
+        ${pct_limit_dn}=    Evaluate    100*${actual_dnstream_bw_used}/${limiting_bw_value_dnstream}
+        Log    Up: bwprof ${limiting_bw_value_upstream}Kbps, got ${actual_upstream_bw_used}Kbps (${pct_limit_up}%)
+        Log    Down: bwprof ${limiting_bw_value_dnstream}Kbps, got ${actual_dnstream_bw_used}Kbps (${pct_limit_dn}%)
+
+        Should Be True    ${pct_limit_up} <= ${upper_margin_pct}
+        ...    The upstream bandwidth exceeded the limit (${pct_limit_up}% of limit)
+        Should Be True    ${pct_limit_dn} <= ${upper_margin_pct}
+        ...    The downstream bandwidth exceeded the limit (${pct_limit_dn}% of limit)
+        Should Be True    ${pct_limit_up} >= ${lower_margin_pct}
+        ...    The upstream bandwidth guarantee was not met (${pct_limit_up}% of resv)
+        Should Be True    ${pct_limit_dn} >= ${lower_margin_pct}
+        ...    The downstream bandwidth guarantee was not met (${pct_limit_dn}% of resv)
+    END
+
+Data plane verification using UDP
+    [Documentation]    Test bandwidth profile is met and not exceeded for each subscriber.
+    ...    Assumes iperf3 and jq installed on client and iperf -s running on DHCP server
+    [Tags]    dataplane    BW-profile-UDP    VOL-2052
+    [Setup]    None
+    [Teardown]    None
+    Pass Execution If   '${has_dataplane}'=='False'    Bandwidth profile validation can be done only in
+    ...    physical pod.  Skipping this test in BBSIM.
+    FOR    ${I}    IN RANGE    0    ${num_onus}
+        ${src}=    Set Variable    ${hosts.src[${I}]}
+        ${dst}=    Set Variable    ${hosts.dst[${I}]}
+        ${onu_port}=    Wait Until Keyword Succeeds    ${timeout}    2s    Get ONU Port in ONOS    ${src['onu']}
+        ...    ${of_id}
+        ${subscriber_id}=    Set Variable    ${of_id}/${onu_port}
+        ${bandwidth_profile_name}    Get Bandwidth Profile Name For Given Subscriber    ${subscriber_id}
+        ...    upstreamBandwidthProfile
+        ${limiting_bw_value_upstream}    Get Bandwidth Details    ${bandwidth_profile_name}
+        ${bandwidth_profile_name}    Get Bandwidth Profile Name For Given Subscriber    ${subscriber_id}
+        ...    downstreamBandwidthProfile
+        ${limiting_bw_value_dnstream}    Get Bandwidth Details    ${bandwidth_profile_name}
+
+        # Stream UDP packets from RG to server
+        ${uprate}=    Evaluate    ${limiting_bw_value_upstream}*${udp_rate_multiplier}
+        ${updict}=    Run Iperf3 Test Client    ${src}    server=${dst['dp_iface_ip_qinq']}
+        ...    args=-u -b ${uprate}K -t 30 -l ${udp_packet_bytes}
+        # With UDP test, bits per second is the sending rate.  Multiply by the loss rate to get the throughput.
+        ${actual_upstream_bw_used}=    Evaluate
+        ...    (100 - ${updict['end']['sum']['lost_percent']})*${updict['end']['sum']['bits_per_second']}/100000
+
+        # Stream UDP packets from server to RG
+        ${dnrate}=    Evaluate    ${limiting_bw_value_dnstream}*${udp_rate_multiplier}
+        ${dndict}=    Run Iperf3 Test Client    ${src}    server=${dst['dp_iface_ip_qinq']}
+        ...    args=-u -b ${dnrate}K -R -t 30 -l ${udp_packet_bytes}
+        # With UDP test, bits per second is the sending rate.  Multiply by the loss rate to get the throughput.
+        ${actual_dnstream_bw_used}=    Evaluate
+        ...    (100 - ${dndict['end']['sum']['lost_percent']})*${dndict['end']['sum']['bits_per_second']}/100000
+
+        ${pct_limit_up}=    Evaluate    100*${actual_upstream_bw_used}/${limiting_bw_value_upstream}
+        ${pct_limit_dn}=    Evaluate    100*${actual_dnstream_bw_used}/${limiting_bw_value_dnstream}
+        Log    Up: bwprof ${limiting_bw_value_upstream}Kbps, got ${actual_upstream_bw_used}Kbps (${pct_limit_up}%)
+        Log    Down: bwprof ${limiting_bw_value_dnstream}Kbps, got ${actual_dnstream_bw_used}Kbps (${pct_limit_dn}%)
+
+        Should Be True    ${pct_limit_up} <= ${upper_margin_pct}
+        ...    The upstream bandwidth exceeded the limit (${pct_limit_up}% of limit)
+        Should Be True    ${pct_limit_dn} <= ${upper_margin_pct}
+        ...    The downstream bandwidth exceeded the limit (${pct_limit_dn}% of limit)
+        Should Be True    ${pct_limit_up} >= ${lower_margin_pct}
+        ...    The upstream bandwidth guarantee was not met (${pct_limit_up}% of resv)
+        Should Be True    ${pct_limit_dn} >= ${lower_margin_pct}
+        ...    The downstream bandwidth guarantee was not met (${pct_limit_dn}% of resv)
+    END
 
 *** Keywords ***
 Setup Suite