diff options
50 files changed, 9770 insertions, 55 deletions
diff --git a/docs/testing/user/userguide/14-nsb-operation.rst b/docs/testing/user/userguide/14-nsb-operation.rst index 941a0bb65..69ffb8a3b 100644 --- a/docs/testing/user/userguide/14-nsb-operation.rst +++ b/docs/testing/user/userguide/14-nsb-operation.rst @@ -640,3 +640,37 @@ A testcase can be started with the following command as an example: .. code-block:: bash yardstick task start /yardstick/samples/vnf_samples/nsut/vpe/tc_baremetal_rfc2544_ipv4_1flow_64B_ixia.yaml + +Preparing test run of vIPSEC test case +------------------------------------ + +Location of vIPSEC test cases: ``samples/vnf_samples/nsut/ipsec/``. + +Before running a specific vIPSEC test case using NSB, some dependencies have to be +preinstalled and properly configured. +- VPP + +.. code-block:: console + + export UBUNTU="xenial" + export RELEASE=".stable.1810" + sudo rm /etc/apt/sources.list.d/99fd.io.list + echo "deb [trusted=yes] https://nexus.fd.io/content/repositories/fd.io$RELEASE.ubuntu.$UBUNTU.main/ ./" | sudo tee -a /etc/apt/sources.list.d/99fd.io.list + sudo apt-get update + sudo apt-get install vpp vpp-lib vpp-plugin vpp-dbg vpp-dev vpp-api-java vpp-api-python vpp-api-lua + +- VAT templates + + VAT templates is required for the VPP API. + +.. code-block:: console + + mkdir -p /opt/nsb_bin/vpp/templates/ + echo 'exec trace add dpdk-input 50' > /opt/nsb_bin/vpp/templates/enable_dpdk_traces.vat + echo 'exec trace add vhost-user-input 50' > /opt/nsb_bin/vpp/templates/enable_vhost_user_traces.vat + echo 'exec trace add memif-input 50' > /opt/nsb_bin/vpp/templates/enable_memif_traces.vat + cat > /opt/nsb_bin/vpp/templates/dump_interfaces.vat << EOL + sw_interface_dump + dump_interface_table + quit + EOL diff --git a/docs/testing/user/userguide/nsb/nsb-list-of-tcs.rst b/docs/testing/user/userguide/nsb/nsb-list-of-tcs.rst index 6c18c7d89..a578216da 100644 --- a/docs/testing/user/userguide/nsb/nsb-list-of-tcs.rst +++ b/docs/testing/user/userguide/nsb/nsb-list-of-tcs.rst @@ -36,3 +36,4 @@ NSB PROX Test Case Descriptions tc_vfw_rfc2544 tc_vfw_rfc2544_correlated tc_vfw_rfc3511 + tc_vpp_baremetal_crypto_ipsec diff --git a/docs/testing/user/userguide/nsb/tc_vpp_baremetal_crypto_ipsec.rst b/docs/testing/user/userguide/nsb/tc_vpp_baremetal_crypto_ipsec.rst new file mode 100644 index 000000000..6a4a37697 --- /dev/null +++ b/docs/testing/user/userguide/nsb/tc_vpp_baremetal_crypto_ipsec.rst @@ -0,0 +1,113 @@ +.. This work is licensed under a Creative Commons Attribution 4.0 International +.. License. +.. http://creativecommons.org/licenses/by/4.0 +.. (c) OPNFV, 2019 Viosoft Corporation. + +*********************************************** +Yardstick Test Case Description: NSB VPP IPSEC +*********************************************** + ++------------------------------------------------------------------------------+ +|NSB VPP test for vIPSEC characterization | +| | ++--------------+---------------------------------------------------------------+ +|test case id | tc_baremetal_rfc2544_ipv4_{crypto_dev}_{crypto_alg} | +| | | +| | * crypto_dev = HW_cryptodev or SW_cryptodev; | +| | * crypto_alg = aes-gcm or cbc-sha1; | +| | | ++--------------+---------------------------------------------------------------+ +|metric | * Network Throughput NDR or PDR; | +| | * Connections Per Second (CPS); | +| | * Latency; | +| | * Number of tunnels; | +| | * TG Packets Out; | +| | * TG Packets In; | +| | * VNF Packets Out; | +| | * VNF Packets In; | +| | * Dropped packets; | +| | | ++--------------+---------------------------------------------------------------+ +|test purpose | IPv4 IPsec tunnel mode performance test: | +| | | +| | * Finds and reports throughput NDR (Non Drop Rate) with zero | +| | packet loss tolerance or throughput PDR (Partial Drop Rate) | +| | with non-zero packet loss tolerance (LT) expressed in | +| | number of packets transmitted. | +| | | +| | * The IPSEC test cases are implemented to run in baremetal | +| | | ++--------------+---------------------------------------------------------------+ +|configuration | The IPSEC test cases are listed below: | +| | | +| | * tc_baremetal_rfc2544_ipv4_hw_aesgcm_IMIX_trex.yaml | +| | * tc_baremetal_rfc2544_ipv4_hw_aesgcm_trex.yaml | +| | * tc_baremetal_rfc2544_ipv4_hw_cbcsha1_IMIX_trex.yaml | +| | * tc_baremetal_rfc2544_ipv4_hw_cbcsha1_trex.yaml | +| | * tc_baremetal_rfc2544_ipv4_sw_aesgcm_IMIX_trex.yaml | +| | * tc_baremetal_rfc2544_ipv4_sw_aesgcm_trex.yaml | +| | * tc_baremetal_rfc2544_ipv4_sw_cbcsha1_IMIX_trex.yaml | +| | * tc_baremetal_rfc2544_ipv4_sw_cbcsha1_trex.yaml | +| | | +| | Test duration is set as 500sec for each test. | +| | Packet size set as 64 bytes or higher. | +| | Number of tunnels set as 1 or higher. | +| | Number of connections set as 1 or higher | +| | These can be configured | +| | | ++--------------+---------------------------------------------------------------+ +|test tool | Vector Packet Processing (VPP) | +| | The VPP platform is an extensible framework that provides | +| | out-of-the-box production quality switch/router functionality.| +| | Its high performance, proven technology, its modularity and, | +| | flexibility and rich feature set | +| | | ++--------------+---------------------------------------------------------------+ +|applicability | This VPP IPSEC test cases can be configured with different: | +| | | +| | * packet sizes; | +| | * test durations; | +| | * tolerated loss; | +| | * crypto device type; | +| | * number of physical cores; | +| | * number of tunnels; | +| | * number of connections; | +| | * encryption algorithms - integrity algorithm; | +| | | +| | Default values exist. | +| | | ++--------------+---------------------------------------------------------------+ +|pre-test | For Baremetal tests cases VPP and DPDK must be installed in | +|conditions | the hosts where the test is executed. The pod.yaml file must | +| | have the necessary system and NIC information | +| | | ++--------------+---------------------------------------------------------------+ +|test sequence | description and expected result | +| | | ++--------------+---------------------------------------------------------------+ +|step 1 | For Baremetal test: The TG and VNF are started on the hosts | +| | based on the pod file. | +| | | ++--------------+---------------------------------------------------------------+ +|step 2 | Yardstick is connected with the TG and VNF by using ssh. | +| | The test will resolve the topology and instantiate the VNF | +| | and TG and collect the KPI's/metrics. | +| | | ++--------------+---------------------------------------------------------------+ +|step 3 | Test packets are generated by TG on links to DUTs. If the | +| | number of dropped packets is more than the tolerated loss | +| | the line rate or throughput is halved. This is done until | +| | the dropped packets are within an acceptable tolerated loss. | +| | | +| | The KPI is the number of packets per second for a packet size | +| | specified in the test case with an accepted minimal packet | +| | loss for the default configuration. | +| | | ++--------------+---------------------------------------------------------------+ +|step 4 | In Baremetal test: The test quits the application and unbind | +| | the DPDK ports. | +| | | ++--------------+---------------------------------------------------------------+ +|test verdict | The test case will achieve a Throughput with an accepted | +| | minimal tolerated packet loss. | ++--------------+---------------------------------------------------------------+
\ No newline at end of file diff --git a/etc/yardstick/nodes/standalone/sipp_baremetal_pod.yaml b/etc/yardstick/nodes/standalone/sipp_baremetal_pod.yaml new file mode 100644 index 000000000..c7a0af1dd --- /dev/null +++ b/etc/yardstick/nodes/standalone/sipp_baremetal_pod.yaml @@ -0,0 +1,52 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. + +nodes: +- + name: sipp + role: TrafficGen + ip: 10.80.3.7 + user: USER_ROOT + password: PASSWORD_ROOT + interfaces: + xe0: # logical name from topology.yaml and vnfd.yaml + local_ip: "10.80.3.7" + netmask: "255.255.255.0" + local_mac: "90:e2:ba:7c:30:e8" + xe1: + local_ip: "10.80.3.7" + netmask: "255.255.255.0" + local_mac: "90:e2:ba:7c:30:e8" +- + name: pcscf + role: VirtualNetworkFunction + ip: 10.80.3.11 + user: USER_ROOT + password: PASSWORD_ROOT + interfaces: + xe0: + local_ip: "10.80.3.11" + netmask: "255.255.255.0" + local_mac: "90:e2:ba:7c:41:a8" +- + name: hss + role: VirtualNetworkFunction + ip: 10.80.3.11 + user: USER_ROOT + password: PASSWORD_ROOT + interfaces: + xe0: + local_ip: "10.80.3.11" + netmask: "255.255.255.0" + local_mac: "90:e2:ba:7c:41:a8" diff --git a/etc/yardstick/nodes/standalone/sipp_pod.yaml b/etc/yardstick/nodes/standalone/sipp_pod.yaml new file mode 100644 index 000000000..ae473c509 --- /dev/null +++ b/etc/yardstick/nodes/standalone/sipp_pod.yaml @@ -0,0 +1,30 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. + +nodes: +- + name: sipp + role: TrafficGen + ip: 10.80.3.7 + user: USER_ROOT + password: PASSWORD_ROOT + interfaces: + xe0: # logical name from topology.yaml and vnfd.yaml + local_ip: "10.80.3.7" + netmask: "255.255.255.0" + local_mac: "0c:c4:7a:de:8f:da" + xe1: # logical name from topology.yaml and vnfd.yaml + local_ip: "10.80.3.7" + netmask: "255.255.255.0" + local_mac: "0c:c4:7a:de:8f:da" diff --git a/samples/vnf_samples/nsut/cmts/k8s_vcmts_topology.yaml b/samples/vnf_samples/nsut/cmts/k8s_vcmts_topology.yaml new file mode 100755 index 000000000..95ac76964 --- /dev/null +++ b/samples/vnf_samples/nsut/cmts/k8s_vcmts_topology.yaml @@ -0,0 +1,36 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. + +{% set num_tg = get(extra_args, 'num_tg', 2) %} +{% set num_sg = get(extra_args, 'num_sg', 2) %} + +nsd:nsd-catalog: + nsd: + - id: vcmts-topology + name: vcmts-topology + short-name: vcmts-topology + description: vcmts-topology + constituent-vnfd: + {% for tg_num in range(0, num_tg) %} + - member-vnf-index: '{{ tg_num + 1 }}' + vnfd-id-ref: tg__{{ tg_num }} + VNF model: ../../vnf_descriptors/tg_vcmts_tpl.yaml #VNF type + {% endfor %} + {% for vnf_num in range(0, num_sg * 2) %} + - member-vnf-index: '{{ vnf_num + num_tg + 1 }}' + vnfd-id-ref: vnf__{{ vnf_num }} + VNF model: ../../vnf_descriptors/vnf_vcmts_tpl.yaml #VNF type + {% endfor %} + + vld: [] diff --git a/samples/vnf_samples/nsut/cmts/tc_vcmts_k8s_pktgen.yaml b/samples/vnf_samples/nsut/cmts/tc_vcmts_k8s_pktgen.yaml new file mode 100755 index 000000000..6c85a0892 --- /dev/null +++ b/samples/vnf_samples/nsut/cmts/tc_vcmts_k8s_pktgen.yaml @@ -0,0 +1,360 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. + + +{% set num_tg = 2 %} +{% set num_sg = 4 %} + +{% set vcmtsd_image = "vcmts-d:perf" %} +{% set qat_on = false %} + +schema: "yardstick:task:0.1" + +scenarios: +- type: NSPerf + traffic_profile: ../../traffic_profiles/fixed.yaml + extra_args: + num_sg: {{ num_sg }} + num_tg: {{ num_tg }} + topology: k8s_vcmts_topology.yaml + nodes: +{% for tg_num in range(0, num_tg) %} + tg__{{ tg_num }}: pktgen{{ tg_num }}-k8syardstick +{% endfor %} +{% for vnf_index in range(0, num_sg) %} + vnf__{{ vnf_index * 2 }}: vnf{{ vnf_index }}us-k8syardstick + vnf__{{ (vnf_index * 2) + 1}}: vnf{{ vnf_index }}ds-k8syardstick +{% endfor %} + + runner: + type: Duration + duration: 120 + + options: + vcmts_influxdb_ip: "10.80.5.150" + vcmts_influxdb_port: 8086 + vcmtsd_values: /etc/yardstick/vcmtsd_values.yaml + pktgen_values: /etc/yardstick/pktgen_values.yaml + pktgen_rate: 6.5 +{% for vnf_index in range(0, num_sg) %} + vnf__{{ vnf_index * 2 }}: + sg_id: {{ vnf_index }} + stream_dir: "us" + vnf__{{ (vnf_index * 2) + 1}}: + sg_id: {{ vnf_index }} + stream_dir: "ds" +{% endfor %} +{% for tg_num in range(0, num_tg) %} + tg__{{ tg_num }}: + pktgen_id: {{ tg_num }} +{% endfor %} + +context: + name: k8syardstick + type: Kubernetes + + servers: +{% for vnf_index in range(0, num_sg) %} + vnf{{ vnf_index }}us: + nodeSelector: + vcmts: "true" + containers: + - image: {{ vcmtsd_image }} + imagePullPolicy: IfNotPresent + env: + - name: CMK_PROC_FS + value: "/host/proc" + command: /bin/bash + args: ['-c', 'mkdir /root/.ssh; cp /tmp/.ssh/authorized_keys ~/.ssh/.; + chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; service ssh restart; + while true ; do sleep 10000; done'] + resources: + requests: + memory: 10Ki +{% if qat_on %} + hugepages-1Gi: 2Gi + qat.intel.com/generic: '1' +{% else %} + hugepages-1Gi: 1Gi +{% endif %} + limits: + memory: 1Gi +{% if qat_on %} + hugepages-1Gi: 2Gi + qat.intel.com/generic: '1' +{% else %} + hugepages-1Gi: 1Gi +{% endif %} + lifecycle: + postStart: + exec: + command: [ "/bin/bash", "-c", "env > /tmp/qat" ] + volumeMounts: + - name: vcmts-configmap-vcmtspod + mountPath: /vcmts-config + - name: hugepages + mountPath: /hugepages + readOnly: false + - name: collectd + mountPath: /opt/collectd/var + readOnly: false + - name: sysfs + mountPath: /sys + readOnly: false + - name: sriov + mountPath: /sriov-cni + readOnly: false + - name: host-proc + mountPath: /host/proc + readOnly: true + - name: cmk-install-dir + mountPath: /opt/bin + - name: cmk-conf-dir + mountPath: /etc/cmk + - name: power-mgmt + mountPath: /opt/power_mgmt + ports: + - containerPort: 22022 + securityContext: + allowPrivilegeEscalation: true + privileged: true + node_ports: + - name: lua # Lower case alphanumeric characters or '-' + port: 22022 + networks: + - flannel + - xe0 + - xe1 + volumes: + - name: vcmts-configmap-vcmtspod + configMap: + name: vcmts-configmap-vcmtspod + defaultMode: 0744 + - name: hugepages + emptyDir: + medium: HugePages + - name: collectd + hostPath: + path: /opt/collectd/var + - name: sysfs + hostPath: + path: /sys + - name: sriov + hostPath: + path: /var/lib/cni/sriov + - name: cmk-install-dir + hostPath: + path: /opt/bin + - name: host-proc + hostPath: + path: /proc + - name: cmk-conf-dir + hostPath: + path: /etc/cmk + - name: power-mgmt + hostPath: + path: /opt/power_mgmt + + vnf{{ vnf_index }}ds: + nodeSelector: + vcmts: "true" + containers: + - image: {{ vcmtsd_image }} + imagePullPolicy: IfNotPresent + env: + - name: CMK_PROC_FS + value: "/host/proc" + command: /bin/bash + args: ['-c', 'mkdir /root/.ssh; cp /tmp/.ssh/authorized_keys ~/.ssh/.; + chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; service ssh restart; + while true ; do sleep 10000; done'] + resources: + requests: + memory: 10Ki +{% if qat_on %} + hugepages-1Gi: 2Gi + qat.intel.com/generic: '1' +{% else %} + hugepages-1Gi: 1Gi +{% endif %} + limits: + memory: 1Gi +{% if qat_on %} + hugepages-1Gi: 2Gi + qat.intel.com/generic: '1' +{% else %} + hugepages-1Gi: 1Gi +{% endif %} + lifecycle: + postStart: + exec: + command: [ "/bin/bash", "-c", "env > /tmp/qat" ] + volumeMounts: + - name: vcmts-configmap-vcmtspod + mountPath: /vcmts-config + - name: hugepages + mountPath: /hugepages + readOnly: false + - name: collectd + mountPath: /opt/collectd/var + readOnly: false + - name: sysfs + mountPath: /sys + readOnly: false + - name: sriov + mountPath: /sriov-cni + readOnly: false + - name: host-proc + mountPath: /host/proc + readOnly: true + - name: cmk-install-dir + mountPath: /opt/bin + - name: cmk-conf-dir + mountPath: /etc/cmk + - name: power-mgmt + mountPath: /opt/power_mgmt + ports: + - containerPort: 22022 + securityContext: + allowPrivilegeEscalation: true + privileged: true + node_ports: + - name: lua # Lower case alphanumeric characters or '-' + port: 22022 + networks: + - flannel + - xe0 + - xe1 + volumes: + - name: vcmts-configmap-vcmtspod + configMap: + name: vcmts-configmap-vcmtspod + defaultMode: 0744 + - name: hugepages + emptyDir: + medium: HugePages + - name: collectd + hostPath: + path: /opt/collectd/var + - name: sysfs + hostPath: + path: /sys + - name: sriov + hostPath: + path: /var/lib/cni/sriov + - name: cmk-install-dir + hostPath: + path: /opt/bin + - name: host-proc + hostPath: + path: /proc + - name: cmk-conf-dir + hostPath: + path: /etc/cmk + - name: power-mgmt + hostPath: + path: /opt/power_mgmt +{% endfor %} + +{% for index in range(0, num_tg) %} + pktgen{{index}}: + nodeSelector: + vcmtspktgen: "true" + containers: + - image: vcmts-pktgen:v18.10 + imagePullPolicy: IfNotPresent + tty: true + stdin: true + env: + - name: LUA_PATH + value: "/vcmts/Pktgen.lua" + - name: CMK_PROC_FS + value: "/host/proc" + command: /bin/bash + args: ['-c', 'mkdir /root/.ssh; cp /tmp/.ssh/authorized_keys ~/.ssh/.; + chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; service ssh restart; + while true ; do sleep 10000; done'] + resources: + requests: + hugepages-1Gi: 9Gi + memory: 200Mi + limits: + hugepages-1Gi: 9Gi + memory: 200Mi + volumeMounts: + - name: sysfs + mountPath: /sys + readOnly: false + - name: hugepages + mountPath: /hugepages + readOnly: false + - name: sriov + mountPath: /sriov-cni + readOnly: false + - name: host-proc + mountPath: /host/proc + readOnly: true + - name: cmk-install-dir + mountPath: /opt/bin + - name: cmk-conf-dir + mountPath: /etc/cmk + - name: pktgen-config + mountPath: /pktgen-config + ports: + - containerPort: 22022 + securityContext: + allowPrivilegeEscalation: true + privileged: true + volumes: + - name: sysfs + hostPath: + path: /sys + - name: hugepages + emptyDir: + medium: HugePages + - name: sriov + hostPath: + path: /var/lib/cni/sriov + - name: cmk-install-dir + hostPath: + path: /opt/bin + - name: host-proc + hostPath: + path: /proc + - name: cmk-conf-dir + hostPath: + path: /etc/cmk + - name: pktgen-config + configMap: + name: vcmts-configmap-pktgen + defaultMode: 0744 + node_ports: + - name: lua # Lower case alphanumeric characters or '-' + port: 22022 + networks: + - flannel + - xe0 + - xe1 +{% endfor %} + + networks: + flannel: + args: '[{ "delegate": { "isDefaultGateway": true }}]' + plugin: flannel + xe0: + args: '[{ "delegate": { "isDefaultGateway": true }}]' + plugin: flannel + xe1: + args: '[{ "delegate": { "isDefaultGateway": true }}]' + plugin: flannel diff --git a/samples/vnf_samples/nsut/vims/tc_vims_baremetal_sipp.yaml b/samples/vnf_samples/nsut/vims/tc_vims_baremetal_sipp.yaml new file mode 100644 index 000000000..bf9f5a44a --- /dev/null +++ b/samples/vnf_samples/nsut/vims/tc_vims_baremetal_sipp.yaml @@ -0,0 +1,57 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. +--- +schema: yardstick:task:0.1 +run_in_parallel: false +scenarios: +- + type: NSPerf + traffic_profile: "../../traffic_profiles/sip.yaml" + topology: vims-topology.yaml + + nodes: + vnf__0: pcscf.yardstick + vnf__1: hss.yardstick + tg__0: sipp.yardstick + + runner: + type: Duration + duration: 10000 + interval: 1 + options: + port: 5060 + start_user: 1 + end_user: 10000 + init_reg_cps: 50 + init_reg_max: 5000 + reg_cps: 20 + reg_step: 10 + rereg_cps: 20 + rereg_step: 10 + dereg_cps: 20 + dereg_step: 10 + msgc_cps: 10 + msgc_step: 5 + run_mode: nortp + call_cps: 10 + hold_time: 15 + call_step: 5 + + wait_time: 5 + +context: + type: Node + name: yardstick + nfvi_type: baremetal + file: /etc/yardstick/nodes/sipp_baremetal_pod.yaml diff --git a/samples/vnf_samples/nsut/vims/tc_vims_heat_sipp.yaml b/samples/vnf_samples/nsut/vims/tc_vims_heat_sipp.yaml new file mode 100644 index 000000000..8bf59c962 --- /dev/null +++ b/samples/vnf_samples/nsut/vims/tc_vims_heat_sipp.yaml @@ -0,0 +1,97 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. +--- +schema: "yardstick:task:0.1" +run_in_parallel: false +scenarios: +- + type: NSPerf + traffic_profile: "../../traffic_profiles/sip.yaml" + topology: vims-topology.yaml + nodes: + tg__0: sipp.trafficgen + vnf__0: + name: bono-0.yardstick + public_ip_attr: "bono_public_ip" + private_ip_attr: "bono_private_ip" + interfaces: + ims_network: + local_ip: bono_private_ip + local_mac: bono_local_mac + netmask: "255.255.255.0" + network: private_net_name + gateway_ip: private_net_gateway + + vnf__1: + name: homestead-0.yardstick + public_ip_attr: "homestead_public_ip" + private_ip_attr: "homestead_private_ip" + interfaces: + ims_network: + local_ip: homestead_private_ip + local_mac: homestead_local_mac + netmask: "255.255.255.0" + network: private_net_name + gateway_ip: private_net_gateway + options: + # Public key to create keypair on openstack + key_name: yardstick + public_key: "" + port: 5060 + start_user: 1 + end_user: 10000 + init_reg_cps: 50 + init_reg_max: 5000 + reg_cps: 50 + reg_step: 10 + rereg_cps: 10 + rereg_step: 5 + dereg_cps: 10 + dereg_step: 5 + msgc_cps: 10 + msgc_step: 2 + run_mode: rtp + call_cps: 10 + hold_time: 15 + call_step: 5 + wait_time: 600 + runner: + type: Duration + duration: 10000 + interval: 1 +contexts: +# put node context first, so we don't HEAT deploy if node has errors +- + name: trafficgen + type: Node + nfvi_type: baremetal + file: /etc/yardstick/nodes/sipp_pod.yaml + +- + name: yardstick + user: ubuntu + # Private key to access VM + key_filename: /etc/yardstick/yardstick.pem + heat_template: /tmp/clearwater.yaml + heat_parameters: + image: "Ubuntu_14_trusty" + flavor: m1.small + key_name: yardstick + public_net_id: "public1" + repo_url: "http://repo.cw-ngv.com/archive/repo107/" + dnssec_key: "HOz2ZYgvbyCFRUymRLBXyLD4+tESevesP07hhzs/uKUD+Oy3aWvReEWMKgBwy75FAl9XQqw6wIGb" + private_net_name: "ims_network" + private_net_cidr: "10.0.1.0/24" + private_net_gateway: "10.0.1.1" + private_net_pool_start: "10.0.1.2" diff --git a/samples/vnf_samples/nsut/vims/vims-topology.yaml b/samples/vnf_samples/nsut/vims/vims-topology.yaml new file mode 100644 index 000000000..a7525ab57 --- /dev/null +++ b/samples/vnf_samples/nsut/vims/vims-topology.yaml @@ -0,0 +1,52 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. + +nsd:nsd-catalog: + nsd: + - id: vims-tg-topology + name: vims-tg-topology + short-name: vims-tg-topology + description: vims-tg-topology + constituent-vnfd: + - member-vnf-index: '1' + vnfd-id-ref: tg__0 + VNF model: ../../vnf_descriptors/tg_sipp_vnfd.yaml # Traffic Generator + - member-vnf-index: '2' + vnfd-id-ref: vnf__0 + VNF model: ../../vnf_descriptors/vims_pcscf_vnfd.yaml # vims + - member-vnf-index: '3' + vnfd-id-ref: vnf__1 + VNF model: ../../vnf_descriptors/vims_hss_vnfd.yaml # vims + + vld: + - id: ims_network + name: tg__0 to vnf__0 link 1 + type: ELAN + vnfd-connection-point-ref: + - member-vnf-index-ref: '1' + vnfd-connection-point-ref: xe0 + vnfd-id-ref: tg__0 + - member-vnf-index-ref: '2' + vnfd-connection-point-ref: xe0 + vnfd-id-ref: vnf__0 + - id: data_network + name: tg__0 to vnf__0 link 2 + type: ELAN + vnfd-connection-point-ref: + - member-vnf-index-ref: '1' + vnfd-connection-point-ref: xe1 + vnfd-id-ref: tg__0 + - member-vnf-index-ref: '2' + vnfd-connection-point-ref: xe0 + vnfd-id-ref: vnf__0 diff --git a/samples/vnf_samples/traffic_profiles/ipv4_throughput_latency_vpp.yaml b/samples/vnf_samples/traffic_profiles/ipv4_throughput_latency_vpp.yaml index 1add9bfb4..abbad6728 100644 --- a/samples/vnf_samples/traffic_profiles/ipv4_throughput_latency_vpp.yaml +++ b/samples/vnf_samples/traffic_profiles/ipv4_throughput_latency_vpp.yaml @@ -20,6 +20,7 @@ description: Traffic profile to run RFC2544 latency traffic_profile: traffic_type: VppRFC2544Profile # defines traffic behavior - constant or look for highest possible throughput enable_latency: true + intermediate_phases: 2 test_precision: 0.1 duration: 30 lower_bound: 1.0 diff --git a/samples/vnf_samples/vnf_descriptors/tg_sipp_vnfd.yaml b/samples/vnf_samples/vnf_descriptors/tg_sipp_vnfd.yaml new file mode 100644 index 000000000..4a9d7055b --- /dev/null +++ b/samples/vnf_samples/vnf_descriptors/tg_sipp_vnfd.yaml @@ -0,0 +1,54 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. + +vnfd:vnfd-catalog: + vnfd: + - id: SippVnf + name: SippVnf + short-name: SippVnf + description: ImsbenchSipp + mgmt-interface: + vdu-id: sippvnf-baremetal + {% if user is defined %} + user: '{{user}}' # Value filled by vnfdgen + {% endif %} + {% if password is defined %} + password: '{{password}}' # Value filled by vnfdgen + {% endif %} + {% if ip is defined %} + ip: '{{ip}}' # Value filled by vnfdgen + {% endif %} + {% if key_filename is defined %} + key_filename: '{{key_filename}}' # Value filled by vnfdgen + {% endif %} + vdu: + - id: sippvnf-baremetal + name: sippvnf-baremetal + description: Sipp + routing_table: {{ routing_table }} + external-interface: + - name: xe0 + virtual-interface: + local_ip: '{{ interfaces.xe0.local_ip }}' + dst_ip: '{{ interfaces.xe0.dst_ip }}' + vnfd-connection-point-ref: xe0 + - name: xe1 + virtual-interface: + local_ip: '{{ interfaces.xe1.local_ip }}' + vnfd-connection-point-ref: xe1 + benchmark: + kpi: + - packets_in + - packets_fwd + - packets_dropped diff --git a/samples/vnf_samples/vnf_descriptors/tg_vcmts_tpl.yaml b/samples/vnf_samples/vnf_descriptors/tg_vcmts_tpl.yaml new file mode 100755 index 000000000..bb56fcb6a --- /dev/null +++ b/samples/vnf_samples/vnf_descriptors/tg_vcmts_tpl.yaml @@ -0,0 +1,77 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. + +vnfd:vnfd-catalog: + vnfd: + - id: VcmtsPktgen + name: vcmtspktgen + short-name: vcmtspktgen + description: vCMTS Pktgen Kubernetes + vm-flavor: + vcpu-count: '4' + memory-mb: '4096' + mgmt-interface: + vdu-id: vcmtspktgen-kubernetes + {% if user is defined %} + user: '{{user}}' # Value filled by vnfdgen + {% endif %} + {% if password is defined %} + password: '{{password}}' # Value filled by vnfdgen + {% endif %} + {% if ip is defined %} + ip: '{{ip}}' # Value filled by vnfdgen + {% endif %} + {% if key_filename is defined %} + key_filename: '{{key_filename}}' # Value filled by vnfdgen + {% endif %} + connection-point: + - name: xe0 + type: VPORT + - name: xe1 + type: VPORT + vdu: + - id: vcmtspktgen-kubernetes + name: vcmtspktgen-kubernetes + description: vCMTS Pktgen Kubernetes + external-interface: + - name: xe0 + virtual-interface: + type: virtio + # Substitution variables MUST be quoted. Otherwise Python can misinterpet them. + vpci: '{{ interfaces.xe0.vpci }}' # Value filled by vnfdgen + local_iface_name: eth0 # '{{ interfaces.xe0.local_iface_name }}' + driver: '{{ interfaces.xe0.driver}}' # Value filled by vnfdgen + local_ip: '{{ interfaces.xe0.local_ip }}' # Value filled by vnfdgen + dst_ip: '{{ interfaces.xe0.dst_ip }}' # Value filled by vnfdgen + local_mac: '{{ interfaces.xe0.local_mac }}' # Value filled by vnfdgen + dst_mac: '{{ interfaces.xe0.dst_mac }}' # Value filled by vnfdgen + bandwidth: 10 Gbps + vnfd-connection-point-ref: xe0 + - name: xe1 + virtual-interface: + type: virtio + # Substitution variables MUST be quoted. Otherwise Python can misinterpet them. + vpci: '{{ interfaces.xe1.vpci }}' # Value filled by vnfdgen + local_iface_name: eth0 # '{{ interfaces.xe1.local_iface_name }}' + local_ip: '{{ interfaces.xe1.local_ip }}' # Value filled by vnfdgen + driver: '{{ interfaces.xe1.driver}}' # Value filled by vnfdgen + dst_ip: '{{ interfaces.xe1.dst_ip }}' # Value filled by vnfdgen + local_mac: '{{ interfaces.xe1.local_mac }}' # Value filled by vnfdgen + dst_mac: '{{ interfaces.xe1.dst_mac }}' # Value filled by vnfdgen + bandwidth: 10 Gbps + vnfd-connection-point-ref: xe0 + benchmark: + kpi: + - upstream/bits_per_second + diff --git a/samples/vnf_samples/vnf_descriptors/vims_hss_vnfd.yaml b/samples/vnf_samples/vnf_descriptors/vims_hss_vnfd.yaml new file mode 100644 index 000000000..835ca4987 --- /dev/null +++ b/samples/vnf_samples/vnf_descriptors/vims_hss_vnfd.yaml @@ -0,0 +1,54 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. + +vnfd:vnfd-catalog: + vnfd: + - id: VimsHssVnf + name: HssVnf + short-name: HssVnf + description: IMS Database + mgmt-interface: + vdu-id: HssVnf + {% if user is defined %} + user: '{{user}}' # Value filled by vnfdgen + {% endif %} + {% if password is defined %} + password: '{{password}}' # Value filled by vnfdgen + {% endif %} + {% if ip is defined %} + ip: '{{ip}}' # Value filled by vnfdgen + {% endif %} + {% if key_filename is defined %} + key_filename: '{{key_filename}}' # Value filled by vnfdgen + {% endif %} + vdu: + - id: HssVnf + name: HssVnf + description: IMS Database + external-interface: + - name: ims_network + virtual-interface: + local_ip: '{{ip}}' + local_mac: '{{local_mac}}' + netmask: '{{netmask}}' + vm-flavor: + vcpu-count: '4' + memory-mb: '4096' + routing_table: {{ routing_table }} + nd_route_tbl: {{ nd_route_tbl }} + benchmark: + kpi: + - packets_in + - packets_fwd + - packets_dropped diff --git a/samples/vnf_samples/vnf_descriptors/vims_pcscf_vnfd.yaml b/samples/vnf_samples/vnf_descriptors/vims_pcscf_vnfd.yaml new file mode 100644 index 000000000..02a96d037 --- /dev/null +++ b/samples/vnf_samples/vnf_descriptors/vims_pcscf_vnfd.yaml @@ -0,0 +1,54 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. + +vnfd:vnfd-catalog: + vnfd: + - id: VimsPcscfVnf + name: PcscfVnf + short-name: PcscfVnf + description: IMS Gateway + mgmt-interface: + vdu-id: PcscfVnf + {% if user is defined %} + user: '{{user}}' # Value filled by vnfdgen + {% endif %} + {% if password is defined %} + password: '{{password}}' # Value filled by vnfdgen + {% endif %} + {% if ip is defined %} + ip: '{{ip}}' # Value filled by vnfdgen + {% endif %} + {% if key_filename is defined %} + key_filename: '{{key_filename}}' # Value filled by vnfdgen + {% endif %} + vdu: + - id: PcscfVnf + name: PcscfVnf + description: VIMS Gateway + external-interface: + - name: ims_network + virtual-interface: + local_ip: '{{ip}}' + local_mac: '{{local_mac}}' + netmask: '{{netmask}}' + vm-flavor: + vcpu-count: '4' + memory-mb: '4096' + routing_table: {{ routing_table }} + nd_route_tbl: {{ nd_route_tbl }} + benchmark: + kpi: + - packets_in + - packets_fwd + - packets_dropped diff --git a/samples/vnf_samples/vnf_descriptors/vnf_vcmts_tpl.yaml b/samples/vnf_samples/vnf_descriptors/vnf_vcmts_tpl.yaml new file mode 100755 index 000000000..d1eb6a869 --- /dev/null +++ b/samples/vnf_samples/vnf_descriptors/vnf_vcmts_tpl.yaml @@ -0,0 +1,77 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. + +vnfd:vnfd-catalog: + vnfd: + - id: VcmtsVNF + name: vcmtsvnf + short-name: vcmtsvnf + description: vCMTS Upstream-Downstream Kubernetes + vm-flavor: + vcpu-count: '4' + memory-mb: '4096' + mgmt-interface: + vdu-id: vcmtsvnf-kubernetes + {% if user is defined %} + user: '{{user}}' # Value filled by vnfdgen + {% endif %} + {% if password is defined %} + password: '{{password}}' # Value filled by vnfdgen + {% endif %} + {% if ip is defined %} + ip: '{{ip}}' # Value filled by vnfdgen + {% endif %} + {% if key_filename is defined %} + key_filename: '{{key_filename}}' # Value filled by vnfdgen + {% endif %} + connection-point: + - name: xe0 + type: VPORT + - name: xe1 + type: VPORT + vdu: + - id: vcmtsvnf-kubernetes + name: vcmtsvnf-kubernetes + description: vCMTS Upstream-Downstream Kubernetes + external-interface: + - name: xe0 + virtual-interface: + type: virtio + # Substitution variables MUST be quoted. Otherwise Python can misinterpet them. + vpci: '{{ interfaces.xe0.vpci }}' # Value filled by vnfdgen + local_iface_name: eth0 # NOT TESTED YET '{{ interfaces.xe0.local_iface_name }}' # Value filled by vnfdgen + driver: '{{ interfaces.xe0.driver}}' # Value filled by vnfdgen + local_ip: '{{ interfaces.xe0.local_ip }}' # Value filled by vnfdgen + dst_ip: '{{ interfaces.xe0.dst_ip }}' # Value filled by vnfdgen + local_mac: '{{ interfaces.xe0.local_mac }}' # Value filled by vnfdgen + dst_mac: '{{ interfaces.xe0.dst_mac }}' # Value filled by vnfdgen + bandwidth: 10 Gbps + vnfd-connection-point-ref: xe0 + - name: xe1 + virtual-interface: + type: virtio + # Substitution variables MUST be quoted. Otherwise Python can misinterpet them. + vpci: '{{ interfaces.xe1.vpci }}' # Value filled by vnfdgen + local_iface_name: eth0 # NOT TESTED YET '{{ interfaces.xe1.local_iface_name }}' # Value filled by vnfdgen + local_ip: '{{ interfaces.xe1.local_ip }}' # Value filled by vnfdgen + driver: '{{ interfaces.xe1.driver}}' # Value filled by vnfdgen + dst_ip: '{{ interfaces.xe1.dst_ip }}' # Value filled by vnfdgen + local_mac: '{{ interfaces.xe1.local_mac }}' # Value filled by vnfdgen + dst_mac: '{{ interfaces.xe1.dst_mac }}' # Value filled by vnfdgen + bandwidth: 10 Gbps + vnfd-connection-point-ref: xe0 + benchmark: + kpi: + - upstream/bits_per_second + @@ -33,7 +33,8 @@ setup( 'network_services/nfvi/collectd.sh', 'resources/files/*', 'resources/scripts/install/*.bash', - 'resources/scripts/remove/*.bash' + 'resources/scripts/remove/*.bash', + 'resources/templates/*.vat' ], 'etc': [ 'yardstick/nodes/*/*.yaml', diff --git a/tools/run_tests.sh b/tools/run_tests.sh index 49f628eec..40b21cab9 100755 --- a/tools/run_tests.sh +++ b/tools/run_tests.sh @@ -66,16 +66,21 @@ run_functional_test() { fi } -if [[ $opts =~ "--unit" ]]; then - run_tests -fi +# get file types of the last change on git +file_types=$(git diff-tree -r --name-only HEAD~1 HEAD | awk -F[/] '{print $NF}' | awk -F[.] 'NF>1 {print $NF}' | uniq) -if [[ $opts =~ "--coverage" ]]; then - run_coverage -fi +if [[ $file_types =~ "py" ]]; then + if [[ $opts =~ "--unit" ]]; then + run_tests + fi -if [[ $opts =~ "--functional" ]]; then - run_functional_test + if [[ $opts =~ "--coverage" ]]; then + run_coverage + fi + + if [[ $opts =~ "--functional" ]]; then + run_functional_test + fi fi if [[ -z $opts ]]; then diff --git a/yardstick/network_services/helpers/cpu.py b/yardstick/network_services/helpers/cpu.py index 8cdd829a0..279af204a 100644 --- a/yardstick/network_services/helpers/cpu.py +++ b/yardstick/network_services/helpers/cpu.py @@ -15,11 +15,15 @@ import io +# Number of threads per core. +NR_OF_THREADS = 2 + class CpuSysCores(object): def __init__(self, connection=""): self.core_map = {} + self.cpuinfo = {} self.connection = connection def _open_cpuinfo(self): @@ -62,13 +66,14 @@ class CpuSysCores(object): def get_cpu_layout(self): _, stdout, _ = self.connection.execute("lscpu -p") - cpuinfo = {} - cpuinfo['cpuinfo'] = list() + self.cpuinfo = {} + self.cpuinfo['cpuinfo'] = list() for line in stdout.split("\n"): if line and line[0] != "#": - cpuinfo['cpuinfo'].append([CpuSysCores._str2int(x) for x in - line.split(",")]) - return cpuinfo + self.cpuinfo['cpuinfo'].append( + [CpuSysCores._str2int(x) for x in + line.split(",")]) + return self.cpuinfo def validate_cpu_cfg(self, vnf_cfg=None): if vnf_cfg is None: @@ -89,9 +94,80 @@ class CpuSysCores(object): return 0 + def is_smt_enabled(self): + return CpuSysCores.smt_enabled(self.cpuinfo) + + def cpu_list_per_node(self, cpu_node, smt_used=False): + cpu_node = int(cpu_node) + cpu_info = self.cpuinfo.get("cpuinfo") + if cpu_info is None: + raise RuntimeError("Node cpuinfo not available.") + + smt_enabled = self.is_smt_enabled() + if not smt_enabled and smt_used: + raise RuntimeError("SMT is not enabled.") + + cpu_list = [] + for cpu in cpu_info: + if cpu[3] == cpu_node: + cpu_list.append(cpu[0]) + + if not smt_enabled or smt_enabled and smt_used: + pass + + if smt_enabled and not smt_used: + cpu_list_len = len(cpu_list) + cpu_list = cpu_list[:int(cpu_list_len / NR_OF_THREADS)] + + return cpu_list + + def cpu_slice_of_list_per_node(self, cpu_node, skip_cnt=0, cpu_cnt=0, + smt_used=False): + cpu_list = self.cpu_list_per_node(cpu_node, smt_used) + + cpu_list_len = len(cpu_list) + if cpu_cnt + skip_cnt > cpu_list_len: + raise RuntimeError("cpu_cnt + skip_cnt > length(cpu list).") + + if cpu_cnt == 0: + cpu_cnt = cpu_list_len - skip_cnt + + if smt_used: + cpu_list_0 = cpu_list[:int(cpu_list_len / NR_OF_THREADS)] + cpu_list_1 = cpu_list[int(cpu_list_len / NR_OF_THREADS):] + cpu_list = [cpu for cpu in cpu_list_0[skip_cnt:skip_cnt + cpu_cnt]] + cpu_list_ex = [cpu for cpu in + cpu_list_1[skip_cnt:skip_cnt + cpu_cnt]] + cpu_list.extend(cpu_list_ex) + else: + cpu_list = [cpu for cpu in cpu_list[skip_cnt:skip_cnt + cpu_cnt]] + + return cpu_list + + def cpu_list_per_node_str(self, cpu_node, skip_cnt=0, cpu_cnt=0, sep=",", + smt_used=False): + cpu_list = self.cpu_slice_of_list_per_node(cpu_node, + skip_cnt=skip_cnt, + cpu_cnt=cpu_cnt, + smt_used=smt_used) + return sep.join(str(cpu) for cpu in cpu_list) + @staticmethod def _str2int(string): try: return int(string) except ValueError: return 0 + + @staticmethod + def smt_enabled(cpuinfo): + cpu_info = cpuinfo.get("cpuinfo") + if cpu_info is None: + raise RuntimeError("Node cpuinfo not available.") + cpu_mems = [item[-4:] for item in cpu_info] + cpu_mems_len = int(len(cpu_mems) / NR_OF_THREADS) + count = 0 + for cpu_mem in cpu_mems[:cpu_mems_len]: + if cpu_mem in cpu_mems[cpu_mems_len:]: + count += 1 + return count == cpu_mems_len diff --git a/yardstick/network_services/helpers/dpdkbindnic_helper.py b/yardstick/network_services/helpers/dpdkbindnic_helper.py index 0fc63f88d..33a5e8c1d 100644 --- a/yardstick/network_services/helpers/dpdkbindnic_helper.py +++ b/yardstick/network_services/helpers/dpdkbindnic_helper.py @@ -13,7 +13,6 @@ # limitations under the License. import logging import os - import re from collections import defaultdict from itertools import chain @@ -21,7 +20,6 @@ from itertools import chain from yardstick.common import exceptions from yardstick.common.utils import validate_non_string_sequence - NETWORK_KERNEL = 'network_kernel' NETWORK_DPDK = 'network_dpdk' NETWORK_OTHER = 'network_other' @@ -288,12 +286,18 @@ printf "%s/driver:" $1 ; basename $(readlink -s $1/device/driver); } \ raise DpdkBindHelperException(template.format(self.dpdk_devbind, res[0])) return res - def load_dpdk_driver(self): + def load_dpdk_driver(self, dpdk_driver=None): + if dpdk_driver is None: + dpdk_driver = self.dpdk_driver cmd_template = "sudo modprobe {} && sudo modprobe {}" - self.ssh_helper.execute(cmd_template.format(self.UIO_DRIVER, self.dpdk_driver)) - - def check_dpdk_driver(self): - return self.ssh_helper.execute("lsmod | grep -i {}".format(self.dpdk_driver))[0] + self.ssh_helper.execute( + cmd_template.format(self.UIO_DRIVER, dpdk_driver)) + + def check_dpdk_driver(self, dpdk_driver=None): + if dpdk_driver is None: + dpdk_driver = self.dpdk_driver + return \ + self.ssh_helper.execute("lsmod | grep -i {}".format(dpdk_driver))[0] @property def _status_cmd(self): diff --git a/yardstick/network_services/helpers/vpp_helpers/__init__.py b/yardstick/network_services/helpers/vpp_helpers/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/network_services/helpers/vpp_helpers/__init__.py diff --git a/yardstick/network_services/helpers/vpp_helpers/abstract_search_algorithm.py b/yardstick/network_services/helpers/vpp_helpers/abstract_search_algorithm.py new file mode 100644 index 000000000..fced05833 --- /dev/null +++ b/yardstick/network_services/helpers/vpp_helpers/abstract_search_algorithm.py @@ -0,0 +1,53 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. +# +# This is a modified copy of +# https://gerrit.fd.io/r/gitweb?p=csit.git;a=blob_plain;f=resources/libraries/python/MLRsearch/AbstractSearchAlgorithm.py;hb=HEAD + + +from abc import ABCMeta, abstractmethod + + +class AbstractSearchAlgorithm(object): + """Abstract class defining common API for search algorithms.""" + + __metaclass__ = ABCMeta + + def __init__(self, measurer): + """Store the rate provider. + + :param measurer: Object able to perform trial or composite measurements. + :type measurer: AbstractMeasurer.AbstractMeasurer + """ + # TODO: Type check for AbstractMeasurer? + self.measurer = measurer + + @abstractmethod + def narrow_down_ndr_and_pdr( + self, fail_rate, line_rate, packet_loss_ratio): + """Perform measurements to narrow down intervals, return them. + + This will be renamed when custom loss ratio lists are supported. + + :param fail_rate: Minimal target transmit rate [pps]. + :param line_rate: Maximal target transmit rate [pps]. + :param packet_loss_ratio: Fraction of packets lost, for PDR [1]. + :type fail_rate: float + :type line_rate: float + :type packet_loss_ratio: float + :returns: Structure containing narrowed down intervals + and their measurements. + :rtype: NdrPdrResult.NdrPdrResult + """ + # TODO: Do we agree on arguments related to precision or trial duration? diff --git a/yardstick/network_services/helpers/vpp_helpers/multiple_loss_ratio_search.py b/yardstick/network_services/helpers/vpp_helpers/multiple_loss_ratio_search.py new file mode 100644 index 000000000..582e3dc27 --- /dev/null +++ b/yardstick/network_services/helpers/vpp_helpers/multiple_loss_ratio_search.py @@ -0,0 +1,688 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. +# +# This is a modified copy of +# https://gerrit.fd.io/r/gitweb?p=csit.git;a=blob_plain;f=resources/libraries/python/MLRsearch/MultipleLossRatioSearch.py;hb=HEAD + +import datetime +import logging +import math +import time + +from yardstick.network_services.helpers.vpp_helpers.abstract_search_algorithm import \ + AbstractSearchAlgorithm +from yardstick.network_services.helpers.vpp_helpers.ndr_pdr_result import \ + NdrPdrResult +from yardstick.network_services.helpers.vpp_helpers.receive_rate_interval import \ + ReceiveRateInterval +from yardstick.network_services.helpers.vpp_helpers.receive_rate_measurement import \ + ReceiveRateMeasurement + +LOGGING = logging.getLogger(__name__) + + +class MultipleLossRatioSearch(AbstractSearchAlgorithm): + """Optimized binary search algorithm for finding NDR and PDR bounds. + + Traditional binary search algorithm needs initial interval + (lower and upper bound), and returns final interval after bisecting + (until some exit condition is met). + The exit condition is usually related to the interval width, + (upper bound value minus lower bound value). + + The optimized algorithm contains several improvements + aimed to reduce overall search time. + + One improvement is searching for two intervals at once. + The intervals are for NDR (No Drop Rate) and PDR (Partial Drop Rate). + + Next improvement is that the initial interval does not need to be valid. + Imagine initial interval (10, 11) where 11 is smaller + than the searched value. + The algorithm will try (11, 13) interval next, and if 13 is still smaller, + (13, 17) and so on, doubling width until the upper bound is valid. + The part when interval expands is called external search, + the part when interval is bisected is called internal search. + + Next improvement is that trial measurements at small trial duration + can be used to find a reasonable interval for full trial duration search. + This results in more trials performed, but smaller overall duration + in general. + + Next improvement is bisecting in logarithmic quantities, + so that exit criteria can be independent of measurement units. + + Next improvement is basing the initial interval on receive rates. + + Final improvement is exiting early if the minimal value + is not a valid lower bound. + + The complete search consist of several phases, + each phase performing several trial measurements. + Initial phase creates initial interval based on receive rates + at maximum rate and at maximum receive rate (MRR). + Final phase and preceding intermediate phases are performing + external and internal search steps, + each resulting interval is the starting point for the next phase. + The resulting interval of final phase is the result of the whole algorithm. + + Each non-initial phase uses its own trial duration and width goal. + Any non-initial phase stops searching (for NDR or PDR independently) + when minimum is not a valid lower bound (at current duration), + or all of the following is true: + Both bounds are valid, bound bounds are measured at the current phase + trial duration, interval width is less than the width goal + for current phase. + + TODO: Review and update this docstring according to rst docs. + TODO: Support configurable number of Packet Loss Ratios. + """ + + class ProgressState(object): + """Structure containing data to be passed around in recursion.""" + + def __init__( + self, result, phases, duration, width_goal, packet_loss_ratio, + minimum_transmit_rate, maximum_transmit_rate): + """Convert and store the argument values. + + :param result: Current measured NDR and PDR intervals. + :param phases: How many intermediate phases to perform + before the current one. + :param duration: Trial duration to use in the current phase [s]. + :param width_goal: The goal relative width for the curreent phase. + :param packet_loss_ratio: PDR fraction for the current search. + :param minimum_transmit_rate: Minimum target transmit rate + for the current search [pps]. + :param maximum_transmit_rate: Maximum target transmit rate + for the current search [pps]. + :type result: NdrPdrResult.NdrPdrResult + :type phases: int + :type duration: float + :type width_goal: float + :type packet_loss_ratio: float + :type minimum_transmit_rate: float + :type maximum_transmit_rate: float + """ + self.result = result + self.phases = int(phases) + self.duration = float(duration) + self.width_goal = float(width_goal) + self.packet_loss_ratio = float(packet_loss_ratio) + self.minimum_transmit_rate = float(minimum_transmit_rate) + self.maximum_transmit_rate = float(maximum_transmit_rate) + + def __init__(self, measurer, latency=False, pkt_size=64, + final_relative_width=0.005, + final_trial_duration=30.0, initial_trial_duration=1.0, + number_of_intermediate_phases=2, timeout=600.0, doublings=1): + """Store the measurer object and additional arguments. + + :param measurer: Rate provider to use by this search object. + :param final_relative_width: Final lower bound transmit rate + cannot be more distant that this multiple of upper bound [1]. + :param final_trial_duration: Trial duration for the final phase [s]. + :param initial_trial_duration: Trial duration for the initial phase + and also for the first intermediate phase [s]. + :param number_of_intermediate_phases: Number of intermediate phases + to perform before the final phase [1]. + :param timeout: The search will fail itself when not finished + before this overall time [s]. + :param doublings: How many doublings to do in external search step. + Default 1 is suitable for fairly stable tests, + less stable tests might get better overal duration with 2 or more. + :type measurer: AbstractMeasurer.AbstractMeasurer + :type final_relative_width: float + :type final_trial_duration: float + :type initial_trial_duration: int + :type number_of_intermediate_phases: int + :type timeout: float + :type doublings: int + """ + super(MultipleLossRatioSearch, self).__init__(measurer) + self.latency = latency + self.pkt_size = int(pkt_size) + self.final_trial_duration = float(final_trial_duration) + self.final_relative_width = float(final_relative_width) + self.number_of_intermediate_phases = int(number_of_intermediate_phases) + self.initial_trial_duration = float(initial_trial_duration) + self.timeout = float(timeout) + self.doublings = int(doublings) + + self.queue = None + self.port_pg_id = None + self.ports = [] + self.test_data = {} + self.profiles = {} + + @staticmethod + def double_relative_width(relative_width): + """Return relative width corresponding to double logarithmic width. + + :param relative_width: The base relative width to double. + :type relative_width: float + :returns: The relative width of double logarithmic size. + :rtype: float + """ + return 1.999 * relative_width - relative_width * relative_width + # The number should be 2.0, but we want to avoid rounding errors, + # and ensure half of double is not larger than the original value. + + @staticmethod + def double_step_down(relative_width, current_bound): + """Return rate of double logarithmic width below. + + :param relative_width: The base relative width to double. + :param current_bound: The current target transmit rate to move [pps]. + :type relative_width: float + :type current_bound: float + :returns: Transmit rate smaller by logarithmically double width [pps]. + :rtype: float + """ + return current_bound * ( + 1.0 - MultipleLossRatioSearch.double_relative_width( + relative_width)) + + @staticmethod + def expand_down(relative_width, doublings, current_bound): + """Return rate of expanded logarithmic width below. + + :param relative_width: The base relative width to double. + :param doublings: How many doublings to do for expansion. + :param current_bound: The current target transmit rate to move [pps]. + :type relative_width: float + :type doublings: int + :type current_bound: float + :returns: Transmit rate smaller by logarithmically double width [pps]. + :rtype: float + """ + for _ in range(doublings): + relative_width = MultipleLossRatioSearch.double_relative_width( + relative_width) + return current_bound * (1.0 - relative_width) + + @staticmethod + def double_step_up(relative_width, current_bound): + """Return rate of double logarithmic width above. + + :param relative_width: The base relative width to double. + :param current_bound: The current target transmit rate to move [pps]. + :type relative_width: float + :type current_bound: float + :returns: Transmit rate larger by logarithmically double width [pps]. + :rtype: float + """ + return current_bound / ( + 1.0 - MultipleLossRatioSearch.double_relative_width( + relative_width)) + + @staticmethod + def expand_up(relative_width, doublings, current_bound): + """Return rate of expanded logarithmic width above. + + :param relative_width: The base relative width to double. + :param doublings: How many doublings to do for expansion. + :param current_bound: The current target transmit rate to move [pps]. + :type relative_width: float + :type doublings: int + :type current_bound: float + :returns: Transmit rate smaller by logarithmically double width [pps]. + :rtype: float + """ + for _ in range(doublings): + relative_width = MultipleLossRatioSearch.double_relative_width( + relative_width) + return current_bound / (1.0 - relative_width) + + @staticmethod + def half_relative_width(relative_width): + """Return relative width corresponding to half logarithmic width. + + :param relative_width: The base relative width to halve. + :type relative_width: float + :returns: The relative width of half logarithmic size. + :rtype: float + """ + return 1.0 - math.sqrt(1.0 - relative_width) + + @staticmethod + def half_step_up(relative_width, current_bound): + """Return rate of half logarithmic width above. + + :param relative_width: The base relative width to halve. + :param current_bound: The current target transmit rate to move [pps]. + :type relative_width: float + :type current_bound: float + :returns: Transmit rate larger by logarithmically half width [pps]. + :rtype: float + """ + return current_bound / ( + 1.0 - MultipleLossRatioSearch.half_relative_width( + relative_width)) + + def init_generator(self, ports, port_pg_id, profiles, test_data, queue): + self.ports = ports + self.port_pg_id = port_pg_id + self.profiles = profiles + self.test_data = test_data + self.queue = queue + self.queue.cancel_join_thread() + + def collect_kpi(self, stats, test_value): + samples = self.measurer.generate_samples(stats, self.ports, + self.port_pg_id, self.latency) + samples.update(self.test_data) + LOGGING.info("Collect TG KPIs %s %s %s", datetime.datetime.now(), + test_value, samples) + self.queue.put(samples) + + def narrow_down_ndr_and_pdr( + self, minimum_transmit_rate, maximum_transmit_rate, + packet_loss_ratio): + """Perform initial phase, create state object, proceed with next phases. + + :param minimum_transmit_rate: Minimal target transmit rate [pps]. + :param maximum_transmit_rate: Maximal target transmit rate [pps]. + :param packet_loss_ratio: Fraction of packets lost, for PDR [1]. + :type minimum_transmit_rate: float + :type maximum_transmit_rate: float + :type packet_loss_ratio: float + :returns: Structure containing narrowed down intervals + and their measurements. + :rtype: NdrPdrResult.NdrPdrResult + :raises RuntimeError: If total duration is larger than timeout. + """ + minimum_transmit_rate = float(minimum_transmit_rate) + maximum_transmit_rate = float(maximum_transmit_rate) + packet_loss_ratio = float(packet_loss_ratio) + line_measurement = self.measure( + self.initial_trial_duration, maximum_transmit_rate, self.latency) + initial_width_goal = self.final_relative_width + for _ in range(self.number_of_intermediate_phases): + initial_width_goal = self.double_relative_width(initial_width_goal) + max_lo = maximum_transmit_rate * (1.0 - initial_width_goal) + mrr = max( + minimum_transmit_rate, + min(max_lo, line_measurement.receive_rate)) + mrr_measurement = self.measure( + self.initial_trial_duration, mrr, self.latency) + # Attempt to get narrower width. + if mrr_measurement.loss_fraction > 0.0: + max2_lo = mrr * (1.0 - initial_width_goal) + mrr2 = min(max2_lo, mrr_measurement.receive_rate) + else: + mrr2 = mrr / (1.0 - initial_width_goal) + if mrr2 > minimum_transmit_rate and mrr2 < maximum_transmit_rate: + line_measurement = mrr_measurement + mrr_measurement = self.measure( + self.initial_trial_duration, mrr2, self.latency) + if mrr2 > mrr: + buf = line_measurement + line_measurement = mrr_measurement + mrr_measurement = buf + starting_interval = ReceiveRateInterval( + mrr_measurement, line_measurement) + starting_result = NdrPdrResult(starting_interval, starting_interval) + state = self.ProgressState( + starting_result, self.number_of_intermediate_phases, + self.final_trial_duration, self.final_relative_width, + packet_loss_ratio, minimum_transmit_rate, maximum_transmit_rate) + state = self.ndrpdr(state) + result = state.result + # theor_max_thruput = 0 + result_samples = {} + + MultipleLossRatioSearch.display_single_bound(result_samples, + 'NDR_LOWER', result.ndr_interval.measured_low.transmit_rate, + self.pkt_size, result.ndr_interval.measured_low.latency) + MultipleLossRatioSearch.display_single_bound(result_samples, + 'NDR_UPPER', result.ndr_interval.measured_high.transmit_rate, + self.pkt_size) + MultipleLossRatioSearch.display_single_bound(result_samples, + 'PDR_LOWER', result.pdr_interval.measured_low.transmit_rate, + self.pkt_size, result.pdr_interval.measured_low.latency) + MultipleLossRatioSearch.display_single_bound(result_samples, + 'PDR_UPPER', result.pdr_interval.measured_high.transmit_rate, + self.pkt_size) + pdr_msg = self.check_ndrpdr_interval_validity(result_samples, "PDR", + result.pdr_interval, + packet_loss_ratio) + ndr_msg = self.check_ndrpdr_interval_validity(result_samples, "NDR", + result.ndr_interval) + self.queue.put(result_samples) + + LOGGING.debug("result_samples: %s", result_samples) + LOGGING.info(pdr_msg) + LOGGING.info(ndr_msg) + + self.perform_additional_measurements_based_on_ndrpdr_result(result) + + return result_samples + + def _measure_and_update_state(self, state, transmit_rate): + """Perform trial measurement, update bounds, return new state. + + :param state: State before this measurement. + :param transmit_rate: Target transmit rate for this measurement [pps]. + :type state: ProgressState + :type transmit_rate: float + :returns: State after the measurement. + :rtype: ProgressState + """ + # TODO: Implement https://stackoverflow.com/a/24683360 + # to avoid the string manipulation if log verbosity is too low. + LOGGING.info("result before update: %s", state.result) + LOGGING.debug( + "relative widths in goals: %s", state.result.width_in_goals( + self.final_relative_width)) + measurement = self.measure(state.duration, transmit_rate, self.latency) + ndr_interval = self._new_interval( + state.result.ndr_interval, measurement, 0.0) + pdr_interval = self._new_interval( + state.result.pdr_interval, measurement, state.packet_loss_ratio) + state.result = NdrPdrResult(ndr_interval, pdr_interval) + return state + + @staticmethod + def _new_interval(old_interval, measurement, packet_loss_ratio): + """Return new interval with bounds updated according to the measurement. + + :param old_interval: The current interval before the measurement. + :param measurement: The new meaqsurement to take into account. + :param packet_loss_ratio: Fraction for PDR (or zero for NDR). + :type old_interval: ReceiveRateInterval.ReceiveRateInterval + :type measurement: ReceiveRateMeasurement.ReceiveRateMeasurement + :type packet_loss_ratio: float + :returns: The updated interval. + :rtype: ReceiveRateInterval.ReceiveRateInterval + """ + old_lo, old_hi = old_interval.measured_low, old_interval.measured_high + # Priority zero: direct replace if the target Tr is the same. + if measurement.target_tr in (old_lo.target_tr, old_hi.target_tr): + if measurement.target_tr == old_lo.target_tr: + return ReceiveRateInterval(measurement, old_hi) + else: + return ReceiveRateInterval(old_lo, measurement) + # Priority one: invalid lower bound allows only one type of update. + if old_lo.loss_fraction > packet_loss_ratio: + # We can only expand down, old bound becomes valid upper one. + if measurement.target_tr < old_lo.target_tr: + return ReceiveRateInterval(measurement, old_lo) + else: + return old_interval + # Lower bound is now valid. + # Next priorities depend on target Tr. + if measurement.target_tr < old_lo.target_tr: + # Lower external measurement, relevant only + # if the new measurement has high loss rate. + if measurement.loss_fraction > packet_loss_ratio: + # Returning the broader interval as old_lo + # would be invalid upper bound. + return ReceiveRateInterval(measurement, old_hi) + elif measurement.target_tr > old_hi.target_tr: + # Upper external measurement, only relevant for invalid upper bound. + if old_hi.loss_fraction <= packet_loss_ratio: + # Old upper bound becomes valid new lower bound. + return ReceiveRateInterval(old_hi, measurement) + else: + # Internal measurement, replaced boundary + # depends on measured loss fraction. + if measurement.loss_fraction > packet_loss_ratio: + # We have found a narrow valid interval, + # regardless of whether old upper bound was valid. + return ReceiveRateInterval(old_lo, measurement) + else: + # In ideal world, we would not want to shrink interval + # if upper bound is not valid. + # In the real world, we want to shrink it for + # "invalid upper bound at maximal rate" case. + return ReceiveRateInterval(measurement, old_hi) + # Fallback, the interval is unchanged by the measurement. + return old_interval + + def ndrpdr(self, state): + """Pefrom trials for this phase. Return the new state when done. + + :param state: State before this phase. + :type state: ProgressState + :returns: The updated state. + :rtype: ProgressState + :raises RuntimeError: If total duration is larger than timeout. + """ + start_time = time.time() + if state.phases > 0: + # We need to finish preceding intermediate phases first. + saved_phases = state.phases + state.phases -= 1 + # Preceding phases have shorter duration. + saved_duration = state.duration + duration_multiplier = state.duration / self.initial_trial_duration + phase_exponent = float(state.phases) / saved_phases + state.duration = self.initial_trial_duration * math.pow( + duration_multiplier, phase_exponent) + # Shorter durations do not need that narrow widths. + saved_width = state.width_goal + state.width_goal = self.double_relative_width(state.width_goal) + # Recurse. + state = self.ndrpdr(state) + # Restore the state for current phase. + state.duration = saved_duration + state.width_goal = saved_width + state.phases = saved_phases # Not needed, but just in case. + LOGGING.info( + "starting iterations with duration %s and relative width goal %s", + state.duration, state.width_goal) + while 1: + if time.time() > start_time + self.timeout: + raise RuntimeError("Optimized search takes too long.") + # Order of priorities: invalid bounds (nl, pl, nh, ph), + # then narrowing relative Tr widths. + # Durations are not priorities yet, + # they will settle on their own hopefully. + ndr_lo = state.result.ndr_interval.measured_low + ndr_hi = state.result.ndr_interval.measured_high + pdr_lo = state.result.pdr_interval.measured_low + pdr_hi = state.result.pdr_interval.measured_high + ndr_rel_width = max( + state.width_goal, state.result.ndr_interval.rel_tr_width) + pdr_rel_width = max( + state.width_goal, state.result.pdr_interval.rel_tr_width) + # If we are hitting maximal or minimal rate, we cannot shift, + # but we can re-measure. + if ndr_lo.loss_fraction > 0.0: + if ndr_lo.target_tr > state.minimum_transmit_rate: + new_tr = max( + state.minimum_transmit_rate, + self.expand_down( + ndr_rel_width, self.doublings, ndr_lo.target_tr)) + LOGGING.info("ndr lo external %s", new_tr) + state = self._measure_and_update_state(state, new_tr) + continue + elif ndr_lo.duration < state.duration: + LOGGING.info("ndr lo minimal re-measure") + state = self._measure_and_update_state( + state, state.minimum_transmit_rate) + continue + if pdr_lo.loss_fraction > state.packet_loss_ratio: + if pdr_lo.target_tr > state.minimum_transmit_rate: + new_tr = max( + state.minimum_transmit_rate, + self.expand_down( + pdr_rel_width, self.doublings, pdr_lo.target_tr)) + LOGGING.info("pdr lo external %s", new_tr) + state = self._measure_and_update_state(state, new_tr) + continue + elif pdr_lo.duration < state.duration: + LOGGING.info("pdr lo minimal re-measure") + state = self._measure_and_update_state( + state, state.minimum_transmit_rate) + continue + if ndr_hi.loss_fraction <= 0.0: + if ndr_hi.target_tr < state.maximum_transmit_rate: + new_tr = min( + state.maximum_transmit_rate, + self.expand_up( + ndr_rel_width, self.doublings, ndr_hi.target_tr)) + LOGGING.info("ndr hi external %s", new_tr) + state = self._measure_and_update_state(state, new_tr) + continue + elif ndr_hi.duration < state.duration: + LOGGING.info("ndr hi maximal re-measure") + state = self._measure_and_update_state( + state, state.maximum_transmit_rate) + continue + if pdr_hi.loss_fraction <= state.packet_loss_ratio: + if pdr_hi.target_tr < state.maximum_transmit_rate: + new_tr = min( + state.maximum_transmit_rate, + self.expand_up( + pdr_rel_width, self.doublings, pdr_hi.target_tr)) + LOGGING.info("pdr hi external %s", new_tr) + state = self._measure_and_update_state(state, new_tr) + continue + elif pdr_hi.duration < state.duration: + LOGGING.info("ndr hi maximal re-measure") + state = self._measure_and_update_state( + state, state.maximum_transmit_rate) + continue + # If we are hitting maximum_transmit_rate, + # it is still worth narrowing width, + # hoping large enough loss fraction will happen. + # But if we are hitting the minimal rate (at current duration), + # no additional measurement will help with that, + # so we can stop narrowing in this phase. + if (ndr_lo.target_tr <= state.minimum_transmit_rate + and ndr_lo.loss_fraction > 0.0): + ndr_rel_width = 0.0 + if (pdr_lo.target_tr <= state.minimum_transmit_rate + and pdr_lo.loss_fraction > state.packet_loss_ratio): + pdr_rel_width = 0.0 + if ndr_rel_width > state.width_goal: + # We have to narrow NDR width first, as NDR internal search + # can invalidate PDR (but not vice versa). + new_tr = self.half_step_up(ndr_rel_width, ndr_lo.target_tr) + LOGGING.info("Bisecting for NDR at %s", new_tr) + state = self._measure_and_update_state(state, new_tr) + continue + if pdr_rel_width > state.width_goal: + # PDR iternal search. + new_tr = self.half_step_up(pdr_rel_width, pdr_lo.target_tr) + LOGGING.info("Bisecting for PDR at %s", new_tr) + state = self._measure_and_update_state(state, new_tr) + continue + # We do not need to improve width, but there still might be + # some measurements with smaller duration. + # We need to re-measure with full duration, possibly + # creating invalid bounds to resolve (thus broadening width). + if ndr_lo.duration < state.duration: + LOGGING.info("re-measuring NDR lower bound") + state = self._measure_and_update_state(state, ndr_lo.target_tr) + continue + if pdr_lo.duration < state.duration: + LOGGING.info("re-measuring PDR lower bound") + state = self._measure_and_update_state(state, pdr_lo.target_tr) + continue + # Except when lower bounds have high loss fraction, in that case + # we do not need to re-measure _upper_ bounds. + if ndr_hi.duration < state.duration and ndr_rel_width > 0.0: + LOGGING.info("re-measuring NDR upper bound") + state = self._measure_and_update_state(state, ndr_hi.target_tr) + continue + if pdr_hi.duration < state.duration and pdr_rel_width > 0.0: + LOGGING.info("re-measuring PDR upper bound") + state = self._measure_and_update_state(state, pdr_hi.target_tr) + continue + # Widths are narrow (or lower bound minimal), bound measurements + # are long enough, we can return. + LOGGING.info("phase done") + break + return state + + def measure(self, duration, transmit_rate, latency): + duration = float(duration) + transmit_rate = float(transmit_rate) + # Trex needs target Tr per stream, but reports aggregate Tx and Dx. + unit_rate = str(transmit_rate / 2.0) + "pps" + stats = self.measurer.send_traffic_on_tg(self.ports, self.port_pg_id, + duration, unit_rate, + latency=latency) + self.measurer.client.reset(ports=self.ports) + self.measurer.client.clear_stats(ports=self.ports) + self.measurer.client.remove_all_streams(ports=self.ports) + for port, profile in self.profiles.items(): + self.measurer.client.add_streams(profile, ports=[port]) + self.collect_kpi(stats, unit_rate) + transmit_count = int(self.measurer.sent) + loss_count = int(self.measurer.loss) + measurement = ReceiveRateMeasurement( + duration, transmit_rate, transmit_count, loss_count) + measurement.latency = self.measurer.latency + return measurement + + def perform_additional_measurements_based_on_ndrpdr_result(self, result): + duration = 5.0 + rate = "{}{}".format(result.ndr_interval.measured_low.target_tr / 2.0, + 'pps') + for _ in range(0, 1): + stats = self.measurer.send_traffic_on_tg(self.ports, + self.port_pg_id, duration, + rate) + self.collect_kpi(stats, rate) + LOGGING.info('Traffic loss occurred: %s', self.measurer.loss) + + @staticmethod + def display_single_bound(result_samples, result_type, rate_total, pkt_size, + latency=None): + bandwidth_total = float(rate_total) * (pkt_size + 20) * 8 / (10 ** 9) + + result_samples["Result_{}".format(result_type)] = { + "rate_total_pps": float(rate_total), + "bandwidth_total_Gbps": float(bandwidth_total), + } + + if latency: + for item in latency: + if latency.index(item) == 0: + name = "Result_{}_{}".format("stream0", result_type) + else: + name = "Result_{}_{}".format("stream1", result_type) + lat_min, lat_avg, lat_max = item.split('/') + result_samples[name] = { + "min_latency": float(lat_min), + "avg_latency": float(lat_avg), + "max_latency": float(lat_max), + } + + @staticmethod + def check_ndrpdr_interval_validity(result_samples, result_type, interval, + packet_loss_ratio=0.0): + lower_bound = interval.measured_low + lower_bound_lf = lower_bound.loss_fraction + + result_samples["Result_{}_packets_lost".format(result_type)] = { + "packet_loss_ratio": float(lower_bound_lf), + "packets_lost": float(lower_bound.loss_count), + } + + if lower_bound_lf <= packet_loss_ratio: + return "Minimal rate loss fraction {} reach target {}".format( + lower_bound_lf, packet_loss_ratio) + else: + message = "Minimal rate loss fraction {} does not reach target {}".format( + lower_bound_lf, packet_loss_ratio) + if lower_bound_lf >= 1.0: + return '{}\nZero packets forwarded!'.format(message) + else: + return '{}\n{} packets lost.'.format(message, + lower_bound.loss_count) diff --git a/yardstick/network_services/helpers/vpp_helpers/ndr_pdr_result.py b/yardstick/network_services/helpers/vpp_helpers/ndr_pdr_result.py new file mode 100644 index 000000000..34a97f9fb --- /dev/null +++ b/yardstick/network_services/helpers/vpp_helpers/ndr_pdr_result.py @@ -0,0 +1,68 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. +# +# This is a modified copy of +# https://gerrit.fd.io/r/gitweb?p=csit.git;a=blob_plain;f=resources/libraries/python/MLRsearch/NdrPdrResult.py;hb=HEAD + +from yardstick.network_services.helpers.vpp_helpers.receive_rate_interval import \ + ReceiveRateInterval + + +class NdrPdrResult(object): + """Two measurement intervals, return value of search algorithms. + + Partial fraction is NOT part of the result. Pdr interval should be valid + for all partial fractions implied by the interval.""" + + def __init__(self, ndr_interval, pdr_interval): + """Store the measured intervals after checking argument types. + + :param ndr_interval: Object containing data for NDR part of the result. + :param pdr_interval: Object containing data for PDR part of the result. + :type ndr_interval: ReceiveRateInterval.ReceiveRateInterval + :type pdr_interval: ReceiveRateInterval.ReceiveRateInterval + """ + # TODO: Type checking is not very pythonic, + # perhaps users can fix wrong usage without it? + if not isinstance(ndr_interval, ReceiveRateInterval): + raise TypeError("ndr_interval, is not a ReceiveRateInterval: " + "{ndr!r}".format(ndr=ndr_interval)) + if not isinstance(pdr_interval, ReceiveRateInterval): + raise TypeError("pdr_interval, is not a ReceiveRateInterval: " + "{pdr!r}".format(pdr=pdr_interval)) + self.ndr_interval = ndr_interval + self.pdr_interval = pdr_interval + + def width_in_goals(self, relative_width_goal): + """Return a debug string related to current widths in logarithmic scale. + + :param relative_width_goal: Upper bound times this is the goal + difference between upper bound and lower bound. + :type relative_width_goal: float + :returns: Message containing NDR and PDR widths in goals. + :rtype: str + """ + return "ndr {ndr_in_goals}; pdr {pdr_in_goals}".format( + ndr_in_goals=self.ndr_interval.width_in_goals(relative_width_goal), + pdr_in_goals=self.pdr_interval.width_in_goals(relative_width_goal)) + + def __str__(self): + """Return string as tuple of named values.""" + return "NDR={ndr!s};PDR={pdr!s}".format( + ndr=self.ndr_interval, pdr=self.pdr_interval) + + def __repr__(self): + """Return string evaluable as a constructor call.""" + return "NdrPdrResult(ndr_interval={ndr!r},pdr_interval={pdr!r})".format( + ndr=self.ndr_interval, pdr=self.pdr_interval) diff --git a/yardstick/network_services/helpers/vpp_helpers/receive_rate_interval.py b/yardstick/network_services/helpers/vpp_helpers/receive_rate_interval.py new file mode 100644 index 000000000..517a99c1f --- /dev/null +++ b/yardstick/network_services/helpers/vpp_helpers/receive_rate_interval.py @@ -0,0 +1,88 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. +# +# This is a modified copy of +# https://gerrit.fd.io/r/gitweb?p=csit.git;a=blob_plain;f=resources/libraries/python/MLRsearch/ReceiveRateInterval.py;hb=HEAD + +import math + +from yardstick.network_services.helpers.vpp_helpers.receive_rate_measurement import \ + ReceiveRateMeasurement + + +class ReceiveRateInterval(object): + """Structure defining two Rr measurements, and their relation.""" + + def __init__(self, measured_low, measured_high): + """Store the bound measurements after checking argument types. + + :param measured_low: Measurement for the lower bound. + :param measured_high: Measurement for the upper bound. + :type measured_low: ReceiveRateMeasurement.ReceiveRateMeasurement + :type measured_high: ReceiveRateMeasurement.ReceiveRateMeasurement + """ + # TODO: Type checking is not very pythonic, + # perhaps users can fix wrong usage without it? + if not isinstance(measured_low, ReceiveRateMeasurement): + raise TypeError("measured_low is not a ReceiveRateMeasurement: " + "{low!r}".format(low=measured_low)) + if not isinstance(measured_high, ReceiveRateMeasurement): + raise TypeError("measured_high is not a ReceiveRateMeasurement: " + "{high!r}".format(high=measured_high)) + self.measured_low = measured_low + self.measured_high = measured_high + # Declare secondary quantities to appease pylint. + self.abs_tr_width = None + """Absolute width of target transmit rate. Upper minus lower.""" + self.rel_tr_width = None + """Relative width of target transmit rate. Absolute divided by upper.""" + self.sort() + + def sort(self): + """Sort bounds by target Tr, compute secondary quantities.""" + if self.measured_low.target_tr > self.measured_high.target_tr: + self.measured_low, self.measured_high = ( + self.measured_high, self.measured_low) + self.abs_tr_width = ( + self.measured_high.target_tr - self.measured_low.target_tr) + self.rel_tr_width = round( + self.abs_tr_width / self.measured_high.target_tr, 5) + + def width_in_goals(self, relative_width_goal): + """Return float value. + + Relative width goal is some (negative) value on logarithmic scale. + Current relative width is another logarithmic value. + Return the latter divided by the former. + This is useful when investigating how did surprising widths come to be. + + :param relative_width_goal: Upper bound times this is the goal + difference between upper bound and lower bound. + :type relative_width_goal: float + :returns: Current width as logarithmic multiple of goal width [1]. + :rtype: float + """ + return round(math.log(1.0 - self.rel_tr_width) / math.log( + 1.0 - relative_width_goal), 5) + + def __str__(self): + """Return string as half-open interval.""" + return "[{low!s};{high!s})".format( + low=self.measured_low, high=self.measured_high) + + def __repr__(self): + """Return string evaluable as a constructor call.""" + return ("ReceiveRateInterval(measured_low={low!r}" + ",measured_high={high!r})".format(low=self.measured_low, + high=self.measured_high)) diff --git a/yardstick/network_services/helpers/vpp_helpers/receive_rate_measurement.py b/yardstick/network_services/helpers/vpp_helpers/receive_rate_measurement.py new file mode 100644 index 000000000..2c59ea104 --- /dev/null +++ b/yardstick/network_services/helpers/vpp_helpers/receive_rate_measurement.py @@ -0,0 +1,58 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. +# +# This is a modified copy of +# https://gerrit.fd.io/r/gitweb?p=csit.git;a=blob_plain;f=resources/libraries/python/MLRsearch/ReceiveRateMeasurement.py;hb=HEAD + + +class ReceiveRateMeasurement(object): + """Structure defining the result of single Rr measurement.""" + + def __init__(self, duration, target_tr, transmit_count, loss_count): + """Constructor, normalize primary and compute secondary quantities. + + :param duration: Measurement duration [s]. + :param target_tr: Target transmit rate [pps]. + If bidirectional traffic is measured, this is bidirectional rate. + :param transmit_count: Number of packets transmitted [1]. + :param loss_count: Number of packets transmitted but not received [1]. + :type duration: float + :type target_tr: float + :type transmit_count: int + :type loss_count: int + """ + self.duration = float(duration) + self.target_tr = float(target_tr) + self.transmit_count = int(transmit_count) + self.loss_count = int(loss_count) + self.receive_count = round(transmit_count - loss_count, 5) + self.transmit_rate = round(transmit_count / self.duration, 5) + self.loss_rate = round(loss_count / self.duration, 5) + self.receive_rate = round(self.receive_count / self.duration, 5) + self.loss_fraction = round( + float(self.loss_count) / self.transmit_count, 5) + # TODO: Do we want to store also the real time (duration + overhead)? + + def __str__(self): + """Return string reporting input and loss fraction.""" + return "d={dur!s},Tr={rate!s},Df={frac!s}".format( + dur=self.duration, rate=self.target_tr, frac=self.loss_fraction) + + def __repr__(self): + """Return string evaluable as a constructor call.""" + return ("ReceiveRateMeasurement(duration={dur!r},target_tr={rate!r}" + ",transmit_count={trans!r},loss_count={loss!r})".format( + dur=self.duration, rate=self.target_tr, + trans=self.transmit_count, + loss=self.loss_count)) diff --git a/yardstick/network_services/traffic_profile/vpp_rfc2544.py b/yardstick/network_services/traffic_profile/vpp_rfc2544.py index 0f8185f90..412e4e69a 100644 --- a/yardstick/network_services/traffic_profile/vpp_rfc2544.py +++ b/yardstick/network_services/traffic_profile/vpp_rfc2544.py @@ -13,17 +13,19 @@ # limitations under the License. import datetime +import ipaddress import logging -from random import choice -from string import ascii_letters +import random +import string -from ipaddress import ip_address from trex_stl_lib import api as Pkt from trex_stl_lib import trex_stl_client from trex_stl_lib import trex_stl_packet_builder_scapy from trex_stl_lib import trex_stl_streams from yardstick.common import constants +from yardstick.network_services.helpers.vpp_helpers.multiple_loss_ratio_search import \ + MultipleLossRatioSearch from yardstick.network_services.traffic_profile.rfc2544 import RFC2544Profile, \ PortPgIDMap from yardstick.network_services.traffic_profile.trex_traffic_profile import IP, \ @@ -37,6 +39,10 @@ class VppRFC2544Profile(RFC2544Profile): def __init__(self, traffic_generator): super(VppRFC2544Profile, self).__init__(traffic_generator) + tp_cfg = traffic_generator["traffic_profile"] + self.number_of_intermediate_phases = tp_cfg.get("intermediate_phases", + 2) + self.duration = self.config.duration self.precision = self.config.test_precision self.lower_bound = self.config.lower_bound @@ -85,7 +91,7 @@ class VppRFC2544Profile(RFC2544Profile): def _gen_payload(length): payload = "" for _ in range(length): - payload += choice(ascii_letters) + payload += random.choice(string.ascii_letters) return payload @@ -153,8 +159,9 @@ class VppRFC2544Profile(RFC2544Profile): dst=dst_start_ip, proto=outer_l3v4['proto']) if self.flow > 1: - dst_start_int = int(ip_address(str(dst_start_ip))) - dst_end_ip_new = ip_address(dst_start_int + self.flow - 1) + dst_start_int = int(ipaddress.ip_address(str(dst_start_ip))) + dst_end_ip_new = ipaddress.ip_address( + dst_start_int + self.flow - 1) # self._set_proto_addr(IP, SRC, outer_l3v4['srcip4'], outer_l3v4['count']) self._set_proto_addr(IP, DST, "{start_ip}-{end_ip}".format( @@ -248,8 +255,19 @@ class VppRFC2544Profile(RFC2544Profile): def binary_search_with_optimized(self, traffic_generator, duration, timeout, test_data): - # TODO Support FD.io Multiple Loss Ratio search (MLRsearch) - pass + self.queue.cancel_join_thread() + algorithm = MultipleLossRatioSearch( + measurer=traffic_generator, latency=self.enable_latency, + pkt_size=self.pkt_size, + final_trial_duration=duration, + final_relative_width=self.step_interval / 100, + number_of_intermediate_phases=self.number_of_intermediate_phases, + initial_trial_duration=1, + timeout=timeout) + algorithm.init_generator(self.ports, self.port_pg_id, self.profiles, + test_data, self.queue) + return algorithm.narrow_down_ndr_and_pdr(10000, self.max_rate, + self.tolerance_high) def binary_search(self, traffic_generator, duration, tolerance_value, test_data): diff --git a/yardstick/network_services/vnf_generic/vnf/ipsec_vnf.py b/yardstick/network_services/vnf_generic/vnf/ipsec_vnf.py index 75a8cce06..1961ac1b1 100644 --- a/yardstick/network_services/vnf_generic/vnf/ipsec_vnf.py +++ b/yardstick/network_services/vnf_generic/vnf/ipsec_vnf.py @@ -15,18 +15,46 @@ import logging import re import time +from collections import Counter +from enum import Enum from yardstick.benchmark.contexts.base import Context from yardstick.common.process import check_if_process_failed from yardstick.network_services import constants -from yardstick.network_services.helpers.cpu import CpuSysCores from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNF from yardstick.network_services.vnf_generic.vnf.vpp_helpers import \ - VppSetupEnvHelper + VppSetupEnvHelper, VppConfigGenerator LOG = logging.getLogger(__name__) +class CryptoAlg(Enum): + """Encryption algorithms.""" + AES_CBC_128 = ('aes-cbc-128', 'AES-CBC', 16) + AES_CBC_192 = ('aes-cbc-192', 'AES-CBC', 24) + AES_CBC_256 = ('aes-cbc-256', 'AES-CBC', 32) + AES_GCM_128 = ('aes-gcm-128', 'AES-GCM', 20) + + def __init__(self, alg_name, scapy_name, key_len): + self.alg_name = alg_name + self.scapy_name = scapy_name + self.key_len = key_len + + +class IntegAlg(Enum): + """Integrity algorithms.""" + SHA1_96 = ('sha1-96', 'HMAC-SHA1-96', 20) + SHA_256_128 = ('sha-256-128', 'SHA2-256-128', 32) + SHA_384_192 = ('sha-384-192', 'SHA2-384-192', 48) + SHA_512_256 = ('sha-512-256', 'SHA2-512-256', 64) + AES_GCM_128 = ('aes-gcm-128', 'AES-GCM', 20) + + def __init__(self, alg_name, scapy_name, key_len): + self.alg_name = alg_name + self.scapy_name = scapy_name + self.key_len = key_len + + class VipsecApproxSetupEnvHelper(VppSetupEnvHelper): DEFAULT_IPSEC_VNF_CFG = { 'crypto_type': 'SW_cryptodev', @@ -99,17 +127,51 @@ class VipsecApproxSetupEnvHelper(VppSetupEnvHelper): return flow_src_start_ip def build_config(self): - # TODO Implement later - pass + vnf_cfg = self.scenario_helper.options.get('vnf_config', + self.DEFAULT_IPSEC_VNF_CFG) + rxq = vnf_cfg.get('rxq', 1) + phy_cores = vnf_cfg.get('worker_threads', 1) + # worker_config = vnf_cfg.get('worker_config', '1C/1T').split('/')[1].lower() + + vpp_cfg = self.create_startup_configuration_of_vpp() + self.add_worker_threads_and_rxqueues(vpp_cfg, phy_cores, rxq) + self.add_pci_devices(vpp_cfg) + + frame_size_cfg = self.scenario_helper.all_options.get('framesize', {}) + uplink_cfg = frame_size_cfg.get('uplink', {}) + downlink_cfg = frame_size_cfg.get('downlink', {}) + framesize = min(self.calculate_frame_size(uplink_cfg), + self.calculate_frame_size(downlink_cfg)) + if framesize < 1522: + vpp_cfg.add_dpdk_no_multi_seg() + + crypto_algorithms = self._get_crypto_algorithms() + if crypto_algorithms == 'aes-gcm': + self.add_dpdk_cryptodev(vpp_cfg, 'aesni_gcm', phy_cores) + elif crypto_algorithms == 'cbc-sha1': + self.add_dpdk_cryptodev(vpp_cfg, 'aesni_mb', phy_cores) + + vpp_cfg.add_dpdk_dev_default_rxd(2048) + vpp_cfg.add_dpdk_dev_default_txd(2048) + self.apply_config(vpp_cfg, True) + self.update_vpp_interface_data() def setup_vnf_environment(self): resource = super(VipsecApproxSetupEnvHelper, self).setup_vnf_environment() self.start_vpp_service() - - sys_cores = CpuSysCores(self.ssh_helper) - self._update_vnfd_helper(sys_cores.get_cpu_layout()) + # for QAT device DH895xCC, the number of VFs is required as 32 + if self._get_crypto_type() == 'HW_cryptodev': + sriov_numvfs = self.get_sriov_numvfs( + self.find_encrypted_data_interface()["vpci"]) + if sriov_numvfs != 32: + self.crypto_device_init( + self.find_encrypted_data_interface()["vpci"], 32) + + self._update_vnfd_helper(self.sys_cores.get_cpu_layout()) + self.update_vpp_interface_data() + self.iface_update_numa() return resource @@ -144,12 +206,90 @@ class VipsecApproxSetupEnvHelper(VppSetupEnvHelper): return ipsec_created def get_vpp_statistics(self): - # TODO Implement later - return None + cmd = "vppctl show int {intf}" + result = {} + for interface in self.vnfd_helper.interfaces: + iface_name = self.get_value_by_interface_key( + interface["virtual-interface"]["ifname"], "vpp_name") + command = cmd.format(intf=iface_name) + _, stdout, _ = self.ssh_helper.execute(command) + result.update( + self.parser_vpp_stats(interface["virtual-interface"]["ifname"], + iface_name, stdout)) + self.ssh_helper.execute("vppctl clear interfaces") + return result + + @staticmethod + def parser_vpp_stats(interface, iface_name, stats): + packets_in = 0 + packets_fwd = 0 + packets_dropped = 0 + result = {} + + entries = re.split(r"\n+", stats) + tmp = [re.split(r"\s\s+", entry, 5) for entry in entries] + + for item in tmp: + if isinstance(item, list): + if item[0] == iface_name and len(item) >= 5: + if item[3] == 'rx packets': + packets_in = int(item[4]) + elif item[4] == 'rx packets': + packets_in = int(item[5]) + elif len(item) == 3: + if item[1] == 'tx packets': + packets_fwd = int(item[2]) + elif item[1] == 'drops' or item[1] == 'rx-miss': + packets_dropped = int(item[2]) + if packets_dropped == 0 and packets_in > 0 and packets_fwd > 0: + packets_dropped = abs(packets_fwd - packets_in) + + result[interface] = { + 'packets_in': packets_in, + 'packets_fwd': packets_fwd, + 'packets_dropped': packets_dropped, + } + + return result def create_ipsec_tunnels(self): - # TODO Implement later - pass + self.initialize_ipsec() + + # TODO generate the same key + crypto_algorithms = self._get_crypto_algorithms() + if crypto_algorithms == 'aes-gcm': + encr_alg = CryptoAlg.AES_GCM_128 + auth_alg = IntegAlg.AES_GCM_128 + encr_key = 'LNYZXMBQDKESNLREHJMS' + auth_key = 'SWGLDTYZSQKVBZZMPIEV' + elif crypto_algorithms == 'cbc-sha1': + encr_alg = CryptoAlg.AES_CBC_128 + auth_alg = IntegAlg.SHA1_96 + encr_key = 'IFEMSHYLCZIYFUTT' + auth_key = 'PEALEIPSCPTRHYJSDXLY' + + self.execute_script("enable_dpdk_traces.vat", json_out=False) + self.execute_script("enable_vhost_user_traces.vat", json_out=False) + self.execute_script("enable_memif_traces.vat", json_out=False) + + node_name = self.find_encrypted_data_interface()["node_name"] + n_tunnels = self._get_n_tunnels() + n_connections = self._get_n_connections() + flow_dst_start_ip = self._get_flow_dst_start_ip() + if node_name == "vnf__0": + self.vpp_create_ipsec_tunnels( + self.find_encrypted_data_interface()["local_ip"], + self.find_encrypted_data_interface()["peer_intf"]["local_ip"], + self.find_encrypted_data_interface()["ifname"], + n_tunnels, n_connections, encr_alg, encr_key, auth_alg, + auth_key, flow_dst_start_ip) + elif node_name == "vnf__1": + self.vpp_create_ipsec_tunnels( + self.find_encrypted_data_interface()["local_ip"], + self.find_encrypted_data_interface()["peer_intf"]["local_ip"], + self.find_encrypted_data_interface()["ifname"], + n_tunnels, n_connections, encr_alg, encr_key, auth_alg, + auth_key, flow_dst_start_ip, 20000, 10000) def find_raw_data_interface(self): try: @@ -160,6 +300,145 @@ class VipsecApproxSetupEnvHelper(VppSetupEnvHelper): def find_encrypted_data_interface(self): return self.vnfd_helper.find_virtual_interface(vld_id="ciphertext") + def create_startup_configuration_of_vpp(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_unix_log() + vpp_config_generator.add_unix_cli_listen() + vpp_config_generator.add_unix_nodaemon() + vpp_config_generator.add_unix_coredump() + vpp_config_generator.add_dpdk_socketmem('1024,1024') + vpp_config_generator.add_dpdk_no_tx_checksum_offload() + vpp_config_generator.add_dpdk_log_level('debug') + for interface in self.vnfd_helper.interfaces: + vpp_config_generator.add_dpdk_uio_driver( + interface["virtual-interface"]["driver"]) + vpp_config_generator.add_heapsize('4G') + # TODO Enable configuration depend on VPP version + vpp_config_generator.add_statseg_size('4G') + vpp_config_generator.add_plugin('disable', ['default']) + vpp_config_generator.add_plugin('enable', ['dpdk_plugin.so']) + vpp_config_generator.add_ip6_hash_buckets('2000000') + vpp_config_generator.add_ip6_heap_size('4G') + vpp_config_generator.add_ip_heap_size('4G') + return vpp_config_generator + + def add_worker_threads_and_rxqueues(self, vpp_cfg, phy_cores, + rx_queues=None): + thr_count_int = phy_cores + cpu_count_int = phy_cores + num_mbufs_int = 32768 + + numa_list = [] + + if_list = [self.find_encrypted_data_interface()["ifname"], + self.find_raw_data_interface()["ifname"]] + for if_key in if_list: + try: + numa_list.append( + self.get_value_by_interface_key(if_key, 'numa_node')) + except KeyError: + pass + numa_cnt_mc = Counter(numa_list).most_common() + + if numa_cnt_mc and numa_cnt_mc[0][0] is not None and \ + numa_cnt_mc[0][0] != -1: + numa = numa_cnt_mc[0][0] + elif len(numa_cnt_mc) > 1 and numa_cnt_mc[0][0] == -1: + numa = numa_cnt_mc[1][0] + else: + numa = 0 + + try: + smt_used = self.sys_cores.is_smt_enabled() + except KeyError: + smt_used = False + + cpu_main = self.sys_cores.cpu_list_per_node_str(numa, skip_cnt=1, + cpu_cnt=1) + cpu_wt = self.sys_cores.cpu_list_per_node_str(numa, skip_cnt=2, + cpu_cnt=cpu_count_int, + smt_used=smt_used) + + if smt_used: + thr_count_int = 2 * cpu_count_int + + if rx_queues is None: + rxq_count_int = int(thr_count_int / 2) + else: + rxq_count_int = rx_queues + + if rxq_count_int == 0: + rxq_count_int = 1 + + num_mbufs_int = num_mbufs_int * rxq_count_int + + vpp_cfg.add_cpu_main_core(cpu_main) + vpp_cfg.add_cpu_corelist_workers(cpu_wt) + vpp_cfg.add_dpdk_dev_default_rxq(rxq_count_int) + vpp_cfg.add_dpdk_num_mbufs(num_mbufs_int) + + def add_pci_devices(self, vpp_cfg): + pci_devs = [self.find_encrypted_data_interface()["vpci"], + self.find_raw_data_interface()["vpci"]] + vpp_cfg.add_dpdk_dev(*pci_devs) + + def add_dpdk_cryptodev(self, vpp_cfg, sw_pmd_type, count): + crypto_type = self._get_crypto_type() + smt_used = self.sys_cores.is_smt_enabled() + cryptodev = self.find_encrypted_data_interface()["vpci"] + socket_id = self.get_value_by_interface_key( + self.find_encrypted_data_interface()["ifname"], "numa_node") + + if smt_used: + thr_count_int = count * 2 + if crypto_type == 'HW_cryptodev': + vpp_cfg.add_dpdk_cryptodev(thr_count_int, cryptodev) + else: + vpp_cfg.add_dpdk_sw_cryptodev(sw_pmd_type, socket_id, + thr_count_int) + else: + thr_count_int = count + if crypto_type == 'HW_cryptodev': + vpp_cfg.add_dpdk_cryptodev(thr_count_int, cryptodev) + else: + vpp_cfg.add_dpdk_sw_cryptodev(sw_pmd_type, socket_id, + thr_count_int) + + def initialize_ipsec(self): + flow_src_start_ip = self._get_flow_src_start_ip() + + self.set_interface_state( + self.find_encrypted_data_interface()["ifname"], 'up') + self.set_interface_state(self.find_raw_data_interface()["ifname"], + 'up') + self.vpp_interfaces_ready_wait() + self.vpp_set_interface_mtu( + self.find_encrypted_data_interface()["ifname"]) + self.vpp_set_interface_mtu(self.find_raw_data_interface()["ifname"]) + self.vpp_interfaces_ready_wait() + + self.set_ip(self.find_encrypted_data_interface()["ifname"], + self.find_encrypted_data_interface()["local_ip"], 24) + self.set_ip(self.find_raw_data_interface()["ifname"], + self.find_raw_data_interface()["local_ip"], + 24) + + self.add_arp_on_dut(self.find_encrypted_data_interface()["ifname"], + self.find_encrypted_data_interface()["peer_intf"][ + "local_ip"], + self.find_encrypted_data_interface()["peer_intf"][ + "local_mac"]) + self.add_arp_on_dut(self.find_raw_data_interface()["ifname"], + self.find_raw_data_interface()["peer_intf"][ + "local_ip"], + self.find_raw_data_interface()["peer_intf"][ + "local_mac"]) + + self.vpp_route_add(flow_src_start_ip, 8, + self.find_raw_data_interface()["peer_intf"][ + "local_ip"], + self.find_raw_data_interface()["ifname"]) + class VipsecApproxVnf(SampleVNF): """ This class handles vIPSEC VNF model-driver definitions """ diff --git a/yardstick/network_services/vnf_generic/vnf/tg_vcmts_pktgen.py b/yardstick/network_services/vnf_generic/vnf/tg_vcmts_pktgen.py new file mode 100755 index 000000000..c6df9d04c --- /dev/null +++ b/yardstick/network_services/vnf_generic/vnf/tg_vcmts_pktgen.py @@ -0,0 +1,215 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. + +import logging +import time +import socket +import yaml +import os + +from yardstick.network_services.vnf_generic.vnf import sample_vnf +from yardstick.common import exceptions + + +LOG = logging.getLogger(__name__) + + +class PktgenHelper(object): + + RETRY_SECONDS = 0.5 + RETRY_COUNT = 20 + CONNECT_TIMEOUT = 5 + + def __init__(self, host, port=23000): + self.host = host + self.port = port + self.connected = False + + def _connect(self): + self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + ret = True + try: + self._sock.settimeout(self.CONNECT_TIMEOUT) + self._sock.connect((self.host, self.port)) + except (socket.gaierror, socket.error, socket.timeout): + self._sock.close() + ret = False + + return ret + + def connect(self): + if self.connected: + return True + LOG.info("Connecting to pktgen instance at %s...", self.host) + for idx in range(self.RETRY_COUNT): + self.connected = self._connect() + if self.connected: + return True + LOG.debug("Connection attempt %d: Unable to connect to %s, " \ + "retrying in %d seconds", + idx, self.host, self.RETRY_SECONDS) + time.sleep(self.RETRY_SECONDS) + + LOG.error("Unable to connect to pktgen instance on %s !", + self.host) + return False + + + def send_command(self, command): + if not self.connected: + LOG.error("Pktgen socket is not connected") + return False + + try: + self._sock.sendall((command + "\n").encode()) + time.sleep(1) + except (socket.timeout, socket.error): + LOG.error("Error sending command '%s'", command) + return False + + return True + + +class VcmtsPktgenSetupEnvHelper(sample_vnf.SetupEnvHelper): + + BASE_PARAMETERS = "export LUA_PATH=/vcmts/Pktgen.lua;"\ + + "export CMK_PROC_FS=/host/proc;" + + PORTS_COUNT = 8 + + def generate_pcap_filename(self, port_cfg): + return port_cfg['traffic_type'] + "_" + port_cfg['num_subs'] \ + + "cms_" + port_cfg['num_ofdm'] + "ofdm.pcap" + + def find_port_cfg(self, ports_cfg, port_name): + for port_cfg in ports_cfg: + if port_name in port_cfg: + return port_cfg + return None + + def build_pktgen_parameters(self, pod_cfg): + ports_cfg = pod_cfg['ports'] + port_cfg = list() + + for i in range(self.PORTS_COUNT): + port_cfg.append(self.find_port_cfg(ports_cfg, 'port_' + str(i))) + + pktgen_parameters = self.BASE_PARAMETERS + " " \ + + " /pktgen-config/setup.sh " + pod_cfg['pktgen_id'] \ + + " " + pod_cfg['num_ports'] + + for i in range(self.PORTS_COUNT): + pktgen_parameters += " " + port_cfg[i]['net_pktgen'] + + for i in range(self.PORTS_COUNT): + pktgen_parameters += " " + self.generate_pcap_filename(port_cfg[i]) + + return pktgen_parameters + + def start_pktgen(self, pod_cfg): + self.ssh_helper.drop_connection() + cmd = self.build_pktgen_parameters(pod_cfg) + LOG.debug("Executing: '%s'", cmd) + self.ssh_helper.send_command(cmd) + LOG.info("Pktgen executed") + + def setup_vnf_environment(self): + pass + + +class VcmtsPktgen(sample_vnf.SampleVNFTrafficGen): + + TG_NAME = 'VcmtsPktgen' + APP_NAME = 'VcmtsPktgen' + RUN_WAIT = 4 + DEFAULT_RATE = 8.0 + + PKTGEN_BASE_PORT = 23000 + + def __init__(self, name, vnfd, setup_env_helper_type=None, + resource_helper_type=None): + if setup_env_helper_type is None: + setup_env_helper_type = VcmtsPktgenSetupEnvHelper + super(VcmtsPktgen, self).__init__( + name, vnfd, setup_env_helper_type, resource_helper_type) + + self.pktgen_address = vnfd['mgmt-interface']['ip'] + LOG.info("Pktgen container '%s', IP: %s", name, self.pktgen_address) + + def extract_pod_cfg(self, pktgen_pods_cfg, pktgen_id): + for pod_cfg in pktgen_pods_cfg: + if pod_cfg['pktgen_id'] == pktgen_id: + return pod_cfg + return None + + def instantiate(self, scenario_cfg, context_cfg): + super(VcmtsPktgen, self).instantiate(scenario_cfg, context_cfg) + self._start_server() + options = scenario_cfg.get('options', {}) + self.pktgen_rate = options.get('pktgen_rate', self.DEFAULT_RATE) + + try: + pktgen_values_filepath = options['pktgen_values'] + except KeyError: + raise KeyError("Missing pktgen_values key in scenario options" \ + "section of the task definition file") + + if not os.path.isfile(pktgen_values_filepath): + raise RuntimeError("The pktgen_values file path provided " \ + "does not exists") + + # The yaml_loader.py (SafeLoader) underlying regex has an issue + # with reading PCI addresses (processed as double). so the + # BaseLoader is used here. + with open(pktgen_values_filepath) as stream: + pktgen_values = yaml.load(stream, Loader=yaml.BaseLoader) + + if pktgen_values == None: + raise RuntimeError("Error reading pktgen_values file provided (" + + pktgen_values_filepath + ")") + + self.pktgen_id = int(options[self.name]['pktgen_id']) + self.resource_helper.pktgen_id = self.pktgen_id + + self.pktgen_helper = PktgenHelper(self.pktgen_address, + self.PKTGEN_BASE_PORT + self.pktgen_id) + + pktgen_pods_cfg = pktgen_values['topology']['pktgen_pods'] + + self.pod_cfg = self.extract_pod_cfg(pktgen_pods_cfg, + str(self.pktgen_id)) + + if self.pod_cfg == None: + raise KeyError("Pktgen with id " + str(self.pktgen_id) + \ + " was not found") + + self.setup_helper.start_pktgen(self.pod_cfg) + + def run_traffic(self, traffic_profile): + if not self.pktgen_helper.connect(): + raise exceptions.PktgenActionError(command="connect") + LOG.info("Connected to pktgen instance at %s", self.pktgen_address) + + commands = [] + for i in range(self.setup_helper.PORTS_COUNT): + commands.append('pktgen.set("' + str(i) + '", "rate", ' + + "%0.1f" % self.pktgen_rate + ');') + + commands.append('pktgen.start("all");') + + for command in commands: + if self.pktgen_helper.send_command(command): + LOG.debug("Command '%s' sent to pktgen", command) + LOG.info("Traffic started on %s...", self.name) + return True diff --git a/yardstick/network_services/vnf_generic/vnf/vcmts_vnf.py b/yardstick/network_services/vnf_generic/vnf/vcmts_vnf.py new file mode 100755 index 000000000..0b48ef4e9 --- /dev/null +++ b/yardstick/network_services/vnf_generic/vnf/vcmts_vnf.py @@ -0,0 +1,273 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. + +import logging +import os +import yaml + +from influxdb import InfluxDBClient + +from yardstick.network_services.vnf_generic.vnf.sample_vnf import SetupEnvHelper +from yardstick.common import constants +from yardstick.common import exceptions +from yardstick.network_services.vnf_generic.vnf.base import GenericVNF +from yardstick.network_services.vnf_generic.vnf.sample_vnf import ScenarioHelper +from yardstick.network_services.vnf_generic.vnf.vnf_ssh_helper import VnfSshHelper +from yardstick.network_services.utils import get_nsb_option + + +LOG = logging.getLogger(__name__) + + +class InfluxDBHelper(object): + + INITIAL_VALUE = 'now() - 1m' + + def __init__(self, vcmts_influxdb_ip, vcmts_influxdb_port): + self._vcmts_influxdb_ip = vcmts_influxdb_ip + self._vcmts_influxdb_port = vcmts_influxdb_port + self._last_upstream_rx = self.INITIAL_VALUE + self._last_values_time = dict() + + def start(self): + self._read_client = InfluxDBClient(host=self._vcmts_influxdb_ip, + port=self._vcmts_influxdb_port, + database='collectd') + self._write_client = InfluxDBClient(host=constants.INFLUXDB_IP, + port=constants.INFLUXDB_PORT, + database='collectd') + + def _get_last_value_time(self, measurement): + if measurement in self._last_values_time: + return self._last_values_time[measurement] + return self.INITIAL_VALUE + + def _set_last_value_time(self, measurement, time): + self._last_values_time[measurement] = "'" + time + "'" + + def _query_measurement(self, measurement): + # There is a delay before influxdb flushes the data + query = "SELECT * FROM " + measurement + " WHERE time > " \ + + self._get_last_value_time(measurement) \ + + " ORDER BY time ASC;" + query_result = self._read_client.query(query) + if len(query_result.keys()) == 0: + return None + return query_result.get_points(measurement) + + def _rw_measurment(self, measurement, columns): + query_result = self._query_measurement(measurement) + if query_result == None: + return + + points_to_write = list() + for entry in query_result: + point = { + "measurement": measurement, + "tags": { + "type": entry['type'], + "host": entry['host'] + }, + "time": entry['time'], + "fields": {} + } + + for column in columns: + if column == 'value': + point["fields"][column] = float(entry[column]) + else: + point["fields"][column] = entry[column] + + points_to_write.append(point) + self._set_last_value_time(measurement, entry['time']) + + # Write the points to yardstick database + if self._write_client.write_points(points_to_write): + LOG.debug("%d new points written to '%s' measurement", + len(points_to_write), measurement) + + def copy_kpi(self): + self._rw_measurment("cpu_value", ["instance", "type_instance", "value"]) + self._rw_measurment("cpufreq_value", ["type_instance", "value"]) + self._rw_measurment("downstream_rx", ["value"]) + self._rw_measurment("downstream_tx", ["value"]) + self._rw_measurment("downstream_value", ["value"]) + self._rw_measurment("ds_per_cm_value", ["instance", "value"]) + self._rw_measurment("intel_rdt_value", ["instance", "type_instance", "value"]) + self._rw_measurment("turbostat_value", ["instance", "type_instance", "value"]) + self._rw_measurment("upstream_rx", ["value"]) + self._rw_measurment("upstream_tx", ["value"]) + self._rw_measurment("upstream_value", ["value"]) + + +class VcmtsdSetupEnvHelper(SetupEnvHelper): + + BASE_PARAMETERS = "export LD_LIBRARY_PATH=/opt/collectd/lib:;"\ + + "export CMK_PROC_FS=/host/proc;" + + def build_us_parameters(self, pod_cfg): + return self.BASE_PARAMETERS + " " \ + + " /opt/bin/cmk isolate --conf-dir=/etc/cmk" \ + + " --socket-id=" + pod_cfg['cpu_socket_id'] \ + + " --pool=shared" \ + + " /vcmts-config/run_upstream.sh " + pod_cfg['sg_id'] \ + + " " + pod_cfg['ds_core_type'] \ + + " " + pod_cfg['num_ofdm'] + "ofdm" \ + + " " + pod_cfg['num_subs'] + "cm" \ + + " " + pod_cfg['cm_crypto'] \ + + " " + pod_cfg['qat'] \ + + " " + pod_cfg['net_us'] \ + + " " + pod_cfg['power_mgmt'] + + def build_ds_parameters(self, pod_cfg): + return self.BASE_PARAMETERS + " " \ + + " /opt/bin/cmk isolate --conf-dir=/etc/cmk" \ + + " --socket-id=" + pod_cfg['cpu_socket_id'] \ + + " --pool=" + pod_cfg['ds_core_type'] \ + + " /vcmts-config/run_downstream.sh " + pod_cfg['sg_id'] \ + + " " + pod_cfg['ds_core_type'] \ + + " " + pod_cfg['ds_core_pool_index'] \ + + " " + pod_cfg['num_ofdm'] + "ofdm" \ + + " " + pod_cfg['num_subs'] + "cm" \ + + " " + pod_cfg['cm_crypto'] \ + + " " + pod_cfg['qat'] \ + + " " + pod_cfg['net_ds'] \ + + " " + pod_cfg['power_mgmt'] + + def build_cmd(self, stream_dir, pod_cfg): + if stream_dir == 'ds': + return self.build_ds_parameters(pod_cfg) + else: + return self.build_us_parameters(pod_cfg) + + def run_vcmtsd(self, stream_dir, pod_cfg): + cmd = self.build_cmd(stream_dir, pod_cfg) + LOG.debug("Executing %s", cmd) + self.ssh_helper.send_command(cmd) + + def setup_vnf_environment(self): + pass + + +class VcmtsVNF(GenericVNF): + + RUN_WAIT = 4 + + def __init__(self, name, vnfd): + super(VcmtsVNF, self).__init__(name, vnfd) + self.name = name + self.bin_path = get_nsb_option('bin_path', '') + self.scenario_helper = ScenarioHelper(self.name) + self.ssh_helper = VnfSshHelper(self.vnfd_helper.mgmt_interface, self.bin_path) + + self.setup_helper = VcmtsdSetupEnvHelper(self.vnfd_helper, + self.ssh_helper, + self.scenario_helper) + + def extract_pod_cfg(self, vcmts_pods_cfg, sg_id): + for pod_cfg in vcmts_pods_cfg: + if pod_cfg['sg_id'] == sg_id: + return pod_cfg + + def instantiate(self, scenario_cfg, context_cfg): + self._update_collectd_options(scenario_cfg, context_cfg) + self.scenario_helper.scenario_cfg = scenario_cfg + self.context_cfg = context_cfg + + options = scenario_cfg.get('options', {}) + + try: + self.vcmts_influxdb_ip = options['vcmts_influxdb_ip'] + self.vcmts_influxdb_port = options['vcmts_influxdb_port'] + except KeyError: + raise KeyError("Missing destination InfluxDB details in scenario" \ + " section of the task definition file") + + try: + vcmtsd_values_filepath = options['vcmtsd_values'] + except KeyError: + raise KeyError("Missing vcmtsd_values key in scenario options" \ + "section of the task definition file") + + if not os.path.isfile(vcmtsd_values_filepath): + raise RuntimeError("The vcmtsd_values file path provided " \ + "does not exists") + + # The yaml_loader.py (SafeLoader) underlying regex has an issue + # with reading PCI addresses (processed as double). so the + # BaseLoader is used here. + with open(vcmtsd_values_filepath) as stream: + vcmtsd_values = yaml.load(stream, Loader=yaml.BaseLoader) + + if vcmtsd_values == None: + raise RuntimeError("Error reading vcmtsd_values file provided (" + + vcmtsd_values_filepath + ")") + + vnf_options = options.get(self.name, {}) + sg_id = str(vnf_options['sg_id']) + stream_dir = vnf_options['stream_dir'] + + try: + vcmts_pods_cfg = vcmtsd_values['topology']['vcmts_pods'] + except KeyError: + raise KeyError("Missing vcmts_pods key in the " \ + "vcmtsd_values file provided") + + pod_cfg = self.extract_pod_cfg(vcmts_pods_cfg, sg_id) + if pod_cfg == None: + raise exceptions.IncorrectConfig(error_msg="Service group " + sg_id + " not found") + + self.setup_helper.run_vcmtsd(stream_dir, pod_cfg) + + def _update_collectd_options(self, scenario_cfg, context_cfg): + scenario_options = scenario_cfg.get('options', {}) + generic_options = scenario_options.get('collectd', {}) + scenario_node_options = scenario_options.get(self.name, {})\ + .get('collectd', {}) + context_node_options = context_cfg.get('nodes', {})\ + .get(self.name, {}).get('collectd', {}) + + options = generic_options + self._update_options(options, scenario_node_options) + self._update_options(options, context_node_options) + + self.setup_helper.collectd_options = options + + def _update_options(self, options, additional_options): + for k, v in additional_options.items(): + if isinstance(v, dict) and k in options: + options[k].update(v) + else: + options[k] = v + + def wait_for_instantiate(self): + pass + + def terminate(self): + pass + + def scale(self, flavor=""): + pass + + def collect_kpi(self): + self.influxdb_helper.copy_kpi() + return {"n/a": "n/a"} + + def start_collect(self): + self.influxdb_helper = InfluxDBHelper(self.vcmts_influxdb_ip, + self.vcmts_influxdb_port) + self.influxdb_helper.start() + + def stop_collect(self): + pass diff --git a/yardstick/network_services/vnf_generic/vnf/vpp_helpers.py b/yardstick/network_services/vnf_generic/vnf/vpp_helpers.py index 86c42ecfd..fe8e7b2ba 100644 --- a/yardstick/network_services/vnf_generic/vnf/vpp_helpers.py +++ b/yardstick/network_services/vnf_generic/vnf/vpp_helpers.py @@ -12,25 +12,197 @@ # See the License for the specific language governing permissions and # limitations under the License. +import binascii +import ipaddress +import json import logging +import os +import re +import tempfile +import time +from collections import OrderedDict +from yardstick.common import constants +from yardstick.common import exceptions +from yardstick.network_services.helpers.cpu import CpuSysCores from yardstick.network_services.vnf_generic.vnf.sample_vnf import \ DpdkVnfSetupEnvHelper LOG = logging.getLogger(__name__) +class VppConfigGenerator(object): + VPP_LOG_FILE = '/tmp/vpe.log' + + def __init__(self): + self._nodeconfig = {} + self._vpp_config = '' + + def add_config_item(self, config, value, path): + if len(path) == 1: + config[path[0]] = value + return + if path[0] not in config: + config[path[0]] = {} + elif isinstance(config[path[0]], str): + config[path[0]] = {} if config[path[0]] == '' \ + else {config[path[0]]: ''} + self.add_config_item(config[path[0]], value, path[1:]) + + def add_unix_log(self, value=None): + path = ['unix', 'log'] + if value is None: + value = self.VPP_LOG_FILE + self.add_config_item(self._nodeconfig, value, path) + + def add_unix_cli_listen(self, value='/run/vpp/cli.sock'): + path = ['unix', 'cli-listen'] + self.add_config_item(self._nodeconfig, value, path) + + def add_unix_nodaemon(self): + path = ['unix', 'nodaemon'] + self.add_config_item(self._nodeconfig, '', path) + + def add_unix_coredump(self): + path = ['unix', 'full-coredump'] + self.add_config_item(self._nodeconfig, '', path) + + def add_dpdk_dev(self, *devices): + for device in devices: + if VppConfigGenerator.pci_dev_check(device): + path = ['dpdk', 'dev {0}'.format(device)] + self.add_config_item(self._nodeconfig, '', path) + + def add_dpdk_cryptodev(self, count, cryptodev): + for i in range(count): + cryptodev_config = 'dev {0}'.format( + re.sub(r'\d.\d$', '1.' + str(i), cryptodev)) + path = ['dpdk', cryptodev_config] + self.add_config_item(self._nodeconfig, '', path) + self.add_dpdk_uio_driver('igb_uio') + + def add_dpdk_sw_cryptodev(self, sw_pmd_type, socket_id, count): + for _ in range(count): + cryptodev_config = 'vdev cryptodev_{0}_pmd,socket_id={1}'. \ + format(sw_pmd_type, str(socket_id)) + path = ['dpdk', cryptodev_config] + self.add_config_item(self._nodeconfig, '', path) + + def add_dpdk_dev_default_rxq(self, value): + path = ['dpdk', 'dev default', 'num-rx-queues'] + self.add_config_item(self._nodeconfig, value, path) + + def add_dpdk_dev_default_rxd(self, value): + path = ['dpdk', 'dev default', 'num-rx-desc'] + self.add_config_item(self._nodeconfig, value, path) + + def add_dpdk_dev_default_txd(self, value): + path = ['dpdk', 'dev default', 'num-tx-desc'] + self.add_config_item(self._nodeconfig, value, path) + + def add_dpdk_log_level(self, value): + path = ['dpdk', 'log-level'] + self.add_config_item(self._nodeconfig, value, path) + + def add_dpdk_socketmem(self, value): + path = ['dpdk', 'socket-mem'] + self.add_config_item(self._nodeconfig, value, path) + + def add_dpdk_num_mbufs(self, value): + path = ['dpdk', 'num-mbufs'] + self.add_config_item(self._nodeconfig, value, path) + + def add_dpdk_uio_driver(self, value=None): + path = ['dpdk', 'uio-driver'] + self.add_config_item(self._nodeconfig, value, path) + + def add_cpu_main_core(self, value): + path = ['cpu', 'main-core'] + self.add_config_item(self._nodeconfig, value, path) + + def add_cpu_corelist_workers(self, value): + path = ['cpu', 'corelist-workers'] + self.add_config_item(self._nodeconfig, value, path) + + def add_heapsize(self, value): + path = ['heapsize'] + self.add_config_item(self._nodeconfig, value, path) + + def add_ip6_hash_buckets(self, value): + path = ['ip6', 'hash-buckets'] + self.add_config_item(self._nodeconfig, value, path) + + def add_ip6_heap_size(self, value): + path = ['ip6', 'heap-size'] + self.add_config_item(self._nodeconfig, value, path) + + def add_ip_heap_size(self, value): + path = ['ip', 'heap-size'] + self.add_config_item(self._nodeconfig, value, path) + + def add_statseg_size(self, value): + path = ['statseg', 'size'] + self.add_config_item(self._nodeconfig, value, path) + + def add_plugin(self, state, *plugins): + for plugin in plugins: + path = ['plugins', 'plugin {0}'.format(plugin), state] + self.add_config_item(self._nodeconfig, ' ', path) + + def add_dpdk_no_multi_seg(self): + path = ['dpdk', 'no-multi-seg'] + self.add_config_item(self._nodeconfig, '', path) + + def add_dpdk_no_tx_checksum_offload(self): + path = ['dpdk', 'no-tx-checksum-offload'] + self.add_config_item(self._nodeconfig, '', path) + + def dump_config(self, obj=None, level=-1): + if obj is None: + obj = self._nodeconfig + obj = OrderedDict(sorted(obj.items())) + + indent = ' ' + if level >= 0: + self._vpp_config += '{}{{\n'.format(level * indent) + if isinstance(obj, dict): + for key, val in obj.items(): + if hasattr(val, '__iter__') and not isinstance(val, str): + self._vpp_config += '{}{}\n'.format((level + 1) * indent, + key) + self.dump_config(val, level + 1) + else: + self._vpp_config += '{}{} {}\n'.format( + (level + 1) * indent, + key, val) + if level >= 0: + self._vpp_config += '{}}}\n'.format(level * indent) + + return self._vpp_config + + @staticmethod + def pci_dev_check(pci_dev): + pattern = re.compile("^[0-9A-Fa-f]{4}:[0-9A-Fa-f]{2}:" + "[0-9A-Fa-f]{2}\\.[0-9A-Fa-f]$") + if not pattern.match(pci_dev): + raise ValueError('PCI address {addr} is not in valid format ' + 'xxxx:xx:xx.x'.format(addr=pci_dev)) + return True + + class VppSetupEnvHelper(DpdkVnfSetupEnvHelper): APP_NAME = "vpp" CFG_CONFIG = "/etc/vpp/startup.conf" CFG_SCRIPT = "" PIPELINE_COMMAND = "" + QAT_DRIVER = "qat_dh895xcc" VNF_TYPE = "IPSEC" VAT_BIN_NAME = 'vpp_api_test' def __init__(self, vnfd_helper, ssh_helper, scenario_helper): super(VppSetupEnvHelper, self).__init__(vnfd_helper, ssh_helper, scenario_helper) + self.sys_cores = CpuSysCores(self.ssh_helper) def kill_vnf(self): ret_code, _, _ = \ @@ -74,3 +246,506 @@ class VppSetupEnvHelper(DpdkVnfSetupEnvHelper): ifname=interface).get(key) except (KeyError, ValueError): return None + + def crypto_device_init(self, pci_addr, numvfs): + # QAT device must be re-bound to kernel driver before initialization. + self.dpdk_bind_helper.load_dpdk_driver(self.QAT_DRIVER) + + # Stop VPP to prevent deadlock. + self.kill_vnf() + + current_driver = self.get_pci_dev_driver(pci_addr.replace(':', r'\:')) + if current_driver is not None: + self.pci_driver_unbind(pci_addr) + + # Bind to kernel driver. + self.dpdk_bind_helper.bind(pci_addr, self.QAT_DRIVER.replace('qat_', '')) + + # Initialize QAT VFs. + if numvfs > 0: + self.set_sriov_numvfs(pci_addr, numvfs) + + def get_sriov_numvfs(self, pf_pci_addr): + command = 'cat /sys/bus/pci/devices/{pci}/sriov_numvfs'. \ + format(pci=pf_pci_addr.replace(':', r'\:')) + _, stdout, _ = self.ssh_helper.execute(command) + try: + return int(stdout) + except ValueError: + LOG.debug('Reading sriov_numvfs info failed') + return 0 + + def set_sriov_numvfs(self, pf_pci_addr, numvfs=0): + command = "sh -c 'echo {num} | tee /sys/bus/pci/devices/{pci}/sriov_numvfs'". \ + format(num=numvfs, pci=pf_pci_addr.replace(':', r'\:')) + self.ssh_helper.execute(command) + + def pci_driver_unbind(self, pci_addr): + command = "sh -c 'echo {pci} | tee /sys/bus/pci/devices/{pcie}/driver/unbind'". \ + format(pci=pci_addr, pcie=pci_addr.replace(':', r'\:')) + self.ssh_helper.execute(command) + + def get_pci_dev_driver(self, pci_addr): + cmd = 'lspci -vmmks {0}'.format(pci_addr) + ret_code, stdout, _ = self.ssh_helper.execute(cmd) + if int(ret_code): + raise RuntimeError("'{0}' failed".format(cmd)) + for line in stdout.splitlines(): + if not line: + continue + name = None + value = None + try: + name, value = line.split("\t", 1) + except ValueError: + if name == "Driver:": + return None + if name == 'Driver:': + return value + return None + + def vpp_create_ipsec_tunnels(self, if1_ip_addr, if2_ip_addr, if_name, + n_tunnels, n_connections, crypto_alg, + crypto_key, integ_alg, integ_key, addrs_ip, + spi_1=10000, spi_2=20000): + mask_length = 32 + if n_connections <= n_tunnels: + count = 1 + else: + count = int(n_connections / n_tunnels) + addr_ip_i = int(ipaddress.ip_address(str(addrs_ip))) + dst_start_ip = addr_ip_i + + tmp_fd, tmp_path = tempfile.mkstemp() + + vpp_ifname = self.get_value_by_interface_key(if_name, 'vpp_name') + ckey = binascii.hexlify(crypto_key.encode()) + ikey = binascii.hexlify(integ_key.encode()) + + integ = '' + if crypto_alg.alg_name != 'aes-gcm-128': + integ = 'integ_alg {integ_alg} ' \ + 'local_integ_key {local_integ_key} ' \ + 'remote_integ_key {remote_integ_key} ' \ + .format(integ_alg=integ_alg.alg_name, + local_integ_key=ikey, + remote_integ_key=ikey) + create_tunnels_cmds = 'ipsec_tunnel_if_add_del ' \ + 'local_spi {local_spi} ' \ + 'remote_spi {remote_spi} ' \ + 'crypto_alg {crypto_alg} ' \ + 'local_crypto_key {local_crypto_key} ' \ + 'remote_crypto_key {remote_crypto_key} ' \ + '{integ} ' \ + 'local_ip {local_ip} ' \ + 'remote_ip {remote_ip}\n' + start_tunnels_cmds = 'ip_add_del_route {raddr}/{mask} via {addr} ipsec{i}\n' \ + 'exec set interface unnumbered ipsec{i} use {uifc}\n' \ + 'sw_interface_set_flags ipsec{i} admin-up\n' + + with os.fdopen(tmp_fd, 'w') as tmp_file: + for i in range(0, n_tunnels): + create_tunnel = create_tunnels_cmds.format(local_spi=spi_1 + i, + remote_spi=spi_2 + i, + crypto_alg=crypto_alg.alg_name, + local_crypto_key=ckey, + remote_crypto_key=ckey, + integ=integ, + local_ip=if1_ip_addr, + remote_ip=if2_ip_addr) + tmp_file.write(create_tunnel) + self.execute_script(tmp_path, json_out=False, copy_on_execute=True) + os.remove(tmp_path) + + tmp_fd, tmp_path = tempfile.mkstemp() + + with os.fdopen(tmp_fd, 'w') as tmp_file: + for i in range(0, n_tunnels): + if count > 1: + dst_start_ip = addr_ip_i + i * count + dst_end_ip = ipaddress.ip_address(dst_start_ip + count - 1) + ips = [ipaddress.ip_address(ip) for ip in + [str(ipaddress.ip_address(dst_start_ip)), + str(dst_end_ip)]] + lowest_ip, highest_ip = min(ips), max(ips) + mask_length = self.get_prefix_length(int(lowest_ip), + int(highest_ip), + lowest_ip.max_prefixlen) + # TODO check duplicate route for some IPs + elif count == 1: + dst_start_ip = addr_ip_i + i + start_tunnel = start_tunnels_cmds.format( + raddr=str(ipaddress.ip_address(dst_start_ip)), + mask=mask_length, + addr=if2_ip_addr, + i=i, count=count, + uifc=vpp_ifname) + tmp_file.write(start_tunnel) + # TODO add route for remain IPs + + self.execute_script(tmp_path, json_out=False, copy_on_execute=True) + os.remove(tmp_path) + + def apply_config(self, vpp_cfg, restart_vpp=True): + vpp_config = vpp_cfg.dump_config() + ret, _, _ = \ + self.ssh_helper.execute('echo "{config}" | sudo tee {filename}'. + format(config=vpp_config, + filename=self.CFG_CONFIG)) + if ret != 0: + raise RuntimeError('Writing config file failed') + if restart_vpp: + self.start_vpp_service() + + def vpp_route_add(self, network, prefix_len, gateway=None, interface=None, + use_sw_index=True, resolve_attempts=10, + count=1, vrf=None, lookup_vrf=None, multipath=False, + weight=None, local=False): + if interface: + if use_sw_index: + int_cmd = ('sw_if_index {}'.format( + self.get_value_by_interface_key(interface, + 'vpp_sw_index'))) + else: + int_cmd = interface + else: + int_cmd = '' + + rap = 'resolve-attempts {}'.format(resolve_attempts) \ + if resolve_attempts else '' + + via = 'via {}'.format(gateway) if gateway else '' + + cnt = 'count {}'.format(count) \ + if count else '' + + vrf = 'vrf {}'.format(vrf) if vrf else '' + + lookup_vrf = 'lookup-in-vrf {}'.format( + lookup_vrf) if lookup_vrf else '' + + multipath = 'multipath' if multipath else '' + + weight = 'weight {}'.format(weight) if weight else '' + + local = 'local' if local else '' + + with VatTerminal(self.ssh_helper, json_param=False) as vat: + vat.vat_terminal_exec_cmd_from_template('add_route.vat', + network=network, + prefix_length=prefix_len, + via=via, + vrf=vrf, + interface=int_cmd, + resolve_attempts=rap, + count=cnt, + lookup_vrf=lookup_vrf, + multipath=multipath, + weight=weight, + local=local) + + def add_arp_on_dut(self, iface_key, ip_address, mac_address): + with VatTerminal(self.ssh_helper) as vat: + return vat.vat_terminal_exec_cmd_from_template( + 'add_ip_neighbor.vat', + sw_if_index=self.get_value_by_interface_key(iface_key, + 'vpp_sw_index'), + ip_address=ip_address, mac_address=mac_address) + + def set_ip(self, interface, address, prefix_length): + with VatTerminal(self.ssh_helper) as vat: + return vat.vat_terminal_exec_cmd_from_template( + 'add_ip_address.vat', + sw_if_index=self.get_value_by_interface_key(interface, + 'vpp_sw_index'), + address=address, prefix_length=prefix_length) + + def set_interface_state(self, interface, state): + sw_if_index = self.get_value_by_interface_key(interface, + 'vpp_sw_index') + + if state == 'up': + state = 'admin-up link-up' + elif state == 'down': + state = 'admin-down link-down' + else: + raise ValueError('Unexpected interface state: {}'.format(state)) + with VatTerminal(self.ssh_helper) as vat: + return vat.vat_terminal_exec_cmd_from_template( + 'set_if_state.vat', sw_if_index=sw_if_index, state=state) + + def vpp_set_interface_mtu(self, interface, mtu=9200): + sw_if_index = self.get_value_by_interface_key(interface, + 'vpp_sw_index') + if sw_if_index: + with VatTerminal(self.ssh_helper, json_param=False) as vat: + vat.vat_terminal_exec_cmd_from_template( + "hw_interface_set_mtu.vat", sw_if_index=sw_if_index, + mtu=mtu) + + def vpp_interfaces_ready_wait(self, timeout=30): + if_ready = False + not_ready = [] + start = time.time() + while not if_ready: + out = self.vpp_get_interface_data() + if time.time() - start > timeout: + for interface in out: + if interface.get('admin_up_down') == 1: + if interface.get('link_up_down') != 1: + LOG.debug('%s link-down', + interface.get('interface_name')) + raise RuntimeError('timeout, not up {0}'.format(not_ready)) + not_ready = [] + for interface in out: + if interface.get('admin_up_down') == 1: + if interface.get('link_up_down') != 1: + not_ready.append(interface.get('interface_name')) + if not not_ready: + if_ready = True + else: + LOG.debug('Interfaces still in link-down state: %s, ' + 'waiting...', not_ready) + time.sleep(1) + + def vpp_get_interface_data(self, interface=None): + with VatTerminal(self.ssh_helper) as vat: + response = vat.vat_terminal_exec_cmd_from_template( + "interface_dump.vat") + data = response[0] + if interface is not None: + if isinstance(interface, str): + param = "interface_name" + elif isinstance(interface, int): + param = "sw_if_index" + else: + raise TypeError + for data_if in data: + if data_if[param] == interface: + return data_if + return dict() + return data + + def update_vpp_interface_data(self): + data = {} + interface_dump_json = self.execute_script_json_out( + "dump_interfaces.vat") + interface_list = json.loads(interface_dump_json) + for interface in self.vnfd_helper.interfaces: + if_mac = interface['virtual-interface']['local_mac'] + interface_dict = VppSetupEnvHelper.get_vpp_interface_by_mac( + interface_list, if_mac) + if not interface_dict: + LOG.debug('Interface %s not found by MAC %s', interface, + if_mac) + continue + data[interface['virtual-interface']['ifname']] = { + 'vpp_name': interface_dict["interface_name"], + 'vpp_sw_index': interface_dict["sw_if_index"] + } + for iface_key, updated_vnfd in data.items(): + self._update_vnfd_helper(updated_vnfd, iface_key) + + def iface_update_numa(self): + iface_numa = {} + for interface in self.vnfd_helper.interfaces: + cmd = "cat /sys/bus/pci/devices/{}/numa_node".format( + interface["virtual-interface"]["vpci"]) + ret, out, _ = self.ssh_helper.execute(cmd) + if ret == 0: + try: + numa_node = int(out) + if numa_node < 0: + if self.vnfd_helper["cpuinfo"][-1][3] + 1 == 1: + iface_numa[ + interface['virtual-interface']['ifname']] = { + 'numa_node': 0 + } + else: + raise ValueError + else: + iface_numa[ + interface['virtual-interface']['ifname']] = { + 'numa_node': numa_node + } + except ValueError: + LOG.debug( + 'Reading numa location failed for: %s', + interface["virtual-interface"]["vpci"]) + for iface_key, updated_vnfd in iface_numa.items(): + self._update_vnfd_helper(updated_vnfd, iface_key) + + def execute_script(self, vat_name, json_out=True, copy_on_execute=False): + if copy_on_execute: + self.ssh_helper.put_file(vat_name, vat_name) + remote_file_path = vat_name + else: + vat_path = self.ssh_helper.join_bin_path("vpp", "templates") + remote_file_path = '{0}/{1}'.format(vat_path, vat_name) + + cmd = "{vat_bin} {json} in {vat_path} script".format( + vat_bin=self.VAT_BIN_NAME, + json="json" if json_out is True else "", + vat_path=remote_file_path) + + try: + return self.ssh_helper.execute(cmd=cmd) + except Exception: + raise RuntimeError("VAT script execution failed: {0}".format(cmd)) + + def execute_script_json_out(self, vat_name): + vat_path = self.ssh_helper.join_bin_path("vpp", "templates") + remote_file_path = '{0}/{1}'.format(vat_path, vat_name) + + _, stdout, _ = self.execute_script(vat_name, json_out=True) + return self.cleanup_vat_json_output(stdout, vat_file=remote_file_path) + + @staticmethod + def cleanup_vat_json_output(json_output, vat_file=None): + retval = json_output + clutter = ['vat#', 'dump_interface_table error: Misc', + 'dump_interface_table:6019: JSON output supported only ' \ + 'for VPE API calls and dump_stats_table'] + if vat_file: + clutter.append("{0}(2):".format(vat_file)) + for garbage in clutter: + retval = retval.replace(garbage, '') + return retval.strip() + + @staticmethod + def _convert_mac_to_number_list(mac_address): + list_mac = [] + for num in mac_address.split(":"): + list_mac.append(int(num, 16)) + return list_mac + + @staticmethod + def get_vpp_interface_by_mac(interfaces_list, mac_address): + interface_dict = {} + list_mac_address = VppSetupEnvHelper._convert_mac_to_number_list( + mac_address) + LOG.debug("MAC address %s converted to list %s.", mac_address, + list_mac_address) + for interface in interfaces_list: + # TODO: create vat json integrity checking and move there + if "l2_address" not in interface: + raise KeyError( + "key l2_address not found in interface dict." + "Probably input list is not parsed from correct VAT " + "json output.") + if "l2_address_length" not in interface: + raise KeyError( + "key l2_address_length not found in interface " + "dict. Probably input list is not parsed from correct " + "VAT json output.") + mac_from_json = interface["l2_address"][:6] + if mac_from_json == list_mac_address: + if interface["l2_address_length"] != 6: + raise ValueError("l2_address_length value is not 6.") + interface_dict = interface + break + return interface_dict + + @staticmethod + def get_prefix_length(number1, number2, bits): + for i in range(bits): + if number1 >> i == number2 >> i: + return bits - i + return 0 + + +class VatTerminal(object): + + __VAT_PROMPT = ("vat# ",) + __LINUX_PROMPT = (":~# ", ":~$ ", "~]$ ", "~]# ") + + + def __init__(self, ssh_helper, json_param=True): + json_text = ' json' if json_param else '' + self.json = json_param + self.ssh_helper = ssh_helper + EXEC_RETRY = 3 + + try: + self._tty = self.ssh_helper.interactive_terminal_open() + except Exception: + raise RuntimeError("Cannot open interactive terminal") + + for _ in range(EXEC_RETRY): + try: + self.ssh_helper.interactive_terminal_exec_command( + self._tty, + 'sudo -S {0}{1}'.format(VppSetupEnvHelper.VAT_BIN_NAME, + json_text), + self.__VAT_PROMPT) + except exceptions.SSHTimeout: + continue + else: + break + + self._exec_failure = False + self.vat_stdout = None + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.vat_terminal_close() + + def vat_terminal_exec_cmd(self, cmd): + try: + out = self.ssh_helper.interactive_terminal_exec_command(self._tty, + cmd, + self.__VAT_PROMPT) + self.vat_stdout = out + except exceptions.SSHTimeout: + self._exec_failure = True + raise RuntimeError( + "VPP is not running on node. VAT command {0} execution failed". + format(cmd)) + if self.json: + obj_start = out.find('{') + obj_end = out.rfind('}') + array_start = out.find('[') + array_end = out.rfind(']') + + if obj_start == -1 and array_start == -1: + raise RuntimeError( + "VAT command {0}: no JSON data.".format(cmd)) + + if obj_start < array_start or array_start == -1: + start = obj_start + end = obj_end + 1 + else: + start = array_start + end = array_end + 1 + out = out[start:end] + json_out = json.loads(out) + return json_out + else: + return None + + def vat_terminal_close(self): + if not self._exec_failure: + try: + self.ssh_helper.interactive_terminal_exec_command(self._tty, + 'quit', + self.__LINUX_PROMPT) + except exceptions.SSHTimeout: + raise RuntimeError("Failed to close VAT console") + try: + self.ssh_helper.interactive_terminal_close(self._tty) + except Exception: + raise RuntimeError("Cannot close interactive terminal") + + def vat_terminal_exec_cmd_from_template(self, vat_template_file, **args): + file_path = os.path.join(constants.YARDSTICK_ROOT_PATH, + 'yardstick/resources/templates/', + vat_template_file) + with open(file_path, 'r') as template_file: + cmd_template = template_file.readlines() + ret = [] + for line_tmpl in cmd_template: + vat_cmd = line_tmpl.format(**args) + ret.append(self.vat_terminal_exec_cmd(vat_cmd.replace('\n', ''))) + return ret diff --git a/yardstick/resources/templates/add_ip_address.vat b/yardstick/resources/templates/add_ip_address.vat new file mode 100644 index 000000000..d59480c33 --- /dev/null +++ b/yardstick/resources/templates/add_ip_address.vat @@ -0,0 +1 @@ +sw_interface_add_del_address sw_if_index {sw_if_index} {address}/{prefix_length} diff --git a/yardstick/resources/templates/add_ip_neighbor.vat b/yardstick/resources/templates/add_ip_neighbor.vat new file mode 100644 index 000000000..730e7112a --- /dev/null +++ b/yardstick/resources/templates/add_ip_neighbor.vat @@ -0,0 +1 @@ +ip_neighbor_add_del sw_if_index {sw_if_index} dst {ip_address} mac {mac_address} diff --git a/yardstick/resources/templates/add_route.vat b/yardstick/resources/templates/add_route.vat new file mode 100644 index 000000000..64c6a6c3b --- /dev/null +++ b/yardstick/resources/templates/add_route.vat @@ -0,0 +1 @@ +ip_add_del_route {network}/{prefix_length} {via} {vrf} {interface} {resolve_attempts} {count} {lookup_vrf} {multipath} {weight} {local}
\ No newline at end of file diff --git a/yardstick/resources/templates/del_route.vat b/yardstick/resources/templates/del_route.vat new file mode 100644 index 000000000..e7fe4bc1e --- /dev/null +++ b/yardstick/resources/templates/del_route.vat @@ -0,0 +1 @@ +ip_add_del_route {network}/{prefix_length} via {gateway} sw_if_index {sw_if_index} del
\ No newline at end of file diff --git a/yardstick/resources/templates/flush_ip_addresses.vat b/yardstick/resources/templates/flush_ip_addresses.vat new file mode 100644 index 000000000..f38fcf12c --- /dev/null +++ b/yardstick/resources/templates/flush_ip_addresses.vat @@ -0,0 +1 @@ +sw_interface_add_del_address sw_if_index {sw_if_index} del-all
\ No newline at end of file diff --git a/yardstick/resources/templates/hw_interface_set_mtu.vat b/yardstick/resources/templates/hw_interface_set_mtu.vat new file mode 100644 index 000000000..645d1a80c --- /dev/null +++ b/yardstick/resources/templates/hw_interface_set_mtu.vat @@ -0,0 +1 @@ +hw_interface_set_mtu sw_if_index {sw_if_index} mtu {mtu} diff --git a/yardstick/resources/templates/interface_dump.vat b/yardstick/resources/templates/interface_dump.vat new file mode 100644 index 000000000..850c348f6 --- /dev/null +++ b/yardstick/resources/templates/interface_dump.vat @@ -0,0 +1 @@ +sw_interface_dump diff --git a/yardstick/resources/templates/set_if_state.vat b/yardstick/resources/templates/set_if_state.vat new file mode 100644 index 000000000..e2c2d4b29 --- /dev/null +++ b/yardstick/resources/templates/set_if_state.vat @@ -0,0 +1 @@ +sw_interface_set_flags sw_if_index {sw_if_index} {state} diff --git a/yardstick/tests/unit/network_services/helpers/test_cpu.py b/yardstick/tests/unit/network_services/helpers/test_cpu.py index c28178d4b..a1c0826fb 100644 --- a/yardstick/tests/unit/network_services/helpers/test_cpu.py +++ b/yardstick/tests/unit/network_services/helpers/test_cpu.py @@ -141,3 +141,75 @@ class TestCpuSysCores(unittest.TestCase): def test__str2int_error(self): self.assertEqual(0, CpuSysCores._str2int("err")) + + def test_smt_enabled(self): + self.assertEqual(False, CpuSysCores.smt_enabled( + {'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 1, 1, 1, 0]]})) + + def test_is_smt_enabled(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + cpu_topo = CpuSysCores(ssh_mock) + cpu_topo.cpuinfo = {'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 1, 1, 1, 0]]} + self.assertEqual(False, cpu_topo.is_smt_enabled()) + + def test_cpu_list_per_node(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + cpu_topo = CpuSysCores(ssh_mock) + cpu_topo.cpuinfo = {'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 1, 1, 1, 0]]} + self.assertEqual([0, 1], cpu_topo.cpu_list_per_node(0, False)) + + def test_cpu_list_per_node_error(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + cpu_topo = CpuSysCores(ssh_mock) + cpu_topo.cpuinfo = {'err': [[0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 1, 1, 1, 0]]} + with self.assertRaises(RuntimeError) as raised: + cpu_topo.cpu_list_per_node(0, False) + self.assertIn('Node cpuinfo not available.', str(raised.exception)) + + def test_cpu_list_per_node_smt_error(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + cpu_topo = CpuSysCores(ssh_mock) + cpu_topo.cpuinfo = {'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 1, 1, 1, 0]]} + with self.assertRaises(RuntimeError) as raised: + cpu_topo.cpu_list_per_node(0, True) + self.assertIn('SMT is not enabled.', str(raised.exception)) + + def test_cpu_slice_of_list_per_node(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + cpu_topo = CpuSysCores(ssh_mock) + cpu_topo.cpuinfo = {'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 1, 1, 1, 0]]} + self.assertEqual([1], + cpu_topo.cpu_slice_of_list_per_node(0, 1, 0, + False)) + + def test_cpu_slice_of_list_per_node_error(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + cpu_topo = CpuSysCores(ssh_mock) + cpu_topo.cpuinfo = {'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 1, 1, 1, 0]]} + with self.assertRaises(RuntimeError) as raised: + cpu_topo.cpu_slice_of_list_per_node(1, 1, 1, False) + self.assertIn('cpu_cnt + skip_cnt > length(cpu list).', + str(raised.exception)) + + def test_cpu_list_per_node_str(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + cpu_topo = CpuSysCores(ssh_mock) + cpu_topo.cpuinfo = {'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 1, 1, 1, 0]]} + self.assertEqual("1", + cpu_topo.cpu_list_per_node_str(0, 1, 1, ',', + False)) diff --git a/yardstick/tests/unit/network_services/helpers/vpp_helpers/__init__.py b/yardstick/tests/unit/network_services/helpers/vpp_helpers/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/network_services/helpers/vpp_helpers/__init__.py diff --git a/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_multiple_loss_ratio_search.py b/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_multiple_loss_ratio_search.py new file mode 100644 index 000000000..d3145546a --- /dev/null +++ b/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_multiple_loss_ratio_search.py @@ -0,0 +1,2164 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. + +import unittest + +import mock + +from yardstick.network_services.helpers.vpp_helpers.multiple_loss_ratio_search import \ + MultipleLossRatioSearch +from yardstick.network_services.helpers.vpp_helpers.ndr_pdr_result import \ + NdrPdrResult +from yardstick.network_services.helpers.vpp_helpers.receive_rate_interval import \ + ReceiveRateInterval +from yardstick.network_services.helpers.vpp_helpers.receive_rate_measurement import \ + ReceiveRateMeasurement +from yardstick.network_services.traffic_profile.rfc2544 import PortPgIDMap + + +class TestMultipleLossRatioSearch(unittest.TestCase): + + def test___init__(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + self.assertEqual(True, algorithm.latency) + self.assertEqual(64, algorithm.pkt_size) + self.assertEqual(30, algorithm.final_trial_duration) + self.assertEqual(0.005, algorithm.final_relative_width) + self.assertEqual(2, algorithm.number_of_intermediate_phases) + self.assertEqual(1, algorithm.initial_trial_duration) + self.assertEqual(720, algorithm.timeout) + self.assertEqual(1, algorithm.doublings) + + def test_double_relative_width(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + self.assertEqual(0.00997, algorithm.double_relative_width(0.005)) + + def test_double_step_down(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + self.assertEqual(99003.0, algorithm.double_step_down(0.005, 100000)) + + def test_expand_down(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + self.assertEqual(99003.0, algorithm.expand_down(0.005, 1, 100000)) + + def test_double_step_up(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + self.assertEqual(101007.0401907013, + algorithm.double_step_up(0.005, 100000)) + + def test_expand_up(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + self.assertEqual(101007.0401907013, + algorithm.expand_up(0.005, 1, 100000)) + + def test_half_relative_width(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + self.assertEqual(0.0025031328369998773, + algorithm.half_relative_width(0.005)) + + def test_half_step_up(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + self.assertEqual(100250.94142341711, + algorithm.half_step_up(0.005, 100000)) + + def test_init_generator(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), + mock.Mock(), mock.Mock())) + self.assertEqual(ports, algorithm.ports) + self.assertEqual(port_pg_id, algorithm.port_pg_id) + + def test_collect_kpi(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + algorithm.init_generator(ports, port_pg_id, mock.Mock, mock.Mock, + mock.Mock()) + self.assertIsNone(algorithm.collect_kpi({}, 100000)) + + def test_narrow_down_ndr_and_pdr(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, 'ndrpdr') as \ + mock_ndrpdr: + ndr_measured_low = ReceiveRateMeasurement(10, 13880000, 13879927, + 0) + ndr_measured_high = ReceiveRateMeasurement(10, 14880000, 14879927, + 0) + ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_low = ReceiveRateMeasurement(10, 11880000, 11879927, + 0) + pdr_measured_high = ReceiveRateMeasurement(10, 12880000, 12879927, + 0) + pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_interval = ReceiveRateInterval(ndr_measured_low, + ndr_measured_high) + pdr_interval = ReceiveRateInterval(pdr_measured_low, + pdr_measured_high) + starting_result = NdrPdrResult(ndr_interval, pdr_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock_ndrpdr.return_value = MultipleLossRatioSearch.ProgressState( + starting_result, 2, 30, 0.005, 0.0, + 4857361, 4977343) + self.assertEqual( + {'Result_NDR_LOWER': {'bandwidth_total_Gbps': 0.9327310944, + 'rate_total_pps': 1387992.7}, + 'Result_NDR_UPPER': { + 'bandwidth_total_Gbps': 0.9999310943999999, + 'rate_total_pps': 1487992.7}, + 'Result_NDR_packets_lost': {'packet_loss_ratio': 0.0, + 'packets_lost': 0.0}, + 'Result_PDR_LOWER': { + 'bandwidth_total_Gbps': 0.7983310943999999, + 'rate_total_pps': 1187992.7}, + 'Result_PDR_UPPER': {'bandwidth_total_Gbps': 0.8655310944, + 'rate_total_pps': 1287992.7}, + 'Result_PDR_packets_lost': {'packet_loss_ratio': 0.0, + 'packets_lost': 0.0}, + 'Result_stream0_NDR_LOWER': {'avg_latency': 3081.0, + 'max_latency': 3962.0, + 'min_latency': 1000.0}, + 'Result_stream0_PDR_LOWER': {'avg_latency': 3081.0, + 'max_latency': 3962.0, + 'min_latency': 1000.0}, + 'Result_stream1_NDR_LOWER': {'avg_latency': 3149.0, + 'max_latency': 3730.0, + 'min_latency': 500.0}, + 'Result_stream1_PDR_LOWER': {'avg_latency': 3149.0, + 'max_latency': 3730.0, + 'min_latency': 500.0}}, + algorithm.narrow_down_ndr_and_pdr(12880000, 15880000, 0.0)) + + def test__measure_and_update_state(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + starting_interval = ReceiveRateInterval(measured_low, measured_high) + starting_result = NdrPdrResult(starting_interval, starting_interval) + previous_state = MultipleLossRatioSearch.ProgressState(starting_result, + 2, 30, 0.005, + 0.0, 4857361, + 4977343) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure: + mock_measure.return_value = ReceiveRateMeasurement(1, + 4626121.09635, + 4626100, 13074) + state = algorithm._measure_and_update_state(previous_state, + 4626121.09635) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(1, state.result.ndr_interval.measured_low.duration) + self.assertEqual(4626121.09635, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(4626100, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(13074, + state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(4613026, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(4626100, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(13074.0, + state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(4613026.0, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.00283, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(1, state.result.ndr_interval.measured_high.duration) + self.assertEqual(4857361, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(4857339, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(84965, + state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(4772374, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(4857339, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(84965.0, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(4772374.0, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(0.01749, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(1, state.result.pdr_interval.measured_low.duration) + self.assertEqual(4626121.09635, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(4626100, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(13074, + state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(4613026, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(4626100, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(13074.0, + state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(4613026.0, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.00283, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(1, state.result.pdr_interval.measured_high.duration) + self.assertEqual(4857361, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(4857339, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(84965, + state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(4772374, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(4857339, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(84965.0, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(4772374.0, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(0.01749, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(2, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.005, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(4857361, state.minimum_transmit_rate) + self.assertEqual(4977343, state.maximum_transmit_rate) + + def test_new_interval(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + measured = ReceiveRateMeasurement(1, 3972540.4108, 21758482, 0) + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + result = algorithm._new_interval(receive_rate_interval, measured, 0.0) + self.assertIsInstance(result, ReceiveRateInterval) + self.assertEqual(1, result.measured_low.duration) + self.assertEqual(3972540.4108, result.measured_low.target_tr) + self.assertEqual(21758482, result.measured_low.transmit_count) + self.assertEqual(0, result.measured_low.loss_count) + self.assertEqual(21758482, result.measured_low.receive_count) + self.assertEqual(21758482, result.measured_low.transmit_rate) + self.assertEqual(0.0, result.measured_low.loss_rate) + self.assertEqual(21758482.0, result.measured_low.receive_rate) + self.assertEqual(0.0, result.measured_low.loss_fraction) + self.assertEqual(1, result.measured_high.duration) + self.assertEqual(4857361, result.measured_high.target_tr) + self.assertEqual(4857339, result.measured_high.transmit_count) + self.assertEqual(84965, result.measured_high.loss_count) + self.assertEqual(4772374, result.measured_high.receive_count) + self.assertEqual(4857339, result.measured_high.transmit_rate) + self.assertEqual(84965.0, result.measured_high.loss_rate) + self.assertEqual(4772374.0, result.measured_high.receive_rate) + self.assertEqual(0.01749, result.measured_high.loss_fraction) + + def test_new_interval_zero(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + measured = ReceiveRateMeasurement(1, 4977343, 21758482, 0) + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + result = algorithm._new_interval(receive_rate_interval, measured, 0.0) + self.assertIsInstance(result, ReceiveRateInterval) + self.assertEqual(1, result.measured_low.duration) + self.assertEqual(4857361.0, result.measured_low.target_tr) + self.assertEqual(4857339, result.measured_low.transmit_count) + self.assertEqual(84965, result.measured_low.loss_count) + self.assertEqual(4772374, result.measured_low.receive_count) + self.assertEqual(4857339.0, result.measured_low.transmit_rate) + self.assertEqual(84965.0, result.measured_low.loss_rate) + self.assertEqual(4772374.0, result.measured_low.receive_rate) + self.assertEqual(0.01749, result.measured_low.loss_fraction) + self.assertEqual(1, result.measured_high.duration) + self.assertEqual(4977343.0, result.measured_high.target_tr) + self.assertEqual(21758482, result.measured_high.transmit_count) + self.assertEqual(0, result.measured_high.loss_count) + self.assertEqual(21758482, result.measured_high.receive_count) + self.assertEqual(21758482.0, result.measured_high.transmit_rate) + self.assertEqual(0.0, result.measured_high.loss_rate) + self.assertEqual(21758482.0, result.measured_high.receive_rate) + self.assertEqual(0.0, result.measured_high.loss_fraction) + + def test_new_interval_one(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + measured = ReceiveRateMeasurement(1, 5000000, 2175848, 0) + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + result = algorithm._new_interval(receive_rate_interval, measured, 0.0) + self.assertIsInstance(result, ReceiveRateInterval) + self.assertEqual(1, result.measured_low.duration) + self.assertEqual(4857361.0, result.measured_low.target_tr) + self.assertEqual(4857339, result.measured_low.transmit_count) + self.assertEqual(84965, result.measured_low.loss_count) + self.assertEqual(4772374, result.measured_low.receive_count) + self.assertEqual(4857339.0, result.measured_low.transmit_rate) + self.assertEqual(84965.0, result.measured_low.loss_rate) + self.assertEqual(4772374.0, result.measured_low.receive_rate) + self.assertEqual(0.01749, result.measured_low.loss_fraction) + self.assertEqual(1, result.measured_high.duration) + self.assertEqual(4977343.0, result.measured_high.target_tr) + self.assertEqual(4977320, result.measured_high.transmit_count) + self.assertEqual(119959, result.measured_high.loss_count) + self.assertEqual(4857361, result.measured_high.receive_count) + self.assertEqual(4977320.0, result.measured_high.transmit_rate) + self.assertEqual(119959.0, result.measured_high.loss_rate) + self.assertEqual(4857361.0, result.measured_high.receive_rate) + self.assertEqual(0.0241, result.measured_high.loss_fraction) + + def test_new_interval_valid_1st(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + measured = ReceiveRateMeasurement(1, 4000000, 2175848, 0) + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + result = algorithm._new_interval(receive_rate_interval, measured, 0.5) + self.assertIsInstance(result, ReceiveRateInterval) + self.assertEqual(1, result.measured_low.duration) + self.assertEqual(4857361.0, result.measured_low.target_tr) + self.assertEqual(4857339, result.measured_low.transmit_count) + self.assertEqual(84965, result.measured_low.loss_count) + self.assertEqual(4772374, result.measured_low.receive_count) + self.assertEqual(4857339.0, result.measured_low.transmit_rate) + self.assertEqual(84965.0, result.measured_low.loss_rate) + self.assertEqual(4772374.0, result.measured_low.receive_rate) + self.assertEqual(0.01749, result.measured_low.loss_fraction) + self.assertEqual(1, result.measured_high.duration) + self.assertEqual(4977343.0, result.measured_high.target_tr) + self.assertEqual(4977320, result.measured_high.transmit_count) + self.assertEqual(119959, result.measured_high.loss_count) + self.assertEqual(4857361, result.measured_high.receive_count) + self.assertEqual(4977320.0, result.measured_high.transmit_rate) + self.assertEqual(119959.0, result.measured_high.loss_rate) + self.assertEqual(4857361.0, result.measured_high.receive_rate) + self.assertEqual(0.0241, result.measured_high.loss_fraction) + + def test_new_interval_valid_1st_loss(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + measured = ReceiveRateMeasurement(1, 4000000, 2175848, 1000000) + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + result = algorithm._new_interval(receive_rate_interval, measured, 0.02) + self.assertIsInstance(result, ReceiveRateInterval) + self.assertEqual(1, result.measured_low.duration) + self.assertEqual(4000000.0, result.measured_low.target_tr) + self.assertEqual(2175848, result.measured_low.transmit_count) + self.assertEqual(1000000, result.measured_low.loss_count) + self.assertEqual(1175848, result.measured_low.receive_count) + self.assertEqual(2175848.0, result.measured_low.transmit_rate) + self.assertEqual(1000000.0, result.measured_low.loss_rate) + self.assertEqual(1175848.0, result.measured_low.receive_rate) + self.assertEqual(0.45959, result.measured_low.loss_fraction) + self.assertEqual(1, result.measured_high.duration) + self.assertEqual(4977343.0, result.measured_high.target_tr) + self.assertEqual(4977320, result.measured_high.transmit_count) + self.assertEqual(119959, result.measured_high.loss_count) + self.assertEqual(4857361, result.measured_high.receive_count) + self.assertEqual(4977320.0, result.measured_high.transmit_rate) + self.assertEqual(119959.0, result.measured_high.loss_rate) + self.assertEqual(4857361.0, result.measured_high.receive_rate) + self.assertEqual(0.0241, result.measured_high.loss_fraction) + + def test_new_interval_valid_2nd(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + measured = ReceiveRateMeasurement(1, 5000000, 2175848, 0) + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + result = algorithm._new_interval(receive_rate_interval, measured, 0.5) + self.assertIsInstance(result, ReceiveRateInterval) + self.assertEqual(1, result.measured_low.duration) + self.assertEqual(4977343.0, result.measured_low.target_tr) + self.assertEqual(4977320, result.measured_low.transmit_count) + self.assertEqual(119959, result.measured_low.loss_count) + self.assertEqual(4857361, result.measured_low.receive_count) + self.assertEqual(4977320.0, result.measured_low.transmit_rate) + self.assertEqual(119959.0, result.measured_low.loss_rate) + self.assertEqual(4857361.0, result.measured_low.receive_rate) + self.assertEqual(0.0241, result.measured_low.loss_fraction) + self.assertEqual(1, result.measured_high.duration) + self.assertEqual(5000000.0, result.measured_high.target_tr) + self.assertEqual(2175848, result.measured_high.transmit_count) + self.assertEqual(0, result.measured_high.loss_count) + self.assertEqual(2175848, result.measured_high.receive_count) + self.assertEqual(2175848.0, result.measured_high.transmit_rate) + self.assertEqual(0.0, result.measured_high.loss_rate) + self.assertEqual(2175848.0, result.measured_high.receive_rate) + self.assertEqual(0.0, result.measured_high.loss_fraction) + + def test_new_interval_valid_3rd(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + measured = ReceiveRateMeasurement(1, 4867361, 2175848, 0) + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + result = algorithm._new_interval(receive_rate_interval, measured, 0.5) + self.assertIsInstance(result, ReceiveRateInterval) + self.assertEqual(1, result.measured_low.duration) + self.assertEqual(4867361.0, result.measured_low.target_tr) + self.assertEqual(2175848, result.measured_low.transmit_count) + self.assertEqual(0, result.measured_low.loss_count) + self.assertEqual(2175848, result.measured_low.receive_count) + self.assertEqual(2175848.0, result.measured_low.transmit_rate) + self.assertEqual(0.0, result.measured_low.loss_rate) + self.assertEqual(2175848.0, result.measured_low.receive_rate) + self.assertEqual(0.0, result.measured_low.loss_fraction) + self.assertEqual(1, result.measured_high.duration) + self.assertEqual(4977343.0, result.measured_high.target_tr) + self.assertEqual(4977320, result.measured_high.transmit_count) + self.assertEqual(119959, result.measured_high.loss_count) + self.assertEqual(4857361, result.measured_high.receive_count) + self.assertEqual(4977320.0, result.measured_high.transmit_rate) + self.assertEqual(119959.0, result.measured_high.loss_rate) + self.assertEqual(4857361.0, result.measured_high.receive_rate) + self.assertEqual(0.0241, result.measured_high.loss_fraction) + + def test_new_interval_valid_3rd_loss(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + measured = ReceiveRateMeasurement(1, 4867361, 2175848, 1000000) + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + result = algorithm._new_interval(receive_rate_interval, measured, 0.2) + self.assertIsInstance(result, ReceiveRateInterval) + self.assertEqual(1, result.measured_low.duration) + self.assertEqual(4857361.0, result.measured_low.target_tr) + self.assertEqual(4857339, result.measured_low.transmit_count) + self.assertEqual(84965, result.measured_low.loss_count) + self.assertEqual(4772374, result.measured_low.receive_count) + self.assertEqual(4857339.0, result.measured_low.transmit_rate) + self.assertEqual(84965.0, result.measured_low.loss_rate) + self.assertEqual(4772374.0, result.measured_low.receive_rate) + self.assertEqual(0.01749, result.measured_low.loss_fraction) + self.assertEqual(1, result.measured_high.duration) + self.assertEqual(4867361.0, result.measured_high.target_tr) + self.assertEqual(2175848, result.measured_high.transmit_count) + self.assertEqual(1000000, result.measured_high.loss_count) + self.assertEqual(1175848, result.measured_high.receive_count) + self.assertEqual(2175848.0, result.measured_high.transmit_rate) + self.assertEqual(1000000.0, result.measured_high.loss_rate) + self.assertEqual(1175848.0, result.measured_high.receive_rate) + self.assertEqual(0.45959, result.measured_high.loss_fraction) + + def test_ndrpdr(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure: + measured_low = ReceiveRateMeasurement(30, 14880000, 14879927, 0) + measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, 0) + measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + starting_interval = ReceiveRateInterval(measured_low, + measured_high) + starting_result = NdrPdrResult(starting_interval, + starting_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 30, 0.005, 0.0, 14880000, + 14880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(30, state.result.ndr_interval.measured_low.duration) + self.assertEqual(14880000, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(14879927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_low.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(14879927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.005, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_ndrpdr_ndr_rel_width(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, '_measure_and_update_state') as \ + mock__measure_and_update_state: + measured_low = ReceiveRateMeasurement(30, 880000, 879927, 0) + measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, 0) + measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + starting_interval = ReceiveRateInterval(measured_low, + measured_high) + ending_interval = ReceiveRateInterval(measured_high, measured_high) + starting_result = NdrPdrResult(starting_interval, + starting_interval) + ending_result = NdrPdrResult(ending_interval, ending_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock__measure_and_update_state.return_value = \ + MultipleLossRatioSearch.ProgressState(ending_result, -1, 30, + 0.005, 0.0, 14880000, + 14880000) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 30, 0.005, 0.0, 14880000, + 14880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(30, state.result.ndr_interval.measured_low.duration) + self.assertEqual(14880000, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(14879927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_low.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(14879927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.005, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_ndrpdr_pdr_rel_width(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, '_measure_and_update_state') as \ + mock__measure_and_update_state: + ndr_measured_low = ReceiveRateMeasurement(30, 14880000, 14879927, + 0) + ndr_measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, + 0) + ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_low = ReceiveRateMeasurement(30, 880000, 879927, 0) + pdr_measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, + 0) + pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_interval = ReceiveRateInterval(ndr_measured_low, + ndr_measured_high) + pdr_interval = ReceiveRateInterval(pdr_measured_low, + pdr_measured_high) + starting_result = NdrPdrResult(ndr_interval, pdr_interval) + ending_result = NdrPdrResult(ndr_interval, ndr_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock__measure_and_update_state.return_value = \ + MultipleLossRatioSearch.ProgressState(ending_result, -1, 30, + 0.005, 0.0, 14880000, + 14880000) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 30, 0.005, 0.0, 14880000, + 14880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(30, state.result.ndr_interval.measured_low.duration) + self.assertEqual(14880000, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(14879927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_low.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(14879927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.005, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_ndrpdr_ndr_lo_duration(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, '_measure_and_update_state') as \ + mock__measure_and_update_state: + measured_low = ReceiveRateMeasurement(30, 14880000, 14879927, 0) + measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, 100) + measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + starting_interval = ReceiveRateInterval(measured_low, + measured_high) + starting_result = NdrPdrResult(starting_interval, + starting_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock__measure_and_update_state.return_value = \ + MultipleLossRatioSearch.ProgressState(starting_result, -1, 30, + 0.005, 0.0, 14880000, + 14880000) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 50, 0.005, 0.0, 14880000, + 14880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(30, state.result.ndr_interval.measured_low.duration) + self.assertEqual(14880000, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(14879927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(100, + state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(14879827, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(3.33333, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(495994.23333, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(1e-05, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_low.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(14879927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(100, + state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(14879827, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(3.33333, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(495994.23333, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(1e-05, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.005, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_ndrpdr_ndr_hi_duration(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, '_measure_and_update_state') as \ + mock__measure_and_update_state: + measured_low = ReceiveRateMeasurement(60, 14880000, 14879927, 0) + measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, 100) + measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + starting_interval = ReceiveRateInterval(measured_low, + measured_high) + starting_result = NdrPdrResult(starting_interval, + starting_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock__measure_and_update_state.return_value = \ + MultipleLossRatioSearch.ProgressState(starting_result, -1, 30, + 0.005, 0.0, 14880000, + 14880000) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 50, 0.005, 0.0, 14880000, + 14880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(60.0, state.result.ndr_interval.measured_low.duration) + self.assertEqual(14880000, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(14879927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(247998.78333, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(247998.78333, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(100, + state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(14879827, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(3.33333, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(495994.23333, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(1e-05, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(60.0, state.result.pdr_interval.measured_low.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(14879927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(247998.78333, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(247998.78333, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(100, + state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(14879827, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(3.33333, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(495994.23333, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(1e-05, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.005, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_ndrpdr_error(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=0) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure: + measured_low = ReceiveRateMeasurement(30, 14880000, 14879927, 0) + measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, 0) + measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + starting_interval = ReceiveRateInterval(measured_low, + measured_high) + starting_result = NdrPdrResult(starting_interval, + starting_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 30, 0.005, 0.0, 14880000, + 14880000) + with self.assertRaises(RuntimeError) as raised: + algorithm.ndrpdr(previous_state) + + self.assertIn('Optimized search takes too long.', + str(raised.exception)) + + def test_ndrpdr_update_state_ndr_hi(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, '_measure_and_update_state') as \ + mock__measure_and_update_state: + ndr_measured_low = ReceiveRateMeasurement(30, 10880000, 10879927, + 0) + ndr_measured_high = ReceiveRateMeasurement(30, 12880000, 12879927, + 0) + ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_low = ReceiveRateMeasurement(30, 12880000, 12879927, + 0) + pdr_measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, + 0) + pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_interval = ReceiveRateInterval(ndr_measured_low, + ndr_measured_high) + pdr_interval = ReceiveRateInterval(pdr_measured_low, + pdr_measured_high) + starting_result = NdrPdrResult(ndr_interval, pdr_interval) + ending_result = NdrPdrResult(pdr_interval, pdr_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock__measure_and_update_state.return_value = \ + MultipleLossRatioSearch.ProgressState(ending_result, -1, 30, + 0.2, 0.0, 14880000, + 14880000) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 30, 0.005, 0.0, 14880000, + 14880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(30, state.result.ndr_interval.measured_low.duration) + self.assertEqual(12880000.0, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(12879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(12879927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(429330.9, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(429330.9, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(14880000.0, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_low.duration) + self.assertEqual(12880000.0, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(12879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(12879927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(429330.9, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(429330.9, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.2, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_ndrpdr_update_state_ndr_hi_duration(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, '_measure_and_update_state') as \ + mock__measure_and_update_state: + ndr_measured_low = ReceiveRateMeasurement(30, 10880000, 10879927, + 0) + ndr_measured_high = ReceiveRateMeasurement(30, 12880000, 12879927, + 0) + ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_low = ReceiveRateMeasurement(30, 12880000, 12879927, + 0) + pdr_measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, + 0) + pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_interval = ReceiveRateInterval(ndr_measured_low, + ndr_measured_high) + pdr_interval = ReceiveRateInterval(pdr_measured_low, + pdr_measured_high) + starting_result = NdrPdrResult(ndr_interval, pdr_interval) + ending_result = NdrPdrResult(pdr_interval, pdr_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock__measure_and_update_state.return_value = \ + MultipleLossRatioSearch.ProgressState(ending_result, -1, 30, + 0.2, 0.0, 14880000, + 14880000) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 50, 0.005, 0.0, 4880000, + 10880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(30, state.result.ndr_interval.measured_low.duration) + self.assertEqual(12880000.0, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(12879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(12879927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(429330.9, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(429330.9, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(14880000.0, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_low.duration) + self.assertEqual(12880000.0, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(12879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(12879927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(429330.9, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(429330.9, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.2, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_ndrpdr_update_state_ndr_lo(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, '_measure_and_update_state') as \ + mock__measure_and_update_state: + ndr_measured_low = ReceiveRateMeasurement(30, 10880000, 10879927, + 100000) + ndr_measured_high = ReceiveRateMeasurement(30, 12880000, 12879927, + 100000) + ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_low = ReceiveRateMeasurement(30, 12880000, 12879927, + 100000) + pdr_measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, + 100000) + pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_interval = ReceiveRateInterval(ndr_measured_low, + ndr_measured_high) + pdr_interval = ReceiveRateInterval(pdr_measured_low, + pdr_measured_high) + starting_result = NdrPdrResult(ndr_interval, pdr_interval) + ending_result = NdrPdrResult(pdr_interval, pdr_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock__measure_and_update_state.return_value = \ + MultipleLossRatioSearch.ProgressState(ending_result, -1, 30, + 0.2, 0.0, 14880000, + 14880000) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 30, 0.005, 0.0, 100000, + 14880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(30, state.result.ndr_interval.measured_low.duration) + self.assertEqual(12880000.0, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(12879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(100000, + state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(12779927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(429330.9, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(3333.33333, + state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(425997.56667, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.00776, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(14880000.0, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(100000, + state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(14779927, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(3333.33333, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(492664.23333, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(0.00672, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_low.duration) + self.assertEqual(12880000.0, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(12879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(100000, + state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(12779927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(429330.9, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(3333.33333, + state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(425997.56667, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.00776, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(100000, + state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(14779927, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(3333.33333, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(492664.23333, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(0.00672, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.2, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_ndrpdr_update_state_pdr_lo(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, '_measure_and_update_state') as \ + mock__measure_and_update_state: + ndr_measured_low = ReceiveRateMeasurement(30, 10880000, 10879927, + 0) + ndr_measured_high = ReceiveRateMeasurement(30, 12880000, 12879927, + 0) + ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_low = ReceiveRateMeasurement(30, 12880000, 12879927, + 100000) + pdr_measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, + 100000) + pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_interval = ReceiveRateInterval(ndr_measured_low, + ndr_measured_high) + pdr_interval = ReceiveRateInterval(pdr_measured_low, + pdr_measured_high) + starting_result = NdrPdrResult(ndr_interval, pdr_interval) + ending_result = NdrPdrResult(pdr_interval, pdr_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock__measure_and_update_state.return_value = \ + MultipleLossRatioSearch.ProgressState(ending_result, -1, 30, + 0.2, 0.0, 14880000, + 14880000) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 30, 0.005, 0.0, 100000, + 14880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(30, state.result.ndr_interval.measured_low.duration) + self.assertEqual(12880000.0, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(12879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(100000, + state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(12779927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(429330.9, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(3333.33333, + state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(425997.56667, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.00776, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(14880000.0, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(100000, + state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(14779927, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(3333.33333, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(492664.23333, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(0.00672, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_low.duration) + self.assertEqual(12880000.0, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(12879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(100000, + state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(12779927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(429330.9, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(3333.33333, + state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(425997.56667, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.00776, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(100000, + state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(14779927, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(3333.33333, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(492664.23333, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(0.00672, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.2, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_ndrpdr_update_state_pdr_lo_duration(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, '_measure_and_update_state') as \ + mock__measure_and_update_state: + ndr_measured_low = ReceiveRateMeasurement(30, 10880000, 10879927, + 0) + ndr_measured_high = ReceiveRateMeasurement(30, 12880000, 12879927, + 0) + ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_low = ReceiveRateMeasurement(30, 12880000, 12879927, + 100000) + pdr_measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, + 100000) + pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_interval = ReceiveRateInterval(ndr_measured_low, + ndr_measured_high) + pdr_interval = ReceiveRateInterval(pdr_measured_low, + pdr_measured_high) + starting_result = NdrPdrResult(ndr_interval, pdr_interval) + ending_result = NdrPdrResult(pdr_interval, pdr_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock__measure_and_update_state.return_value = \ + MultipleLossRatioSearch.ProgressState(ending_result, -1, 30, + 0.2, 0.0, 14880000, + 14880000) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 50, 0.005, 0.0, 14880000, + 14880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(30, state.result.ndr_interval.measured_low.duration) + self.assertEqual(12880000.0, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(12879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(100000, + state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(12779927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(429330.9, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(3333.33333, + state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(425997.56667, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.00776, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(14880000.0, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(100000, + state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(14779927, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(3333.33333, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(492664.23333, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(0.00672, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_low.duration) + self.assertEqual(12880000.0, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(12879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(100000, + state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(12779927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(429330.9, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(3333.33333, + state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(425997.56667, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.00776, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(100000, + state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(14779927, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(3333.33333, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(492664.23333, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(0.00672, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.2, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_ndrpdr_update_state_pdr_hi(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, '_measure_and_update_state') as \ + mock__measure_and_update_state: + ndr_measured_low = ReceiveRateMeasurement(30, 10880000, 10879927, + 0) + ndr_measured_high = ReceiveRateMeasurement(30, 12880000, 12879927, + 100000) + ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_low = ReceiveRateMeasurement(30, 12880000, 12879927, + 0) + pdr_measured_high = ReceiveRateMeasurement(30, 13880000, 14879927, + 0) + pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_interval = ReceiveRateInterval(ndr_measured_low, + ndr_measured_high) + pdr_interval = ReceiveRateInterval(pdr_measured_low, + pdr_measured_high) + starting_result = NdrPdrResult(ndr_interval, pdr_interval) + ending_result = NdrPdrResult(ndr_interval, ndr_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock__measure_and_update_state.return_value = \ + MultipleLossRatioSearch.ProgressState(ending_result, -1, 30, + 0.2, 0.0, 14880000, + 14880000) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 30, 0.005, 0.0, 100000, + 14880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(30, state.result.ndr_interval.measured_low.duration) + self.assertEqual(10880000.0, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(10879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(0, + state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(10879927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(362664.23333, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(362664.23333, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(12880000.0, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(12879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(100000, + state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(12779927, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(429330.9, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(3333.33333, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(425997.56667, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(0.00776, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_low.duration) + self.assertEqual(10880000.0, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(10879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(0, + state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(10879927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(362664.23333, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(362664.23333, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(12880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(12879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(100000, + state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(12779927, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(429330.9, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(3333.33333, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(425997.56667, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(0.00776, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.2, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_ndrpdr_update_state_pdr_hi_duration(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, '_measure_and_update_state') as \ + mock__measure_and_update_state: + ndr_measured_low = ReceiveRateMeasurement(30, 10880000, 10879927, + 0) + ndr_measured_high = ReceiveRateMeasurement(30, 12880000, 12879927, + 100000) + ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_low = ReceiveRateMeasurement(30, 12880000, 12879927, + 0) + pdr_measured_high = ReceiveRateMeasurement(30, 13880000, 14879927, + 0) + pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_interval = ReceiveRateInterval(ndr_measured_low, + ndr_measured_high) + pdr_interval = ReceiveRateInterval(pdr_measured_low, + pdr_measured_high) + starting_result = NdrPdrResult(ndr_interval, pdr_interval) + ending_result = NdrPdrResult(ndr_interval, ndr_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock__measure_and_update_state.return_value = \ + MultipleLossRatioSearch.ProgressState(ending_result, -1, 30, + 0.2, 0.0, 14880000, + 14880000) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 50, 0.005, 0.0, 100000, + 10880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(30, state.result.ndr_interval.measured_low.duration) + self.assertEqual(10880000.0, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(10879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(0, + state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(10879927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(362664.23333, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(362664.23333, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(12880000.0, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(12879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(100000, + state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(12779927, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(429330.9, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(3333.33333, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(425997.56667, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(0.00776, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_low.duration) + self.assertEqual(10880000.0, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(10879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(0, + state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(10879927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(362664.23333, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(362664.23333, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(12880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(12879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(100000, + state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(12779927, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(429330.9, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(3333.33333, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(425997.56667, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(0.00776, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.2, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_measure(self): + measurer = mock.MagicMock() + measurer.sent = 102563094 + measurer.loss = 30502 + algorithm = MultipleLossRatioSearch(measurer=measurer, latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.MagicMock(), + mock.Mock, mock.Mock())) + measurement = algorithm.measure(30, 3418770.3425, True) + self.assertIsInstance(measurement, ReceiveRateMeasurement) + self.assertEqual(30, measurement.duration) + self.assertEqual(3418770.3425, measurement.target_tr) + self.assertEqual(102563094, measurement.transmit_count) + self.assertEqual(30502, measurement.loss_count) + self.assertEqual(102532592, measurement.receive_count) + self.assertEqual(3418769.8, measurement.transmit_rate) + self.assertEqual(1016.73333, measurement.loss_rate) + self.assertEqual(3417753.06667, measurement.receive_rate) + self.assertEqual(0.0003, measurement.loss_fraction) + + def test_perform_additional_measurements_based_on_ndrpdr_result(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock, mock.Mock, + mock.Mock())) + result = mock.MagicMock() + result.ndr_interval.measured_low.target_tr.return_result = 100000 + self.assertIsNone( + algorithm.perform_additional_measurements_based_on_ndrpdr_result( + result)) + + def test_display_single_bound(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + result_samples = {} + self.assertIsNone( + algorithm.display_single_bound(result_samples, 'NDR_LOWER', + 4857361, 64, + ['20/849/1069', '40/69/183'])) + self.assertEqual( + {'Result_NDR_LOWER': {'bandwidth_total_Gbps': 3.264146592, + 'rate_total_pps': 4857361.0}, + 'Result_stream0_NDR_LOWER': {'avg_latency': 849.0, + 'max_latency': 1069.0, + 'min_latency': 20.0}, + 'Result_stream1_NDR_LOWER': {'avg_latency': 69.0, + 'max_latency': 183.0, + 'min_latency': 40.0}}, + result_samples) + + def test_check_ndrpdr_interval_validity(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + result_samples = {} + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 0) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 0) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + self.assertEqual('Minimal rate loss fraction 0.0 reach target 0.0', + algorithm.check_ndrpdr_interval_validity( + result_samples, 'NDR_LOWER', + receive_rate_interval)) + self.assertEqual( + {'Result_NDR_LOWER_packets_lost': {'packet_loss_ratio': 0.0, + 'packets_lost': 0.0}}, + result_samples) + + def test_check_ndrpdr_interval_validity_fail(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + result_samples = {} + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + self.assertEqual( + 'Minimal rate loss fraction 0.01749 does not reach target 0.005\n84965 packets lost.', + algorithm.check_ndrpdr_interval_validity(result_samples, + 'NDR_LOWER', + receive_rate_interval, + 0.005)) + self.assertEqual({'Result_NDR_LOWER_packets_lost': { + 'packet_loss_ratio': 0.01749, + 'packets_lost': 84965.0}}, result_samples) diff --git a/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_ndr_pdr_result.py b/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_ndr_pdr_result.py new file mode 100644 index 000000000..ea9c39a03 --- /dev/null +++ b/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_ndr_pdr_result.py @@ -0,0 +1,91 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. + +import unittest + +import mock + +from yardstick.network_services.helpers.vpp_helpers.ndr_pdr_result import \ + NdrPdrResult +from yardstick.network_services.helpers.vpp_helpers.receive_rate_interval import \ + ReceiveRateInterval +from yardstick.network_services.helpers.vpp_helpers.receive_rate_measurement import \ + ReceiveRateMeasurement + + +class TestNdrPdrResult(unittest.TestCase): + + def test___init__(self): + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + starting_interval = ReceiveRateInterval(measured_low, measured_high) + ndrpdr_result = NdrPdrResult(starting_interval, starting_interval) + self.assertIsInstance(ndrpdr_result.ndr_interval, ReceiveRateInterval) + self.assertIsInstance(ndrpdr_result.pdr_interval, ReceiveRateInterval) + + def test___init__ndr_error(self): + starting_interval = mock.MagicMock() + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + end_interval = ReceiveRateInterval(measured_low, measured_high) + with self.assertRaises(TypeError) as raised: + NdrPdrResult(starting_interval, end_interval) + self.assertIn('ndr_interval, is not a ReceiveRateInterval: ', + str(raised.exception)) + + def test___init__pdr_error(self): + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + starting_interval = ReceiveRateInterval(measured_low, measured_high) + end_interval = mock.MagicMock() + with self.assertRaises(TypeError) as raised: + NdrPdrResult(starting_interval, end_interval) + self.assertIn('pdr_interval, is not a ReceiveRateInterval: ', + str(raised.exception)) + + def test_width_in_goals(self): + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + starting_interval = ReceiveRateInterval(measured_low, measured_high) + ndrpdr_result = NdrPdrResult(starting_interval, starting_interval) + self.assertEqual('ndr 4.86887; pdr 4.86887', + ndrpdr_result.width_in_goals(0.005)) + + def test___str__(self): + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + starting_interval = ReceiveRateInterval(measured_low, measured_high) + ndrpdr_result = NdrPdrResult(starting_interval, starting_interval) + self.assertEqual( + 'NDR=[d=1.0,Tr=4857361.0,Df=0.01749;d=1.0,Tr=4977343.0,Df=0.0241);' + 'PDR=[d=1.0,Tr=4857361.0,Df=0.01749;d=1.0,Tr=4977343.0,Df=0.0241)', + ndrpdr_result.__str__()) + + def test___repr__(self): + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + starting_interval = ReceiveRateInterval(measured_low, measured_high) + ndrpdr_result = NdrPdrResult(starting_interval, starting_interval) + self.assertEqual( + 'NdrPdrResult(ndr_interval=ReceiveRateInterval(measured_low=' \ + 'ReceiveRateMeasurement(duration=1.0,target_tr=4857361.0,' \ + 'transmit_count=4857339,loss_count=84965),measured_high=' \ + 'ReceiveRateMeasurement(duration=1.0,target_tr=4977343.0,' \ + 'transmit_count=4977320,loss_count=119959)),pdr_interval=' \ + 'ReceiveRateInterval(measured_low=ReceiveRateMeasurement' \ + '(duration=1.0,target_tr=4857361.0,transmit_count=4857339,' \ + 'loss_count=84965),measured_high=ReceiveRateMeasurement' \ + '(duration=1.0,target_tr=4977343.0,transmit_count=4977320,' \ + 'loss_count=119959)))', + ndrpdr_result.__repr__()) diff --git a/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_receive_rate_interval.py b/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_receive_rate_interval.py new file mode 100644 index 000000000..bbf241613 --- /dev/null +++ b/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_receive_rate_interval.py @@ -0,0 +1,100 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. + +import unittest + +import mock + +from yardstick.network_services.helpers.vpp_helpers.receive_rate_interval import \ + ReceiveRateInterval +from yardstick.network_services.helpers.vpp_helpers.receive_rate_measurement import \ + ReceiveRateMeasurement + + +class TestReceiveRateInterval(unittest.TestCase): + + def test__init__(self): + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + self.assertIsInstance(receive_rate_interval.measured_low, + ReceiveRateMeasurement) + self.assertIsInstance(receive_rate_interval.measured_high, + ReceiveRateMeasurement) + + def test__init__measured_low_error(self): + measured_low = mock.MagicMock() + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + with self.assertRaises(TypeError) as raised: + ReceiveRateInterval(measured_low, measured_high) + self.assertIn('measured_low is not a ReceiveRateMeasurement: ', + str(raised.exception)) + + def test__init__measured_high_error(self): + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = mock.MagicMock() + with self.assertRaises(TypeError) as raised: + ReceiveRateInterval(measured_low, measured_high) + self.assertIn('measured_high is not a ReceiveRateMeasurement: ', + str(raised.exception)) + + def test_sort(self): + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + self.assertIsNone(receive_rate_interval.sort()) + self.assertEqual(119982.0, receive_rate_interval.abs_tr_width) + self.assertEqual(0.02411, + receive_rate_interval.rel_tr_width) + + def test_sort_swap(self): + measured_low = ReceiveRateMeasurement(1, 14857361, 14857339, 184965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + self.assertIsNone(receive_rate_interval.sort()) + self.assertEqual(9880018.0, receive_rate_interval.abs_tr_width) + self.assertEqual(0.66499, + receive_rate_interval.rel_tr_width) + + def test_width_in_goals(self): + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + self.assertEqual(4.86887, + receive_rate_interval.width_in_goals(0.005)) + + def test___str__(self): + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + self.assertEqual( + '[d=1.0,Tr=4857361.0,Df=0.01749;d=1.0,Tr=4977343.0,Df=0.0241)', + receive_rate_interval.__str__()) + + def test___repr__(self): + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + self.assertEqual('ReceiveRateInterval(measured_low=' \ + 'ReceiveRateMeasurement(duration=1.0,target_tr=4857361.0,' \ + 'transmit_count=4857339,loss_count=84965),measured_high=' \ + 'ReceiveRateMeasurement(duration=1.0,target_tr=4977343.0,' \ + 'transmit_count=4977320,loss_count=119959))', + receive_rate_interval.__repr__()) diff --git a/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_receive_rate_measurement.py b/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_receive_rate_measurement.py new file mode 100644 index 000000000..d4e2d7920 --- /dev/null +++ b/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_receive_rate_measurement.py @@ -0,0 +1,44 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. + +import unittest + +from yardstick.network_services.helpers.vpp_helpers.receive_rate_measurement import \ + ReceiveRateMeasurement + + +class TestReceiveRateMeasurement(unittest.TestCase): + + def test__init__(self): + measured = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + self.assertEqual(1, measured.duration) + self.assertEqual(4857361, measured.target_tr) + self.assertEqual(4857339, measured.transmit_count) + self.assertEqual(84965, measured.loss_count) + self.assertEqual(4772374, measured.receive_count) + self.assertEqual(4857339, measured.transmit_rate) + self.assertEqual(84965.0, measured.loss_rate) + self.assertEqual(4772374.0, measured.receive_rate) + self.assertEqual(0.01749, measured.loss_fraction) + + def test___str__(self): + measured = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + self.assertEqual('d=1.0,Tr=4857361.0,Df=0.01749', + measured.__str__()) + + def test___repr__(self): + measured = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + self.assertEqual('ReceiveRateMeasurement(duration=1.0,' \ + 'target_tr=4857361.0,transmit_count=4857339,loss_count=84965)', + measured.__repr__()) diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_vpp_rfc2544.py b/yardstick/tests/unit/network_services/traffic_profile/test_vpp_rfc2544.py index 4e5a0f708..8ad17b547 100644 --- a/yardstick/tests/unit/network_services/traffic_profile/test_vpp_rfc2544.py +++ b/yardstick/tests/unit/network_services/traffic_profile/test_vpp_rfc2544.py @@ -18,8 +18,17 @@ from trex_stl_lib import trex_stl_packet_builder_scapy from trex_stl_lib import trex_stl_streams from yardstick.common import constants +from yardstick.network_services.helpers.vpp_helpers.multiple_loss_ratio_search import \ + MultipleLossRatioSearch +from yardstick.network_services.helpers.vpp_helpers.ndr_pdr_result import \ + NdrPdrResult +from yardstick.network_services.helpers.vpp_helpers.receive_rate_interval import \ + ReceiveRateInterval +from yardstick.network_services.helpers.vpp_helpers.receive_rate_measurement import \ + ReceiveRateMeasurement from yardstick.network_services.traffic_profile import base as tp_base from yardstick.network_services.traffic_profile import rfc2544, vpp_rfc2544 +from yardstick.network_services.traffic_profile.rfc2544 import PortPgIDMap from yardstick.tests.unit import base @@ -129,6 +138,7 @@ class TestVppRFC2544Profile(base.BaseUnitTestCase): self.assertEqual(vpp_rfc2544_profile.max_rate, vpp_rfc2544_profile.rate) self.assertEqual(0, vpp_rfc2544_profile.min_rate) + self.assertEqual(2, vpp_rfc2544_profile.number_of_intermediate_phases) self.assertEqual(30, vpp_rfc2544_profile.duration) self.assertEqual(0.1, vpp_rfc2544_profile.precision) self.assertEqual(1.0, vpp_rfc2544_profile.lower_bound) @@ -287,6 +297,7 @@ class TestVppRFC2544Profile(base.BaseUnitTestCase): vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( self.TRAFFIC_PROFILE_MAX_RATE) vpp_rfc2544_profile.init_queue(mock.MagicMock()) + vpp_rfc2544_profile.pkt_size = 64 vpp_rfc2544_profile.params = { 'downlink_0': 'profile1', 'uplink_0': 'profile2'} @@ -480,6 +491,7 @@ class TestVppRFC2544Profile(base.BaseUnitTestCase): mock_latency.side_effect = ['latency1', 'latency2'] vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( self.TRAFFIC_PROFILE_MAX_RATE) + vpp_rfc2544_profile.pkt_size = 64 vpp_rfc2544_profile.port_pg_id = rfc2544.PortPgIDMap() vpp_rfc2544_profile.port_pg_id.add_port(1) with mock.patch.object(vpp_rfc2544_profile, '_create_single_packet') as \ @@ -523,10 +535,69 @@ class TestVppRFC2544Profile(base.BaseUnitTestCase): def test_binary_search_with_optimized(self): vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( self.TRAFFIC_PROFILE) + vpp_rfc2544_profile.pkt_size = 64 + vpp_rfc2544_profile.init_queue(mock.MagicMock()) mock_generator = mock.MagicMock() - self.assertIsNone( - vpp_rfc2544_profile.binary_search_with_optimized(mock_generator, - 30, 720, '')) + mock_generator.vnfd_helper.interfaces = [ + {"name": "xe0"}, {"name": "xe0"} + ] + + vpp_rfc2544_profile.ports = [0, 1] + vpp_rfc2544_profile.port_pg_id = PortPgIDMap() + vpp_rfc2544_profile.port_pg_id.add_port(0) + vpp_rfc2544_profile.port_pg_id.add_port(1) + vpp_rfc2544_profile.profiles = mock.MagicMock() + vpp_rfc2544_profile.test_data = mock.MagicMock() + vpp_rfc2544_profile.queue = mock.MagicMock() + + with mock.patch.object(MultipleLossRatioSearch, 'measure') as \ + mock_measure, \ + mock.patch.object(MultipleLossRatioSearch, 'ndrpdr') as \ + mock_ndrpdr: + measured_low = ReceiveRateMeasurement(1, 14880000, 14879927, 0) + measured_high = ReceiveRateMeasurement(1, 14880000, 14879927, 0) + measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + starting_interval = ReceiveRateInterval(measured_low, + measured_high) + starting_result = NdrPdrResult(starting_interval, + starting_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock_ndrpdr.return_value = MultipleLossRatioSearch.ProgressState( + starting_result, 2, 30, 0.005, 0.0, + 4857361, 4977343) + + result_samples = vpp_rfc2544_profile.binary_search_with_optimized( + traffic_generator=mock_generator, duration=30, + timeout=720, + test_data={}) + + expected = {'Result_NDR_LOWER': {'bandwidth_total_Gbps': 9.999310944, + 'rate_total_pps': 14879927.0}, + 'Result_NDR_UPPER': {'bandwidth_total_Gbps': 9.999310944, + 'rate_total_pps': 14879927.0}, + 'Result_NDR_packets_lost': {'packet_loss_ratio': 0.0, + 'packets_lost': 0.0}, + 'Result_PDR_LOWER': {'bandwidth_total_Gbps': 9.999310944, + 'rate_total_pps': 14879927.0}, + 'Result_PDR_UPPER': {'bandwidth_total_Gbps': 9.999310944, + 'rate_total_pps': 14879927.0}, + 'Result_PDR_packets_lost': {'packet_loss_ratio': 0.0, + 'packets_lost': 0.0}, + 'Result_stream0_NDR_LOWER': {'avg_latency': 3081.0, + 'max_latency': 3962.0, + 'min_latency': 1000.0}, + 'Result_stream0_PDR_LOWER': {'avg_latency': 3081.0, + 'max_latency': 3962.0, + 'min_latency': 1000.0}, + 'Result_stream1_NDR_LOWER': {'avg_latency': 3149.0, + 'max_latency': 3730.0, + 'min_latency': 500.0}, + 'Result_stream1_PDR_LOWER': {'avg_latency': 3149.0, + 'max_latency': 3730.0, + 'min_latency': 500.0}} + self.assertEqual(expected, result_samples) def test_binary_search(self): vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_ipsec_vnf.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_ipsec_vnf.py index f8126307c..00dc4a5d1 100644 --- a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_ipsec_vnf.py +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_ipsec_vnf.py @@ -19,11 +19,12 @@ import mock from yardstick.benchmark.contexts import base as ctx_base from yardstick.common import utils +from yardstick.network_services.helpers import cpu from yardstick.network_services.nfvi.resource import ResourceProfile -from yardstick.network_services.vnf_generic.vnf import ipsec_vnf +from yardstick.network_services.vnf_generic.vnf import ipsec_vnf, vpp_helpers from yardstick.network_services.vnf_generic.vnf.base import VnfdHelper -from yardstick.network_services.vnf_generic.vnf.ipsec_vnf import \ - VipsecApproxSetupEnvHelper +from yardstick.network_services.vnf_generic.vnf.ipsec_vnf import CryptoAlg, \ + IntegAlg, VipsecApproxSetupEnvHelper from yardstick.tests.unit.network_services.vnf_generic.vnf.test_base import \ mock_ssh @@ -32,6 +33,24 @@ SSH_HELPER = 'yardstick.network_services.vnf_generic.vnf.sample_vnf.VnfSshHelper NAME = 'vnf__1' +class TestCryptoAlg(unittest.TestCase): + + def test__init__(self): + encr_alg = CryptoAlg.AES_GCM_128 + self.assertEqual('aes-gcm-128', encr_alg.alg_name) + self.assertEqual('AES-GCM', encr_alg.scapy_name) + self.assertEqual(20, encr_alg.key_len) + + +class TestIntegAlg(unittest.TestCase): + + def test__init__(self): + auth_alg = IntegAlg.AES_GCM_128 + self.assertEqual('aes-gcm-128', auth_alg.alg_name) + self.assertEqual('AES-GCM', auth_alg.scapy_name) + self.assertEqual(20, auth_alg.key_len) + + @mock.patch("yardstick.network_services.vnf_generic.vnf.sample_vnf.Process") class TestVipsecApproxVnf(unittest.TestCase): VNFD = {'vnfd:vnfd-catalog': @@ -791,6 +810,62 @@ class TestVipsecApproxSetupEnvHelper(unittest.TestCase): } } + ALL_OPTIONS_CBC_ALGORITHMS = { + "flow": { + "count": 1, + "dst_ip": [ + "20.0.0.0-20.0.0.100" + ], + "src_ip": [ + "10.0.0.0-10.0.0.100" + ] + }, + "framesize": { + "downlink": { + "64B": 100 + }, + "uplink": { + "64B": 100 + } + }, + "rfc2544": { + "allowed_drop_rate": "0.0 - 0.005" + }, + "tg__0": { + "collectd": { + "interval": 1 + }, + "queues_per_port": 7 + }, + "traffic_type": 4, + "vnf__0": { + "collectd": { + "interval": 1 + }, + "vnf_config": { + "crypto_type": "SW_cryptodev", + "rxq": 1, + "worker_config": "1C/1T", + "worker_threads": 4 + } + }, + "vnf__1": { + "collectd": { + "interval": 1 + }, + "vnf_config": { + "crypto_type": "SW_cryptodev", + "rxq": 1, + "worker_config": "1C/1T", + "worker_threads": 4 + } + }, + "vpp_config": { + "crypto_algorithms": "cbc-sha1", + "tunnel": 1 + } + } + ALL_OPTIONS_ERROR = { "flow_error": { "count": 1, @@ -859,6 +934,18 @@ class TestVipsecApproxSetupEnvHelper(unittest.TestCase): } } + OPTIONS_HW = { + "collectd": { + "interval": 1 + }, + "vnf_config": { + "crypto_type": "HW_cryptodev", + "rxq": 1, + "worker_config": "1C/1T", + "worker_threads": 4 + } + } + CPU_LAYOUT = {'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 1, 1, 0], [2, 1, 0, 0, 0, 2, 2, 1], @@ -877,6 +964,24 @@ class TestVipsecApproxSetupEnvHelper(unittest.TestCase): [15, 8, 0, 1, 0, 15, 15, 7], [16, 9, 0, 1, 0, 16, 16, 8], [17, 9, 0, 1, 0, 17, 17, 8]]} + CPU_SMT = {'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 1, 1, 0], + [2, 1, 0, 0, 0, 2, 2, 1], + [3, 1, 0, 0, 0, 3, 3, 1], + [4, 2, 0, 0, 0, 4, 4, 2], + [5, 2, 0, 0, 0, 5, 5, 2], + [6, 3, 0, 0, 0, 6, 6, 3], + [7, 3, 0, 0, 0, 7, 7, 3], + [8, 4, 0, 0, 0, 8, 8, 4], + [9, 5, 0, 1, 0, 0, 0, 0], + [10, 6, 0, 1, 0, 1, 1, 0], + [11, 6, 0, 1, 0, 2, 2, 1], + [12, 7, 0, 1, 0, 3, 3, 1], + [13, 7, 0, 1, 0, 4, 4, 2], + [14, 8, 0, 1, 0, 5, 5, 2], + [15, 8, 0, 1, 0, 6, 6, 3], + [16, 9, 0, 1, 0, 7, 7, 3], + [17, 9, 0, 1, 0, 8, 8, 4]]} VPP_INTERFACES_DUMP = [ { @@ -1126,12 +1231,97 @@ class TestVipsecApproxSetupEnvHelper(unittest.TestCase): vnfd_helper = VnfdHelper( TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + ipsec_approx_setup_helper.sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper.sys_cores.cpuinfo = self.CPU_LAYOUT + ipsec_approx_setup_helper._update_vnfd_helper( + ipsec_approx_setup_helper.sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone(ipsec_approx_setup_helper.build_config()) + self.assertEqual(0, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'numa_node')) + self.assertEqual('TenGigabitEthernetff/6/0', + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'vpp_name')) + self.assertEqual(1, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'vpp_sw_index')) + self.assertEqual(0, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'numa_node')) + self.assertEqual('VirtualFunctionEthernetff/7/0', + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'vpp_name')) + self.assertEqual(2, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'vpp_sw_index')) + self.assertGreaterEqual(ssh_helper.execute.call_count, 4) + + def test_build_config_cbc_algorithms(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS_CBC_ALGORITHMS ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, ssh_helper, scenario_helper) - ipsec_approx_setup_helper.build_config() + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + ipsec_approx_setup_helper.sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper.sys_cores.cpuinfo = self.CPU_LAYOUT + ipsec_approx_setup_helper._update_vnfd_helper( + ipsec_approx_setup_helper.sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone(ipsec_approx_setup_helper.build_config()) + self.assertEqual(0, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'numa_node')) + self.assertEqual('TenGigabitEthernetff/6/0', + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'vpp_name')) + self.assertEqual(1, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'vpp_sw_index')) + self.assertEqual(0, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'numa_node')) + self.assertEqual('VirtualFunctionEthernetff/7/0', + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'vpp_name')) + self.assertEqual(2, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'vpp_sw_index')) + self.assertGreaterEqual(ssh_helper.execute.call_count, 4) @mock.patch.object(utils, 'setup_hugepages') def test_setup_vnf_environment(self, *args): @@ -1147,9 +1337,81 @@ class TestVipsecApproxSetupEnvHelper(unittest.TestCase): ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, ssh_helper, scenario_helper) - self.assertIsInstance( - ipsec_approx_setup_helper.setup_vnf_environment(), - ResourceProfile) + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + self.assertIsInstance( + ipsec_approx_setup_helper.setup_vnf_environment(), + ResourceProfile) + self.assertEqual(0, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'numa_node')) + self.assertEqual('TenGigabitEthernetff/6/0', + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'vpp_name')) + self.assertEqual(1, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'vpp_sw_index')) + self.assertEqual(0, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'numa_node')) + self.assertEqual('VirtualFunctionEthernetff/7/0', + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'vpp_name')) + self.assertEqual(2, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'vpp_sw_index')) + self.assertGreaterEqual(ssh_helper.execute.call_count, 4) + + @mock.patch.object(utils, 'setup_hugepages') + def test_setup_vnf_environment_hw(self, *args): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.nodes = [None, None] + scenario_helper.options = self.OPTIONS_HW + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + self.assertIsInstance( + ipsec_approx_setup_helper.setup_vnf_environment(), + ResourceProfile) + self.assertEqual(0, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'numa_node')) + self.assertEqual('TenGigabitEthernetff/6/0', + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'vpp_name')) + self.assertEqual(1, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'vpp_sw_index')) + self.assertEqual(0, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'numa_node')) + self.assertEqual('VirtualFunctionEthernetff/7/0', + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'vpp_name')) + self.assertEqual(2, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'vpp_sw_index')) + self.assertGreaterEqual(ssh_helper.execute.call_count, 4) def test_calculate_frame_size(self): vnfd_helper = VnfdHelper( @@ -1214,26 +1476,244 @@ class TestVipsecApproxSetupEnvHelper(unittest.TestCase): self.assertFalse(ipsec_approx_setup_helper.check_status()) def test_get_vpp_statistics(self): + def execute(cmd): + if 'TenGigabitEthernetff/6/0' in cmd: + return 0, output_xe0, '' + elif 'VirtualFunctionEthernetff/7/0' in cmd: + return 0, output_xe1, '' + return 0, '0', '' + + output_xe0 = \ + ' Name Idx State MTU (L3/IP4/IP6/MPLS)' \ + ' Counter Count \n' \ + 'TenGigabitEthernetff/6/0 1 up 9200/0/0/0 ' \ + 'rx packets 23373568\n' \ + ' ' \ + 'rx bytes 1402414080\n' \ + ' ' \ + 'tx packets 20476416\n' \ + ' ' \ + 'tx bytes 1228584960\n' \ + ' ' \ + 'ip4 23373568\n' \ + ' ' \ + 'rx-miss 27789925' + output_xe1 = \ + ' Name Idx State MTU (L3/IP4/IP6/MPLS)' \ + ' Counter Count \n' \ + 'VirtualFunctionEthernetff/7/0 2 up 9200/0/0/0 ' \ + 'rx packets 23373568\n' \ + ' ' \ + 'rx bytes 1402414080\n' \ + ' ' \ + 'tx packets 20476416\n' \ + ' ' \ + 'tx bytes 1228584960\n' \ + ' ' \ + 'ip4 23373568\n' \ + ' ' \ + 'rx-miss 27789925' + vnfd_helper = VnfdHelper( TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) ssh_helper = mock.Mock() + ssh_helper.execute = execute scenario_helper = mock.Mock() ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, ssh_helper, scenario_helper) - ipsec_approx_setup_helper.get_vpp_statistics() + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper._update_vnfd_helper( + sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertEqual({'xe0': {'packets_dropped': 27789925, + 'packets_fwd': 20476416, + 'packets_in': 23373568}, + 'xe1': {'packets_dropped': 27789925, + 'packets_fwd': 20476416, + 'packets_in': 23373568}}, + ipsec_approx_setup_helper.get_vpp_statistics()) + + def test_parser_vpp_stats(self): + output = \ + ' Name Idx State MTU (L3/IP4/IP6/MPLS)' \ + 'Counter Count \n' \ + 'TenGigabitEthernetff/6/0 1 up 9200/0/0/0 ' \ + 'rx packets 23373568\n' \ + ' ' \ + 'rx bytes 1402414080\n' \ + ' ' \ + 'tx packets 20476416\n' \ + ' ' \ + 'tx bytes 1228584960\n' \ + ' ' \ + 'ip4 23373568\n' \ + ' ' \ + 'rx-miss 27789925' + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual({'xe0': {'packets_dropped': 27789925, + 'packets_fwd': 20476416, + 'packets_in': 23373568}}, + ipsec_approx_setup_helper.parser_vpp_stats('xe0', + 'TenGigabitEthernetff/6/0', + output)) + + def test_parser_vpp_stats_no_miss(self): + output = \ + ' Name Idx State ' \ + 'Counter Count \n' \ + 'TenGigabitEthernetff/6/0 1 up ' \ + 'rx packets 23373568\n' \ + ' ' \ + 'rx bytes 1402414080\n' \ + ' ' \ + 'tx packets 20476416\n' \ + ' ' \ + 'tx bytes 1228584960\n' \ + ' ' \ + 'ip4 23373568' + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual({'xe0': {'packets_dropped': 2897152, + 'packets_fwd': 20476416, + 'packets_in': 23373568}}, + ipsec_approx_setup_helper.parser_vpp_stats('xe0', + 'TenGigabitEthernetff/6/0', + output)) def test_create_ipsec_tunnels(self): vnfd_helper = VnfdHelper( TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out, \ + mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template, \ + mock.patch.object(ipsec_approx_setup_helper, + 'vpp_get_interface_data') as \ + mock_ipsec_approx_setup_helper: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + mock_vat_terminal_exec_cmd_from_template.return_value = self.VPP_INTERFACES_DUMP + mock_ipsec_approx_setup_helper.return_value = self.VPP_INTERFACES_DUMP + sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper._update_vnfd_helper( + sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone(ipsec_approx_setup_helper.create_ipsec_tunnels()) + self.assertGreaterEqual( + mock_vat_terminal_exec_cmd_from_template.call_count, 9) + + def test_create_ipsec_tunnels_cbc_algorithms(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS_CBC_ALGORITHMS ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, ssh_helper, scenario_helper) - ipsec_approx_setup_helper.create_ipsec_tunnels() + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out, \ + mock.patch.object(ipsec_approx_setup_helper, + 'find_encrypted_data_interface') as \ + mock_find_encrypted_data_interface, \ + mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template, \ + mock.patch.object(ipsec_approx_setup_helper, + 'vpp_get_interface_data') as \ + mock_ipsec_approx_setup_helper: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + mock_find_encrypted_data_interface.return_value = { + 'dpdk_port_num': 0, + 'driver': 'igb_uio', + 'dst_ip': '192.168.100.1', + 'dst_mac': '90:e2:ba:7c:30:e8', + 'ifname': 'xe0', + 'local_ip': '192.168.100.2', + 'local_mac': '90:e2:ba:7c:41:a8', + 'netmask': '255.255.255.0', + 'network': {}, + 'node_name': 'vnf__1', + 'numa_node': 0, + 'peer_ifname': 'xe0', + 'peer_intf': {'dpdk_port_num': 0, + 'driver': 'igb_uio', + 'dst_ip': '192.168.100.2', + 'dst_mac': '90:e2:ba:7c:41:a8', + 'ifname': 'xe0', + 'local_ip': '192.168.100.1', + 'local_mac': '90:e2:ba:7c:30:e8', + 'netmask': '255.255.255.0', + 'network': {}, + 'node_name': 'tg__0', + 'peer_ifname': 'xe0', + 'peer_name': 'vnf__0', + 'vld_id': 'uplink_0', + 'vpci': '0000:81:00.0'}, + 'peer_name': 'tg__0', + 'vld_id': 'uplink_0', + 'vpci': '0000:ff:06.0', + 'vpp_name': u'TenGigabitEthernetff/6/0', + 'vpp_sw_index': 1} + mock_vat_terminal_exec_cmd_from_template.return_value = self.VPP_INTERFACES_DUMP + mock_ipsec_approx_setup_helper.return_value = self.VPP_INTERFACES_DUMP + sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper._update_vnfd_helper( + sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone(ipsec_approx_setup_helper.create_ipsec_tunnels()) + self.assertGreaterEqual( + mock_vat_terminal_exec_cmd_from_template.call_count, 9) def test_find_raw_data_interface(self): expected = {'dpdk_port_num': 0, @@ -1246,6 +1726,7 @@ class TestVipsecApproxSetupEnvHelper(unittest.TestCase): 'netmask': '255.255.255.0', 'network': {}, 'node_name': 'vnf__0', + 'numa_node': 0, 'peer_ifname': 'xe0', 'peer_intf': {'dpdk_port_num': 0, 'driver': 'igb_uio', @@ -1263,7 +1744,9 @@ class TestVipsecApproxSetupEnvHelper(unittest.TestCase): 'vpci': '0000:81:00.0'}, 'peer_name': 'tg__0', 'vld_id': 'uplink_0', - 'vpci': '0000:ff:06.0'} + 'vpci': '0000:ff:06.0', + 'vpp_name': u'TenGigabitEthernetff/6/0', + 'vpp_sw_index': 1} vnfd_helper = VnfdHelper( TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) ssh_helper = mock.Mock() @@ -1298,6 +1781,7 @@ class TestVipsecApproxSetupEnvHelper(unittest.TestCase): 'netmask': '255.255.255.0', 'network': {}, 'node_name': 'vnf__0', + 'numa_node': 0, 'peer_ifname': 'xe1', 'peer_intf': {'driver': 'igb_uio', 'dst_ip': '1.1.1.1', @@ -1314,7 +1798,9 @@ class TestVipsecApproxSetupEnvHelper(unittest.TestCase): 'vpci': '0000:00:07.0'}, 'peer_name': 'vnf__1', 'vld_id': 'ciphertext', - 'vpci': '0000:ff:07.0'} + 'vpci': '0000:ff:07.0', + 'vpp_name': u'VirtualFunctionEthernetff/7/0', + 'vpp_sw_index': 2} vnfd_helper = VnfdHelper( TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) ssh_helper = mock.Mock() @@ -1325,3 +1811,341 @@ class TestVipsecApproxSetupEnvHelper(unittest.TestCase): scenario_helper) self.assertEqual(expected, ipsec_approx_setup_helper.find_encrypted_data_interface()) + + def test_create_startup_configuration_of_vpp(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper._update_vnfd_helper( + sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsInstance( + ipsec_approx_setup_helper.create_startup_configuration_of_vpp(), + vpp_helpers.VppConfigGenerator) + + def test_add_worker_threads_and_rxqueues(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS + vpp_config_generator = vpp_helpers.VppConfigGenerator() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + ipsec_approx_setup_helper.sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper.sys_cores.cpuinfo = self.CPU_LAYOUT + ipsec_approx_setup_helper._update_vnfd_helper( + ipsec_approx_setup_helper.sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone( + ipsec_approx_setup_helper.add_worker_threads_and_rxqueues( + vpp_config_generator, 1, 1)) + self.assertEqual( + 'cpu\n{\n corelist-workers 2\n main-core 1\n}\ndpdk\n{\n ' \ + 'dev default\n {\n num-rx-queues 1\n }\n num-mbufs 32768\n}\n', + vpp_config_generator.dump_config()) + + def test_add_worker_threads_and_rxqueues_smt(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS + vpp_config_generator = vpp_helpers.VppConfigGenerator() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_SMT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + ipsec_approx_setup_helper.sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper.sys_cores.cpuinfo = self.CPU_SMT + ipsec_approx_setup_helper._update_vnfd_helper( + ipsec_approx_setup_helper.sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone( + ipsec_approx_setup_helper.add_worker_threads_and_rxqueues( + vpp_config_generator, 1)) + self.assertEqual( + 'cpu\n{\n corelist-workers 2,6\n main-core 1\n}\ndpdk\n{\n ' \ + 'dev default\n {\n num-rx-queues 1\n }\n num-mbufs 32768\n}\n', + vpp_config_generator.dump_config()) + + def test_add_worker_threads_and_rxqueues_with_numa(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS + vpp_config_generator = vpp_helpers.VppConfigGenerator() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + ipsec_approx_setup_helper.sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper.sys_cores.cpuinfo = self.CPU_LAYOUT + ipsec_approx_setup_helper._update_vnfd_helper( + ipsec_approx_setup_helper.sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone( + ipsec_approx_setup_helper.add_worker_threads_and_rxqueues( + vpp_config_generator, 1, 1)) + self.assertEqual( + 'cpu\n{\n corelist-workers 2\n main-core 1\n}\ndpdk\n{\n ' \ + 'dev default\n {\n num-rx-queues 1\n }\n num-mbufs 32768\n}\n', + vpp_config_generator.dump_config()) + + def test_add_pci_devices(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS + vpp_config_generator = vpp_helpers.VppConfigGenerator() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper._update_vnfd_helper( + sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone(ipsec_approx_setup_helper.add_pci_devices( + vpp_config_generator)) + self.assertEqual( + 'dpdk\n{\n dev 0000:ff:06.0 \n dev 0000:ff:07.0 \n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_cryptodev(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS + vpp_config_generator = vpp_helpers.VppConfigGenerator() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + ipsec_approx_setup_helper.sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper.sys_cores.cpuinfo = self.CPU_LAYOUT + ipsec_approx_setup_helper._update_vnfd_helper( + ipsec_approx_setup_helper.sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone(ipsec_approx_setup_helper.add_dpdk_cryptodev( + vpp_config_generator, 'aesni_gcm', 1)) + self.assertEqual( + 'dpdk\n{\n vdev cryptodev_aesni_gcm_pmd,socket_id=0 \n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_cryptodev_hw(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS_HW + scenario_helper.all_options = self.ALL_OPTIONS + vpp_config_generator = vpp_helpers.VppConfigGenerator() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + ipsec_approx_setup_helper.sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper.sys_cores.cpuinfo = self.CPU_LAYOUT + ipsec_approx_setup_helper._update_vnfd_helper( + ipsec_approx_setup_helper.sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone(ipsec_approx_setup_helper.add_dpdk_cryptodev( + vpp_config_generator, 'aesni_gcm', 1)) + self.assertEqual( + 'dpdk\n{\n dev 0000:ff:01.0 \n uio-driver igb_uio\n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_cryptodev_smt_used(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS + vpp_config_generator = vpp_helpers.VppConfigGenerator() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_SMT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + ipsec_approx_setup_helper.sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper.sys_cores.cpuinfo = self.CPU_LAYOUT + ipsec_approx_setup_helper._update_vnfd_helper( + ipsec_approx_setup_helper.sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone(ipsec_approx_setup_helper.add_dpdk_cryptodev( + vpp_config_generator, 'aesni_gcm', 1)) + self.assertEqual( + 'dpdk\n{\n vdev cryptodev_aesni_gcm_pmd,socket_id=0 \n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_cryptodev_smt_used_hw(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS_HW + scenario_helper.all_options = self.ALL_OPTIONS + vpp_config_generator = vpp_helpers.VppConfigGenerator() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout: + mock_get_cpu_layout.return_value = self.CPU_SMT + ipsec_approx_setup_helper.sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper.sys_cores.cpuinfo = self.CPU_SMT + ipsec_approx_setup_helper._update_vnfd_helper( + ipsec_approx_setup_helper.sys_cores.get_cpu_layout()) + self.assertIsNone(ipsec_approx_setup_helper.add_dpdk_cryptodev( + vpp_config_generator, 'aesni_gcm', 1)) + self.assertEqual( + 'dpdk\n{\n dev 0000:ff:01.0 \n dev 0000:ff:01.1 \n uio-driver igb_uio\n}\n', + vpp_config_generator.dump_config()) + + def test_initialize_ipsec(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out, \ + mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template, \ + mock.patch.object(ipsec_approx_setup_helper, + 'vpp_get_interface_data') as \ + mock_ipsec_approx_setup_helper: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + mock_vat_terminal_exec_cmd_from_template.return_value = '' + mock_ipsec_approx_setup_helper.return_value = self.VPP_INTERFACES_DUMP + sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper._update_vnfd_helper( + sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone(ipsec_approx_setup_helper.initialize_ipsec()) + self.assertGreaterEqual( + mock_vat_terminal_exec_cmd_from_template.call_count, 9) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_vcmts_pktgen.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_vcmts_pktgen.py new file mode 100755 index 000000000..3b226d3f1 --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_vcmts_pktgen.py @@ -0,0 +1,652 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. + +import unittest +import mock +import socket +import threading +import time +import os +import copy + +from yardstick.benchmark.contexts import base as ctx_base +from yardstick.network_services.vnf_generic.vnf.base import VnfdHelper +from yardstick.network_services.vnf_generic.vnf import tg_vcmts_pktgen +from yardstick.common import exceptions + + +NAME = "tg__0" + + +class TestPktgenHelper(unittest.TestCase): + + def test___init__(self): + pktgen_helper = tg_vcmts_pktgen.PktgenHelper("localhost", 23000) + self.assertEqual(pktgen_helper.host, "localhost") + self.assertEqual(pktgen_helper.port, 23000) + self.assertFalse(pktgen_helper.connected) + + def _run_fake_server(self): + server_sock = socket.socket() + server_sock.bind(('localhost', 23000)) + server_sock.listen(0) + client_socket, _ = server_sock.accept() + client_socket.close() + server_sock.close() + + def test__connect(self): + pktgen_helper = tg_vcmts_pktgen.PktgenHelper("localhost", 23000) + self.assertFalse(pktgen_helper._connect()) + server_thread = threading.Thread(target=self._run_fake_server) + server_thread.start() + time.sleep(0.5) + self.assertTrue(pktgen_helper._connect()) + pktgen_helper._sock.close() + server_thread.join() + + @mock.patch('yardstick.network_services.vnf_generic.vnf.tg_vcmts_pktgen.time') + def test_connect(self, *args): + pktgen_helper = tg_vcmts_pktgen.PktgenHelper("localhost", 23000) + pktgen_helper.connected = True + self.assertTrue(pktgen_helper.connect()) + pktgen_helper.connected = False + + pktgen_helper._connect = mock.MagicMock(return_value=True) + self.assertTrue(pktgen_helper.connect()) + self.assertTrue(pktgen_helper.connected) + + pktgen_helper = tg_vcmts_pktgen.PktgenHelper("localhost", 23000) + pktgen_helper._connect = mock.MagicMock(return_value=False) + self.assertFalse(pktgen_helper.connect()) + self.assertFalse(pktgen_helper.connected) + + def test_send_command(self): + pktgen_helper = tg_vcmts_pktgen.PktgenHelper("localhost", 23000) + self.assertFalse(pktgen_helper.send_command("")) + + pktgen_helper.connected = True + pktgen_helper._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.assertFalse(pktgen_helper.send_command("")) + + pktgen_helper._sock = mock.MagicMock() + self.assertTrue(pktgen_helper.send_command("")) + + +class TestVcmtsPktgenSetupEnvHelper(unittest.TestCase): + + PKTGEN_PARAMETERS = "export LUA_PATH=/vcmts/Pktgen.lua;"\ + "export CMK_PROC_FS=/host/proc;"\ + " /pktgen-config/setup.sh 0 4 18:02.0 "\ + "18:02.1 18:02.2 18:02.3 00:00.0 00:00.0 "\ + "00:00.0 00:00.0 imix1_100cms_1ofdm.pcap "\ + "imix1_100cms_1ofdm.pcap imix1_100cms_1ofdm.pcap "\ + "imix1_100cms_1ofdm.pcap imix1_100cms_1ofdm.pcap "\ + "imix1_100cms_1ofdm.pcap imix1_100cms_1ofdm.pcap "\ + "imix1_100cms_1ofdm.pcap" + + OPTIONS = { + "pktgen_values": "/tmp/pktgen_values.yaml", + "tg__0": { + "pktgen_id": 0 + }, + "vcmts_influxdb_ip": "10.80.5.150", + "vcmts_influxdb_port": 8086, + "vcmtsd_values": "/tmp/vcmtsd_values.yaml", + "vnf__0": { + "sg_id": 0, + "stream_dir": "us" + }, + "vnf__1": { + "sg_id": 0, + "stream_dir": "ds" + } + } + + def setUp(self): + vnfd_helper = VnfdHelper( + TestVcmtsPktgen.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + + self.setup_helper = tg_vcmts_pktgen.VcmtsPktgenSetupEnvHelper( + vnfd_helper, ssh_helper, scenario_helper) + + def test_generate_pcap_filename(self): + pcap_file_name = self.setup_helper.generate_pcap_filename(\ + TestVcmtsPktgen.PKTGEN_POD_VALUES[0]['ports'][0]) + self.assertEquals(pcap_file_name, "imix1_100cms_1ofdm.pcap") + + def test_find_port_cfg(self): + port_cfg = self.setup_helper.find_port_cfg(\ + TestVcmtsPktgen.PKTGEN_POD_VALUES[0]['ports'], "port_0") + self.assertIsNotNone(port_cfg) + + port_cfg = self.setup_helper.find_port_cfg(\ + TestVcmtsPktgen.PKTGEN_POD_VALUES[0]['ports'], "port_8") + self.assertIsNone(port_cfg) + + def test_build_pktgen_parameters(self): + parameters = self.setup_helper.build_pktgen_parameters( + TestVcmtsPktgen.PKTGEN_POD_VALUES[0]) + self.assertEquals(parameters, self.PKTGEN_PARAMETERS) + + def test_start_pktgen(self): + self.setup_helper.ssh_helper = mock.MagicMock() + self.setup_helper.start_pktgen(TestVcmtsPktgen.PKTGEN_POD_VALUES[0]) + self.setup_helper.ssh_helper.send_command.assert_called_with( + self.PKTGEN_PARAMETERS) + + def test_setup_vnf_environment(self): + self.assertIsNone(self.setup_helper.setup_vnf_environment()) + +class TestVcmtsPktgen(unittest.TestCase): + + VNFD = {'vnfd:vnfd-catalog': + {'vnfd': + [{ + "benchmark": { + "kpi": [ + "upstream/bits_per_second" + ] + }, + "connection-point": [ + { + "name": "xe0", + "type": "VPORT" + }, + { + "name": "xe1", + "type": "VPORT" + } + ], + "description": "vCMTS Pktgen Kubernetes", + "id": "VcmtsPktgen", + "mgmt-interface": { + "ip": "192.168.24.150", + "key_filename": "/tmp/yardstick_key-a3b663c2", + "user": "root", + "vdu-id": "vcmtspktgen-kubernetes" + }, + "name": "vcmtspktgen", + "short-name": "vcmtspktgen", + "vdu": [ + { + "description": "vCMTS Pktgen Kubernetes", + "external-interface": [], + "id": "vcmtspktgen-kubernetes", + "name": "vcmtspktgen-kubernetes" + } + ], + "vm-flavor": { + "memory-mb": "4096", + "vcpu-count": "4" + } + }] + }} + + PKTGEN_POD_VALUES = [ + { + "num_ports": "4", + "pktgen_id": "0", + "ports": [ + { + "net_pktgen": "18:02.0", + "num_ofdm": "1", + "num_subs": "100", + "port_0": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "18:02.1", + "num_ofdm": "1", + "num_subs": "100", + "port_1": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "18:02.2", + "num_ofdm": "1", + "num_subs": "100", + "port_2": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "18:02.3", + "num_ofdm": "1", + "num_subs": "100", + "port_3": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "00:00.0", + "num_ofdm": "1", + "num_subs": "100", + "port_4": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "00:00.0", + "num_ofdm": "1", + "num_subs": "100", + "port_5": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "00:00.0", + "num_ofdm": "1", + "num_subs": "100", + "port_6": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "00:00.0", + "num_ofdm": "1", + "num_subs": "100", + "port_7": "", + "traffic_type": "imix1" + } + ] + }, + { + "num_ports": 4, + "pktgen_id": 1, + "ports": [ + { + "net_pktgen": "18:0a.0", + "num_ofdm": "1", + "num_subs": "100", + "port_0": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "18:0a.1", + "num_ofdm": "1", + "num_subs": "100", + "port_1": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "18:0a.2", + "num_ofdm": "1", + "num_subs": "100", + "port_2": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "18:0a.3", + "num_ofdm": "1", + "num_subs": "100", + "port_3": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "00:00.0", + "num_ofdm": "1", + "num_subs": "100", + "port_4": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "00:00.0", + "num_ofdm": "1", + "num_subs": "100", + "port_5": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "00:00.0", + "num_ofdm": "1", + "num_subs": "100", + "port_6": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "00:00.0", + "num_ofdm": "1", + "num_subs": "100", + "port_7": "", + "traffic_type": "imix1" + } + ] + } + ] + + SCENARIO_CFG = { + "nodes": { + "tg__0": "pktgen0-k8syardstick-a3b663c2", + "vnf__0": "vnf0us-k8syardstick-a3b663c2", + "vnf__1": "vnf0ds-k8syardstick-a3b663c2" + }, + "options": { + "pktgen_values": "/tmp/pktgen_values.yaml", + "tg__0": { + "pktgen_id": 0 + }, + "vcmts_influxdb_ip": "10.80.5.150", + "vcmts_influxdb_port": 8086, + "vcmtsd_values": "/tmp/vcmtsd_values.yaml", + "vnf__0": { + "sg_id": 0, + "stream_dir": "us" + }, + "vnf__1": { + "sg_id": 0, + "stream_dir": "ds" + } + }, + "task_id": "a3b663c2-e616-4777-b6d0-ec2ea7a06f42", + "task_path": "samples/vnf_samples/nsut/cmts", + "tc": "tc_vcmts_k8s_pktgen", + "topology": "k8s_vcmts_topology.yaml", + "traffic_profile": "../../traffic_profiles/fixed.yaml", + "type": "NSPerf" + } + + CONTEXT_CFG = { + "networks": { + "flannel": { + "name": "flannel" + }, + "xe0": { + "name": "xe0" + }, + "xe1": { + "name": "xe1" + } + }, + "nodes": { + "tg__0": { + "VNF model": "../../vnf_descriptors/tg_vcmts_tpl.yaml", + "interfaces": { + "flannel": { + "local_ip": "192.168.24.150", + "local_mac": None, + "network_name": "flannel" + }, + "xe0": { + "local_ip": "192.168.24.150", + "local_mac": None, + "network_name": "xe0" + }, + "xe1": { + "local_ip": "192.168.24.150", + "local_mac": None, + "network_name": "xe1" + } + }, + "ip": "192.168.24.150", + "key_filename": "/tmp/yardstick_key-a3b663c2", + "member-vnf-index": "1", + "name": "pktgen0-k8syardstick-a3b663c2", + "private_ip": "192.168.24.150", + "service_ports": [ + { + "name": "ssh", + "node_port": 60270, + "port": 22, + "protocol": "TCP", + "target_port": 22 + }, + { + "name": "lua", + "node_port": 43619, + "port": 22022, + "protocol": "TCP", + "target_port": 22022 + } + ], + "ssh_port": 60270, + "user": "root", + "vnfd-id-ref": "tg__0" + }, + "vnf__0": { + "VNF model": "../../vnf_descriptors/vnf_vcmts_tpl.yaml", + "interfaces": { + "flannel": { + "local_ip": "192.168.100.132", + "local_mac": None, + "network_name": "flannel" + }, + "xe0": { + "local_ip": "192.168.100.132", + "local_mac": None, + "network_name": "xe0" + }, + "xe1": { + "local_ip": "192.168.100.132", + "local_mac": None, + "network_name": "xe1" + } + }, + "ip": "192.168.100.132", + "key_filename": "/tmp/yardstick_key-a3b663c2", + "member-vnf-index": "3", + "name": "vnf0us-k8syardstick-a3b663c2", + "private_ip": "192.168.100.132", + "service_ports": [ + { + "name": "ssh", + "node_port": 57057, + "port": 22, + "protocol": "TCP", + "target_port": 22 + }, + { + "name": "lua", + "node_port": 29700, + "port": 22022, + "protocol": "TCP", + "target_port": 22022 + } + ], + "ssh_port": 57057, + "user": "root", + "vnfd-id-ref": "vnf__0" + }, + "vnf__1": { + "VNF model": "../../vnf_descriptors/vnf_vcmts_tpl.yaml", + "interfaces": { + "flannel": { + "local_ip": "192.168.100.134", + "local_mac": None, + "network_name": "flannel" + }, + "xe0": { + "local_ip": "192.168.100.134", + "local_mac": None, + "network_name": "xe0" + }, + "xe1": { + "local_ip": "192.168.100.134", + "local_mac": None, + "network_name": "xe1" + } + }, + "ip": "192.168.100.134", + "key_filename": "/tmp/yardstick_key-a3b663c2", + "member-vnf-index": "4", + "name": "vnf0ds-k8syardstick-a3b663c2", + "private_ip": "192.168.100.134", + "service_ports": [ + { + "name": "ssh", + "node_port": 18581, + "port": 22, + "protocol": "TCP", + "target_port": 22 + }, + { + "name": "lua", + "node_port": 18469, + "port": 22022, + "protocol": "TCP", + "target_port": 22022 + } + ], + "ssh_port": 18581, + "user": "root", + "vnfd-id-ref": "vnf__1" + } + } + } + + PKTGEN_VALUES_PATH = "/tmp/pktgen_values.yaml" + + PKTGEN_VALUES = \ + "serviceAccount: cmk-serviceaccount\n" \ + "images:\n" \ + " vcmts_pktgen: vcmts-pktgen:v18.10\n" \ + "topology:\n" \ + " pktgen_replicas: 8\n" \ + " pktgen_pods:\n" \ + " - pktgen_id: 0\n" \ + " num_ports: 4\n" \ + " ports:\n" \ + " - port_0:\n" \ + " traffic_type: 'imix2'\n" \ + " num_ofdm: 4\n" \ + " num_subs: 300\n" \ + " net_pktgen: 8a:02.0\n" \ + " - port_1:\n" \ + " traffic_type: 'imix2'\n" \ + " num_ofdm: 4\n" \ + " num_subs: 300\n" \ + " net_pktgen: 8a:02.1\n" \ + " - port_2:\n" \ + " traffic_type: 'imix2'\n" \ + " num_ofdm: 4\n" \ + " num_subs: 300\n" \ + " net_pktgen: 8a:02.2\n" \ + " - port_3:\n" \ + " traffic_type: 'imix2'\n" \ + " num_ofdm: 4\n" \ + " num_subs: 300\n" \ + " net_pktgen: 8a:02.3\n" \ + " - port_4:\n" \ + " traffic_type: 'imix2'\n" \ + " num_ofdm: 4\n" \ + " num_subs: 300\n" \ + " net_pktgen: 8a:02.4\n" \ + " - port_5:\n" \ + " traffic_type: 'imix2'\n" \ + " num_ofdm: 4\n" \ + " num_subs: 300\n" \ + " net_pktgen: 8a:02.5\n" \ + " - port_6:\n" \ + " traffic_type: 'imix2'\n" \ + " num_ofdm: 4\n" \ + " num_subs: 300\n" \ + " net_pktgen: 8a:02.6\n" \ + " - port_7:\n" \ + " traffic_type: 'imix2'\n" \ + " num_ofdm: 4\n" \ + " num_subs: 300\n" \ + " net_pktgen: 8a:02.7\n" + + def setUp(self): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + self.vcmts_pktgen = tg_vcmts_pktgen.VcmtsPktgen(NAME, vnfd) + self.vcmts_pktgen._start_server = mock.Mock(return_value=0) + self.vcmts_pktgen.resource_helper = mock.MagicMock() + self.vcmts_pktgen.setup_helper = mock.MagicMock() + + def test___init__(self): + self.assertFalse(self.vcmts_pktgen.traffic_finished) + self.assertIsNotNone(self.vcmts_pktgen.setup_helper) + self.assertIsNotNone(self.vcmts_pktgen.resource_helper) + + def test_extract_pod_cfg(self): + pod_cfg = self.vcmts_pktgen.extract_pod_cfg(self.PKTGEN_POD_VALUES, "0") + self.assertIsNotNone(pod_cfg) + self.assertEqual(pod_cfg["pktgen_id"], "0") + pod_cfg = self.vcmts_pktgen.extract_pod_cfg(self.PKTGEN_POD_VALUES, "4") + self.assertIsNone(pod_cfg) + + @mock.patch.object(ctx_base.Context, 'get_context_from_server', + return_value='fake_context') + def test_instantiate_missing_pktgen_values_key(self, *args): + err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG) + err_scenario_cfg['options'].pop('pktgen_values', None) + with self.assertRaises(KeyError): + self.vcmts_pktgen.instantiate(err_scenario_cfg, self.CONTEXT_CFG) + + @mock.patch.object(ctx_base.Context, 'get_context_from_server', + return_value='fake_context') + def test_instantiate_missing_pktgen_values_file(self, *args): + if os.path.isfile(self.PKTGEN_VALUES_PATH): + os.remove(self.PKTGEN_VALUES_PATH) + err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG) + err_scenario_cfg['options']['pktgen_values'] = self.PKTGEN_VALUES_PATH + with self.assertRaises(RuntimeError): + self.vcmts_pktgen.instantiate(err_scenario_cfg, self.CONTEXT_CFG) + + @mock.patch.object(ctx_base.Context, 'get_context_from_server', + return_value='fake_context') + def test_instantiate_empty_pktgen_values_file(self, *args): + yaml_sample = open(self.PKTGEN_VALUES_PATH, 'w') + yaml_sample.write("") + yaml_sample.close() + + err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG) + err_scenario_cfg['options']['pktgen_values'] = self.PKTGEN_VALUES_PATH + with self.assertRaises(RuntimeError): + self.vcmts_pktgen.instantiate(err_scenario_cfg, self.CONTEXT_CFG) + + if os.path.isfile(self.PKTGEN_VALUES_PATH): + os.remove(self.PKTGEN_VALUES_PATH) + + @mock.patch.object(ctx_base.Context, 'get_context_from_server', + return_value='fake_context') + def test_instantiate_invalid_pktgen_id(self, *args): + yaml_sample = open(self.PKTGEN_VALUES_PATH, 'w') + yaml_sample.write(self.PKTGEN_VALUES) + yaml_sample.close() + + err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG) + err_scenario_cfg['options'][NAME]['pktgen_id'] = 12 + with self.assertRaises(KeyError): + self.vcmts_pktgen.instantiate(err_scenario_cfg, self.CONTEXT_CFG) + + if os.path.isfile(self.PKTGEN_VALUES_PATH): + os.remove(self.PKTGEN_VALUES_PATH) + + @mock.patch.object(ctx_base.Context, 'get_context_from_server', + return_value='fake_context') + def test_instantiate_all_valid(self, *args): + yaml_sample = open(self.PKTGEN_VALUES_PATH, 'w') + yaml_sample.write(self.PKTGEN_VALUES) + yaml_sample.close() + + self.vcmts_pktgen.instantiate(self.SCENARIO_CFG, self.CONTEXT_CFG) + self.assertIsNotNone(self.vcmts_pktgen.pod_cfg) + self.assertEqual(self.vcmts_pktgen.pod_cfg["pktgen_id"], "0") + + if os.path.isfile(self.PKTGEN_VALUES_PATH): + os.remove(self.PKTGEN_VALUES_PATH) + + def test_run_traffic_failed_connect(self): + self.vcmts_pktgen.pktgen_helper = mock.MagicMock() + self.vcmts_pktgen.pktgen_helper.connect.return_value = False + with self.assertRaises(exceptions.PktgenActionError): + self.vcmts_pktgen.run_traffic({}) + + def test_run_traffic_successful_connect(self): + self.vcmts_pktgen.pktgen_helper = mock.MagicMock() + self.vcmts_pktgen.pktgen_helper.connect.return_value = True + self.vcmts_pktgen.pktgen_rate = 8.0 + self.assertTrue(self.vcmts_pktgen.run_traffic({})) + self.vcmts_pktgen.pktgen_helper.connect.assert_called_once() + self.vcmts_pktgen.pktgen_helper.send_command.assert_called_with( + 'pktgen.start("all");') diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vcmts_vnf.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vcmts_vnf.py new file mode 100755 index 000000000..11e3d6e17 --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vcmts_vnf.py @@ -0,0 +1,651 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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. + +import unittest +import mock +import copy +import os + +from yardstick.tests.unit.network_services.vnf_generic.vnf.test_base import mock_ssh +from yardstick.network_services.vnf_generic.vnf.base import VnfdHelper +from yardstick.network_services.vnf_generic.vnf import vcmts_vnf +from yardstick.common import exceptions + +from influxdb.resultset import ResultSet + +NAME = "vnf__0" + + +class TestInfluxDBHelper(unittest.TestCase): + + def test___init__(self): + influxdb_helper = vcmts_vnf.InfluxDBHelper("localhost", 8086) + self.assertEqual(influxdb_helper._vcmts_influxdb_ip, "localhost") + self.assertEqual(influxdb_helper._vcmts_influxdb_port, 8086) + self.assertIsNotNone(influxdb_helper._last_upstream_rx) + self.assertIsNotNone(influxdb_helper._last_values_time) + + def test_start(self): + influxdb_helper = vcmts_vnf.InfluxDBHelper("localhost", 8086) + influxdb_helper.start() + self.assertIsNotNone(influxdb_helper._read_client) + self.assertIsNotNone(influxdb_helper._write_client) + + def test__get_last_value_time(self): + influxdb_helper = vcmts_vnf.InfluxDBHelper("localhost", 8086) + self.assertEqual(influxdb_helper._get_last_value_time('cpu_value'), + vcmts_vnf.InfluxDBHelper.INITIAL_VALUE) + + influxdb_helper._last_values_time['cpu_value'] = "RANDOM" + self.assertEqual(influxdb_helper._get_last_value_time('cpu_value'), + "RANDOM") + + def test__set_last_value_time(self): + influxdb_helper = vcmts_vnf.InfluxDBHelper("localhost", 8086) + influxdb_helper._set_last_value_time('cpu_value', '00:00') + self.assertEqual(influxdb_helper._last_values_time['cpu_value'], + "'00:00'") + + def test__query_measurement(self): + influxdb_helper = vcmts_vnf.InfluxDBHelper("localhost", 8086) + influxdb_helper._read_client = mock.MagicMock() + + resulted_generator = mock.MagicMock() + resulted_generator.keys.return_value = [] + influxdb_helper._read_client.query.return_value = resulted_generator + query_result = influxdb_helper._query_measurement('cpu_value') + self.assertIsNone(query_result) + + resulted_generator = mock.MagicMock() + resulted_generator.keys.return_value = ["", ""] + resulted_generator.get_points.return_value = ResultSet({"":""}) + influxdb_helper._read_client.query.return_value = resulted_generator + query_result = influxdb_helper._query_measurement('cpu_value') + self.assertIsNotNone(query_result) + + def test__rw_measurment(self): + influxdb_helper = vcmts_vnf.InfluxDBHelper("localhost", 8086) + influxdb_helper._query_measurement = mock.MagicMock() + influxdb_helper._query_measurement.return_value = None + influxdb_helper._rw_measurment('cpu_value', []) + self.assertEqual(len(influxdb_helper._last_values_time), 0) + + entry = { + "type":"type", + "host":"host", + "time":"time", + "id": "1", + "value": "1.0" + } + influxdb_helper._query_measurement.return_value = [entry] + influxdb_helper._write_client = mock.MagicMock() + influxdb_helper._rw_measurment('cpu_value', ["id", "value"]) + self.assertEqual(len(influxdb_helper._last_values_time), 1) + influxdb_helper._write_client.write_points.assert_called_once() + + def test_copy_kpi(self): + influxdb_helper = vcmts_vnf.InfluxDBHelper("localhost", 8086) + influxdb_helper._rw_measurment = mock.MagicMock() + influxdb_helper.copy_kpi() + influxdb_helper._rw_measurment.assert_called() + + +class TestVcmtsdSetupEnvHelper(unittest.TestCase): + POD_CFG = { + "cm_crypto": "aes", + "cpu_socket_id": "0", + "ds_core_pool_index": "2", + "ds_core_type": "exclusive", + "net_ds": "1a:02.1", + "net_us": "1a:02.0", + "num_ofdm": "1", + "num_subs": "100", + "power_mgmt": "pm_on", + "qat": "qat_off", + "service_group_config": "", + "sg_id": "0", + "vcmtsd_image": "vcmts-d:perf" + } + + OPTIONS = { + "pktgen_values": "/tmp/pktgen_values.yaml", + "tg__0": { + "pktgen_id": 0 + }, + "vcmts_influxdb_ip": "10.80.5.150", + "vcmts_influxdb_port": 8086, + "vcmtsd_values": "/tmp/vcmtsd_values.yaml", + "vnf__0": { + "sg_id": 0, + "stream_dir": "us" + }, + "vnf__1": { + "sg_id": 0, + "stream_dir": "ds" + } + } + + def setUp(self): + vnfd_helper = VnfdHelper( + TestVcmtsVNF.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + + self.setup_helper = vcmts_vnf.VcmtsdSetupEnvHelper( + vnfd_helper, ssh_helper, scenario_helper) + + def _build_us_parameters(self): + return vcmts_vnf.VcmtsdSetupEnvHelper.BASE_PARAMETERS + " " \ + + " /opt/bin/cmk isolate --conf-dir=/etc/cmk" \ + + " --socket-id=" + str(self.POD_CFG['cpu_socket_id']) \ + + " --pool=shared" \ + + " /vcmts-config/run_upstream.sh " + self.POD_CFG['sg_id'] \ + + " " + self.POD_CFG['ds_core_type'] \ + + " " + str(self.POD_CFG['num_ofdm']) + "ofdm" \ + + " " + str(self.POD_CFG['num_subs']) + "cm" \ + + " " + self.POD_CFG['cm_crypto'] \ + + " " + self.POD_CFG['qat'] \ + + " " + self.POD_CFG['net_us'] \ + + " " + self.POD_CFG['power_mgmt'] + + def test_build_us_parameters(self): + constructed = self._build_us_parameters() + result = self.setup_helper.build_us_parameters(self.POD_CFG) + self.assertEqual(constructed, result) + + def _build_ds_parameters(self): + return vcmts_vnf.VcmtsdSetupEnvHelper.BASE_PARAMETERS + " " \ + + " /opt/bin/cmk isolate --conf-dir=/etc/cmk" \ + + " --socket-id=" + str(self.POD_CFG['cpu_socket_id']) \ + + " --pool=" + self.POD_CFG['ds_core_type'] \ + + " /vcmts-config/run_downstream.sh " + self.POD_CFG['sg_id'] \ + + " " + self.POD_CFG['ds_core_type'] \ + + " " + str(self.POD_CFG['ds_core_pool_index']) \ + + " " + str(self.POD_CFG['num_ofdm']) + "ofdm" \ + + " " + str(self.POD_CFG['num_subs']) + "cm" \ + + " " + self.POD_CFG['cm_crypto'] \ + + " " + self.POD_CFG['qat'] \ + + " " + self.POD_CFG['net_ds'] \ + + " " + self.POD_CFG['power_mgmt'] + + def test_build_ds_parameters(self): + constructed = self._build_ds_parameters() + result = self.setup_helper.build_ds_parameters(self.POD_CFG) + self.assertEqual(constructed, result) + + def test_build_cmd(self): + us_constructed = self._build_us_parameters() + us_result = self.setup_helper.build_cmd('us', self.POD_CFG) + self.assertEqual(us_constructed, us_result) + ds_constructed = self._build_ds_parameters() + ds_result = self.setup_helper.build_cmd('ds', self.POD_CFG) + self.assertEqual(ds_constructed, ds_result) + + def test_run_vcmtsd(self): + us_constructed = self._build_us_parameters() + + vnfd_helper = VnfdHelper( + TestVcmtsVNF.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.MagicMock() + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + + setup_helper = vcmts_vnf.VcmtsdSetupEnvHelper( + vnfd_helper, ssh_helper, scenario_helper) + + setup_helper.run_vcmtsd('us', self.POD_CFG) + ssh_helper.send_command.assert_called_with(us_constructed) + + def test_setup_vnf_environment(self): + self.assertIsNone(self.setup_helper.setup_vnf_environment()) + +class TestVcmtsVNF(unittest.TestCase): + + VNFD = {'vnfd:vnfd-catalog': + {'vnfd': + [{ + "benchmark": { + "kpi": [ + "upstream/bits_per_second" + ] + }, + "connection-point": [ + { + "name": "xe0", + "type": "VPORT" + }, + { + "name": "xe1", + "type": "VPORT" + } + ], + "description": "vCMTS Upstream-Downstream Kubernetes", + "id": "VcmtsVNF", + "mgmt-interface": { + "ip": "192.168.100.35", + "key_filename": "/tmp/yardstick_key-81dcca91", + "user": "root", + "vdu-id": "vcmtsvnf-kubernetes" + }, + "name": "vcmtsvnf", + "short-name": "vcmtsvnf", + "vdu": [ + { + "description": "vCMTS Upstream-Downstream Kubernetes", + "external-interface": [], + "id": "vcmtsvnf-kubernetes", + "name": "vcmtsvnf-kubernetes" + } + ], + "vm-flavor": { + "memory-mb": "4096", + "vcpu-count": "4" + } + }] + } + } + + POD_CFG = [ + { + "cm_crypto": "aes", + "cpu_socket_id": "0", + "ds_core_pool_index": "2", + "ds_core_type": "exclusive", + "net_ds": "1a:02.1", + "net_us": "1a:02.0", + "num_ofdm": "1", + "num_subs": "100", + "power_mgmt": "pm_on", + "qat": "qat_off", + "service_group_config": "", + "sg_id": "0", + "vcmtsd_image": "vcmts-d:perf" + }, + ] + + SCENARIO_CFG = { + "nodes": { + "tg__0": "pktgen0-k8syardstick-afae18b2", + "vnf__0": "vnf0us-k8syardstick-afae18b2", + "vnf__1": "vnf0ds-k8syardstick-afae18b2" + }, + "options": { + "pktgen_values": "/tmp/pktgen_values.yaml", + "tg__0": { + "pktgen_id": 0 + }, + "vcmts_influxdb_ip": "10.80.5.150", + "vcmts_influxdb_port": 8086, + "vcmtsd_values": "/tmp/vcmtsd_values.yaml", + "vnf__0": { + "sg_id": 0, + "stream_dir": "us" + }, + "vnf__1": { + "sg_id": 0, + "stream_dir": "ds" + } + }, + "task_id": "afae18b2-9902-477f-8128-49afde7c3040", + "task_path": "samples/vnf_samples/nsut/cmts", + "tc": "tc_vcmts_k8s_pktgen", + "topology": "k8s_vcmts_topology.yaml", + "traffic_profile": "../../traffic_profiles/fixed.yaml", + "type": "NSPerf" + } + + CONTEXT_CFG = { + "networks": { + "flannel": { + "name": "flannel" + }, + "xe0": { + "name": "xe0" + }, + "xe1": { + "name": "xe1" + } + }, + "nodes": { + "tg__0": { + "VNF model": "../../vnf_descriptors/tg_vcmts_tpl.yaml", + "interfaces": { + "flannel": { + "local_ip": "192.168.24.110", + "local_mac": None, + "network_name": "flannel" + }, + "xe0": { + "local_ip": "192.168.24.110", + "local_mac": None, + "network_name": "xe0" + }, + "xe1": { + "local_ip": "192.168.24.110", + "local_mac": None, + "network_name": "xe1" + } + }, + "ip": "192.168.24.110", + "key_filename": "/tmp/yardstick_key-afae18b2", + "member-vnf-index": "1", + "name": "pktgen0-k8syardstick-afae18b2", + "private_ip": "192.168.24.110", + "service_ports": [ + { + "name": "ssh", + "node_port": 17153, + "port": 22, + "protocol": "TCP", + "target_port": 22 + }, + { + "name": "lua", + "node_port": 51250, + "port": 22022, + "protocol": "TCP", + "target_port": 22022 + } + ], + "ssh_port": 17153, + "user": "root", + "vnfd-id-ref": "tg__0" + }, + "vnf__0": { + "VNF model": "../../vnf_descriptors/vnf_vcmts_tpl.yaml", + "interfaces": { + "flannel": { + "local_ip": "192.168.100.53", + "local_mac": None, + "network_name": "flannel" + }, + "xe0": { + "local_ip": "192.168.100.53", + "local_mac": None, + "network_name": "xe0" + }, + "xe1": { + "local_ip": "192.168.100.53", + "local_mac": None, + "network_name": "xe1" + } + }, + "ip": "192.168.100.53", + "key_filename": "/tmp/yardstick_key-afae18b2", + "member-vnf-index": "3", + "name": "vnf0us-k8syardstick-afae18b2", + "private_ip": "192.168.100.53", + "service_ports": [ + { + "name": "ssh", + "node_port": 34027, + "port": 22, + "protocol": "TCP", + "target_port": 22 + }, + { + "name": "lua", + "node_port": 32580, + "port": 22022, + "protocol": "TCP", + "target_port": 22022 + } + ], + "ssh_port": 34027, + "user": "root", + "vnfd-id-ref": "vnf__0" + }, + "vnf__1": { + "VNF model": "../../vnf_descriptors/vnf_vcmts_tpl.yaml", + "interfaces": { + "flannel": { + "local_ip": "192.168.100.52", + "local_mac": None, + "network_name": "flannel" + }, + "xe0": { + "local_ip": "192.168.100.52", + "local_mac": None, + "network_name": "xe0" + }, + "xe1": { + "local_ip": "192.168.100.52", + "local_mac": None, + "network_name": "xe1" + } + }, + "ip": "192.168.100.52", + "key_filename": "/tmp/yardstick_key-afae18b2", + "member-vnf-index": "4", + "name": "vnf0ds-k8syardstick-afae18b2", + "private_ip": "192.168.100.52", + "service_ports": [ + { + "name": "ssh", + "node_port": 58661, + "port": 22, + "protocol": "TCP", + "target_port": 22 + }, + { + "name": "lua", + "node_port": 58233, + "port": 22022, + "protocol": "TCP", + "target_port": 22022 + } + ], + "ssh_port": 58661, + "user": "root", + "vnfd-id-ref": "vnf__1" + }, + } + } + + VCMTSD_VALUES_PATH = "/tmp/vcmtsd_values.yaml" + + VCMTSD_VALUES = \ + "serviceAccount: cmk-serviceaccount\n" \ + "topology:\n" \ + " vcmts_replicas: 16\n" \ + " vcmts_pods:\n" \ + " - service_group_config:\n" \ + " sg_id: 0\n" \ + " net_us: 18:02.0\n" \ + " net_ds: 18:02.1\n" \ + " num_ofdm: 4\n" \ + " num_subs: 300\n" \ + " cm_crypto: aes\n" \ + " qat: qat_off\n" \ + " power_mgmt: pm_on\n" \ + " cpu_socket_id: 0\n" \ + " ds_core_type: exclusive\n" \ + " ds_core_pool_index: 0\n" \ + " vcmtsd_image: vcmts-d:feat" + + VCMTSD_VALUES_INCOMPLETE = \ + "serviceAccount: cmk-serviceaccount\n" \ + "topology:\n" \ + " vcmts_replicas: 16" + + def setUp(self): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + self.vnf = vcmts_vnf.VcmtsVNF(NAME, vnfd) + + def test___init__(self, *args): + self.assertIsNotNone(self.vnf.setup_helper) + + def test_extract_pod_cfg(self): + pod_cfg = self.vnf.extract_pod_cfg(self.POD_CFG, "0") + self.assertIsNotNone(pod_cfg) + self.assertEqual(pod_cfg['sg_id'], '0') + pod_cfg = self.vnf.extract_pod_cfg(self.POD_CFG, "1") + self.assertIsNone(pod_cfg) + + def test_instantiate_missing_influxdb_info(self): + err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG) + err_scenario_cfg['options'].pop('vcmts_influxdb_ip', None) + with self.assertRaises(KeyError): + self.vnf.instantiate(err_scenario_cfg, self.CONTEXT_CFG) + + def test_instantiate_missing_vcmtsd_values_file(self): + if os.path.isfile(self.VCMTSD_VALUES_PATH): + os.remove(self.VCMTSD_VALUES_PATH) + err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG) + err_scenario_cfg['options']['vcmtsd_values'] = self.VCMTSD_VALUES_PATH + with self.assertRaises(RuntimeError): + self.vnf.instantiate(err_scenario_cfg, self.CONTEXT_CFG) + + def test_instantiate_empty_vcmtsd_values_file(self): + yaml_sample = open(self.VCMTSD_VALUES_PATH, 'w') + yaml_sample.write("") + yaml_sample.close() + + err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG) + err_scenario_cfg['options']['vcmtsd_values'] = self.VCMTSD_VALUES_PATH + with self.assertRaises(RuntimeError): + self.vnf.instantiate(err_scenario_cfg, self.CONTEXT_CFG) + + if os.path.isfile(self.VCMTSD_VALUES_PATH): + os.remove(self.VCMTSD_VALUES_PATH) + + def test_instantiate_missing_vcmtsd_values_key(self): + err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG) + err_scenario_cfg['options'].pop('vcmtsd_values', None) + with self.assertRaises(KeyError): + self.vnf.instantiate(err_scenario_cfg, self.CONTEXT_CFG) + + def test_instantiate_invalid_vcmtsd_values(self): + yaml_sample = open(self.VCMTSD_VALUES_PATH, 'w') + yaml_sample.write(self.VCMTSD_VALUES_INCOMPLETE) + yaml_sample.close() + + err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG) + with self.assertRaises(KeyError): + self.vnf.instantiate(err_scenario_cfg, self.CONTEXT_CFG) + + if os.path.isfile(self.VCMTSD_VALUES_PATH): + os.remove(self.VCMTSD_VALUES_PATH) + + def test_instantiate_invalid_sg_id(self): + yaml_sample = open(self.VCMTSD_VALUES_PATH, 'w') + yaml_sample.write(self.VCMTSD_VALUES) + yaml_sample.close() + + err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG) + err_scenario_cfg['options'][NAME]['sg_id'] = 8 + with self.assertRaises(exceptions.IncorrectConfig): + self.vnf.instantiate(err_scenario_cfg, self.CONTEXT_CFG) + + if os.path.isfile(self.VCMTSD_VALUES_PATH): + os.remove(self.VCMTSD_VALUES_PATH) + + @mock.patch('yardstick.network_services.vnf_generic.vnf.vcmts_vnf.VnfSshHelper') + def test_instantiate_all_valid(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vnf = vcmts_vnf.VcmtsVNF(NAME, vnfd) + + yaml_sample = open(self.VCMTSD_VALUES_PATH, 'w') + yaml_sample.write(self.VCMTSD_VALUES) + yaml_sample.close() + + vnf.instantiate(self.SCENARIO_CFG, self.CONTEXT_CFG) + self.assertEqual(vnf.vcmts_influxdb_ip, "10.80.5.150") + self.assertEqual(vnf.vcmts_influxdb_port, 8086) + + if os.path.isfile(self.VCMTSD_VALUES_PATH): + os.remove(self.VCMTSD_VALUES_PATH) + + def test__update_collectd_options(self): + scenario_cfg = {'options': + {'collectd': + {'interval': 3, + 'plugins': + {'plugin3': {'param': 3}}}, + 'vnf__0': + {'collectd': + {'interval': 2, + 'plugins': + {'plugin3': {'param': 2}, + 'plugin2': {'param': 2}}}}}} + context_cfg = {'nodes': + {'vnf__0': + {'collectd': + {'interval': 1, + 'plugins': + {'plugin3': {'param': 1}, + 'plugin2': {'param': 1}, + 'plugin1': {'param': 1}}}}}} + expected = {'interval': 1, + 'plugins': + {'plugin3': {'param': 1}, + 'plugin2': {'param': 1}, + 'plugin1': {'param': 1}}} + + self.vnf._update_collectd_options(scenario_cfg, context_cfg) + self.assertEqual(self.vnf.setup_helper.collectd_options, expected) + + def test__update_options(self): + options1 = {'interval': 1, + 'param1': 'value1', + 'plugins': + {'plugin3': {'param': 3}, + 'plugin2': {'param': 1}, + 'plugin1': {'param': 1}}} + options2 = {'interval': 2, + 'param2': 'value2', + 'plugins': + {'plugin4': {'param': 4}, + 'plugin2': {'param': 2}, + 'plugin1': {'param': 2}}} + expected = {'interval': 1, + 'param1': 'value1', + 'param2': 'value2', + 'plugins': + {'plugin4': {'param': 4}, + 'plugin3': {'param': 3}, + 'plugin2': {'param': 1}, + 'plugin1': {'param': 1}}} + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vnf = vcmts_vnf.VcmtsVNF('vnf1', vnfd) + vnf._update_options(options2, options1) + self.assertEqual(options2, expected) + + def test_wait_for_instantiate(self): + self.assertIsNone(self.vnf.wait_for_instantiate()) + + def test_terminate(self): + self.assertIsNone(self.vnf.terminate()) + + def test_scale(self): + self.assertIsNone(self.vnf.scale()) + + def test_collect_kpi(self): + self.vnf.influxdb_helper = mock.MagicMock() + self.vnf.collect_kpi() + self.vnf.influxdb_helper.copy_kpi.assert_called_once() + + def test_start_collect(self): + self.vnf.vcmts_influxdb_ip = "localhost" + self.vnf.vcmts_influxdb_port = 8800 + + self.assertIsNone(self.vnf.start_collect()) + self.assertIsNotNone(self.vnf.influxdb_helper) + + def test_stop_collect(self): + self.assertIsNone(self.vnf.stop_collect()) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vpp_helpers.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vpp_helpers.py index 597844c72..cca604f43 100644 --- a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vpp_helpers.py +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vpp_helpers.py @@ -11,13 +11,199 @@ # 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. +import ipaddress import unittest import mock +from yardstick.common import exceptions +from yardstick.network_services.helpers import cpu +from yardstick.network_services.vnf_generic.vnf import vpp_helpers from yardstick.network_services.vnf_generic.vnf.base import VnfdHelper from yardstick.network_services.vnf_generic.vnf.vpp_helpers import \ - VppSetupEnvHelper + VppSetupEnvHelper, VppConfigGenerator, VatTerminal + + +class TestVppConfigGenerator(unittest.TestCase): + + def test_add_config_item(self): + test_item = {} + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_config_item(test_item, '/tmp/vpe.log', + ['unix', 'log']) + self.assertEqual({'unix': {'log': '/tmp/vpe.log'}}, test_item) + + def test_add_config_item_str(self): + test_item = {'unix': ''} + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_config_item(test_item, '/tmp/vpe.log', + ['unix', 'log']) + self.assertEqual({'unix': {'log': '/tmp/vpe.log'}}, test_item) + + def test_add_unix_log(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_unix_log() + self.assertEqual('unix\n{\n log /tmp/vpe.log\n}\n', + vpp_config_generator.dump_config()) + + def test_add_unix_cli_listen(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_unix_cli_listen() + self.assertEqual('unix\n{\n cli-listen /run/vpp/cli.sock\n}\n', + vpp_config_generator.dump_config()) + + def test_add_unix_nodaemon(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_unix_nodaemon() + self.assertEqual('unix\n{\n nodaemon \n}\n', + vpp_config_generator.dump_config()) + + def test_add_unix_coredump(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_unix_coredump() + self.assertEqual('unix\n{\n full-coredump \n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_dev(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_dev('0000:00:00.0') + self.assertEqual('dpdk\n{\n dev 0000:00:00.0 \n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_cryptodev(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_cryptodev(2, '0000:00:00.0') + self.assertEqual( + 'dpdk\n{\n dev 0000:00:01.0 \n dev 0000:00:01.1 \n uio-driver igb_uio\n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_sw_cryptodev(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_sw_cryptodev('aesni_gcm', 0, 2) + self.assertEqual( + 'dpdk\n{\n vdev cryptodev_aesni_gcm_pmd,socket_id=0 \n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_dev_default_rxq(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_dev_default_rxq(1) + self.assertEqual( + 'dpdk\n{\n dev default\n {\n num-rx-queues 1\n }\n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_dev_default_rxd(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_dev_default_rxd(2048) + self.assertEqual( + 'dpdk\n{\n dev default\n {\n num-rx-desc 2048\n }\n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_dev_default_txd(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_dev_default_txd(2048) + self.assertEqual( + 'dpdk\n{\n dev default\n {\n num-tx-desc 2048\n }\n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_log_level(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_log_level('debug') + self.assertEqual('dpdk\n{\n log-level debug\n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_socketmem(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_socketmem('1024,1024') + self.assertEqual('dpdk\n{\n socket-mem 1024,1024\n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_num_mbufs(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_num_mbufs(32768) + self.assertEqual('dpdk\n{\n num-mbufs 32768\n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_uio_driver(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_uio_driver('igb_uio') + self.assertEqual('dpdk\n{\n uio-driver igb_uio\n}\n', + vpp_config_generator.dump_config()) + + def test_add_cpu_main_core(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_cpu_main_core('1,2') + self.assertEqual('cpu\n{\n main-core 1,2\n}\n', + vpp_config_generator.dump_config()) + + def test_add_cpu_corelist_workers(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_cpu_corelist_workers('1,2') + self.assertEqual('cpu\n{\n corelist-workers 1,2\n}\n', + vpp_config_generator.dump_config()) + + def test_add_heapsize(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_heapsize('4G') + self.assertEqual('heapsize 4G\n', vpp_config_generator.dump_config()) + + def test_add_ip6_hash_buckets(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_ip6_hash_buckets(2000000) + self.assertEqual('ip6\n{\n hash-buckets 2000000\n}\n', + vpp_config_generator.dump_config()) + + def test_add_ip6_heap_size(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_ip6_heap_size('4G') + self.assertEqual('ip6\n{\n heap-size 4G\n}\n', + vpp_config_generator.dump_config()) + + def test_add_ip_heap_size(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_ip_heap_size('4G') + self.assertEqual('ip\n{\n heap-size 4G\n}\n', + vpp_config_generator.dump_config()) + + def test_add_statseg_size(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_statseg_size('4G') + self.assertEqual('statseg\n{\n size 4G\n}\n', + vpp_config_generator.dump_config()) + + def test_add_plugin(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_plugin('enable', ['dpdk_plugin.so']) + self.assertEqual( + 'plugins\n{\n plugin [\'dpdk_plugin.so\']\n {\n enable \n }\n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_no_multi_seg(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_no_multi_seg() + self.assertEqual('dpdk\n{\n no-multi-seg \n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_no_tx_checksum_offload(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_no_tx_checksum_offload() + self.assertEqual('dpdk\n{\n no-tx-checksum-offload \n}\n', + vpp_config_generator.dump_config()) + + def test_dump_config(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_unix_log() + self.assertEqual('unix\n{\n log /tmp/vpe.log\n}\n', + vpp_config_generator.dump_config()) + + def test_pci_dev_check(self): + self.assertTrue(VppConfigGenerator.pci_dev_check('0000:00:00.0')) + + def test_pci_dev_check_error(self): + with self.assertRaises(ValueError) as raised: + VppConfigGenerator.pci_dev_check('0000:00:0.0') + self.assertIn( + 'PCI address 0000:00:0.0 is not in valid format xxxx:xx:xx.x', + str(raised.exception)) class TestVppSetupEnvHelper(unittest.TestCase): @@ -130,6 +316,224 @@ class TestVppSetupEnvHelper(unittest.TestCase): ] } + VNFD_1 = { + "benchmark": { + "kpi": [ + "packets_in", + "packets_fwd", + "packets_dropped" + ] + }, + "connection-point": [ + { + "name": "xe0", + "type": "VPORT" + }, + { + "name": "xe1", + "type": "VPORT" + } + ], + "description": "VPP IPsec", + "id": "VipsecApproxVnf", + "mgmt-interface": { + "ip": "10.10.10.101", + "password": "r00t", + "user": "root", + "vdu-id": "ipsecvnf-baremetal" + }, + "name": "IpsecVnf", + "short-name": "IpsecVnf", + "vdu": [ + { + "description": "VPP Ipsec", + "external-interface": [ + { + "name": "xe0", + "virtual-interface": { + "driver": "igb_uio", + "dst_ip": "192.168.100.1", + "dst_mac": "90:e2:ba:7c:30:e8", + "ifname": "xe0", + "local_ip": "192.168.100.2", + "local_mac": "90:e2:ba:7c:41:a8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe0", + "peer_intf": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.100.2", + "dst_mac": "90:e2:ba:7c:41:a8", + "ifname": "xe0", + "local_ip": "192.168.100.1", + "local_mac": "90:e2:ba:7c:30:e8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "tg__0", + "peer_ifname": "xe0", + "peer_name": "vnf__0", + "vld_id": "uplink_0", + "vpci": "0000:81:00.0" + }, + "peer_name": "tg__0", + "vld_id": "uplink_0", + "vpci": "0000:ff:06.0" + }, + "vnfd-connection-point-ref": "xe0" + }, + { + "name": "xe1", + "virtual-interface": { + "driver": "igb_uio", + "dst_ip": "1.1.1.2", + "dst_mac": "0a:b1:ec:fd:a2:66", + "ifname": "xe1", + "local_ip": "1.1.1.1", + "local_mac": "4e:90:85:d3:c5:13", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe1", + "peer_intf": { + "driver": "igb_uio", + "dst_ip": "1.1.1.1", + "dst_mac": "4e:90:85:d3:c5:13", + "ifname": "xe1", + "local_ip": "1.1.1.2", + "local_mac": "0a:b1:ec:fd:a2:66", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__1", + "peer_ifname": "xe1", + "peer_name": "vnf__0", + "vld_id": "ciphertext", + "vpci": "0000:00:07.0" + }, + "peer_name": "vnf__1", + "vld_id": "ciphertext", + "vpci": "0000:ff:07.0" + }, + "vnfd-connection-point-ref": "xe1" + } + ], + "id": "ipsecvnf-baremetal", + "name": "ipsecvnf-baremetal", + "routing_table": [] + } + ] + } + + VNFD_2 = { + "benchmark": { + "kpi": [ + "packets_in", + "packets_fwd", + "packets_dropped" + ] + }, + "connection-point": [ + { + "name": "xe0", + "type": "VPORT" + }, + { + "name": "xe1", + "type": "VPORT" + } + ], + "description": "VPP IPsec", + "id": "VipsecApproxVnf", + "mgmt-interface": { + "ip": "10.10.10.101", + "password": "r00t", + "user": "root", + "vdu-id": "ipsecvnf-baremetal" + }, + "name": "IpsecVnf", + "short-name": "IpsecVnf", + "vdu": [ + { + "description": "VPP Ipsec", + "external-interface": [ + { + "name": "xe0", + "virtual-interface": { + "driver": "igb_uio", + "dst_ip": "192.168.100.1", + "dst_mac": "90:e2:ba:7c:30:e8", + "ifname": "xe0", + "local_ip": "192.168.100.2", + "local_mac": "90:e2:ba:7c:41:a8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe0", + "peer_intf": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.100.2", + "dst_mac": "90:e2:ba:7c:41:a8", + "ifname": "xe0", + "local_ip": "192.168.100.1", + "local_mac": "90:e2:ba:7c:30:e8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "tg__0", + "peer_ifname": "xe0", + "peer_name": "vnf__0", + "vld_id": "uplink_0", + "vpci": "0000:81:00.0" + }, + "peer_name": "tg__0", + "vld_id": "uplink_0", + "vpci": "0000:ff:06.0" + }, + "vnfd-connection-point-ref": "xe0" + }, + { + "name": "xe1", + "virtual-interface": { + "driver": "igb_uio", + "dst_ip": "1.1.1.2", + "dst_mac": "0a:b1:ec:fd:a2:66", + "ifname": "xe1", + "local_ip": "1.1.1.1", + "local_mac": "4e:90:85:d3:c5:13", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe1", + "peer_intf": { + "driver": "igb_uio", + "dst_ip": "1.1.1.1", + "dst_mac": "4e:90:85:d3:c5:13", + "ifname": "xe1", + "local_ip": "1.1.1.2", + "local_mac": "0a:b1:ec:fd:a2:66", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__1", + "peer_ifname": "xe1", + "peer_name": "vnf__0", + "vld_id": "ciphertext", + "vpci": "0000:00:07.0" + }, + "peer_name": "vnf__1", + "vld_id": "ciphertext", + "vpci": "0000:ff:07.0" + }, + "vnfd-connection-point-ref": "xe1" + } + ], + "id": "ipsecvnf-baremetal", + "name": "ipsecvnf-baremetal", + "routing_table": [] + } + ] + } + VNFD = { 'vnfd:vnfd-catalog': { 'vnfd': [ @@ -216,6 +620,105 @@ class TestVppSetupEnvHelper(unittest.TestCase): } ] + VPP_INTERFACES_DUMP_MAC_ERR = [ + { + "sw_if_index": 0, + "sup_sw_if_index": 0, + "l2_address_length": 0, + "l2_address": [0, 0, 0, 0, 0, 0, 0, 0], + "interface_name": "local0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 0, + "link_speed": 0, + "mtu": 0, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + }, + { + "sw_if_index": 1, + "sup_sw_if_index": 1, + "l2_address_length": 6, + "l2_address": [144, 226, 186, 124, 65, 169, 0, 0], + "interface_name": "TenGigabitEthernetff/6/0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 2, + "link_speed": 32, + "mtu": 9202, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + }, + { + "sw_if_index": 2, + "sup_sw_if_index": 2, + "l2_address_length": 6, + "l2_address": [78, 144, 133, 211, 197, 20, 0, 0], + "interface_name": "VirtualFunctionEthernetff/7/0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 2, + "link_speed": 32, + "mtu": 9206, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + } + ] + + CPU_LAYOUT = {'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 1, 1, 0]]} + CPU_SMT = {'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 1, 1, 0], + [2, 1, 0, 0, 0, 2, 2, 1], + [3, 1, 0, 0, 0, 3, 3, 1], + [4, 2, 0, 0, 0, 4, 4, 2], + [5, 2, 0, 0, 0, 5, 5, 2], + [6, 3, 0, 0, 0, 6, 6, 3], + [7, 3, 0, 0, 0, 7, 7, 3], + [8, 4, 0, 0, 0, 8, 8, 4], + [9, 5, 0, 1, 0, 0, 0, 0], + [10, 6, 0, 1, 0, 1, 1, 0], + [11, 6, 0, 1, 0, 2, 2, 1], + [12, 7, 0, 1, 0, 3, 3, 1], + [13, 7, 0, 1, 0, 4, 4, 2], + [14, 8, 0, 1, 0, 5, 5, 2], + [15, 8, 0, 1, 0, 6, 6, 3], + [16, 9, 0, 1, 0, 7, 7, 3], + [17, 9, 0, 1, 0, 8, 8, 4]]} + def test_kill_vnf(self): vnfd_helper = VnfdHelper(self.VNFD_0) ssh_helper = mock.Mock() @@ -333,3 +836,888 @@ class TestVppSetupEnvHelper(unittest.TestCase): self.assertIsNone(vpp_setup_env_helper.get_value_by_interface_key( 'xe2', 'vpp-err')) + + def test_crypto_device_init(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper.dpdk_bind_helper.load_dpdk_driver = mock.Mock() + vpp_setup_env_helper.dpdk_bind_helper.bind = mock.Mock() + + vpp_setup_env_helper.kill_vnf = mock.Mock() + vpp_setup_env_helper.pci_driver_unbind = mock.Mock() + + with mock.patch.object(vpp_setup_env_helper, 'get_pci_dev_driver') as \ + mock_get_pci_dev_driver, \ + mock.patch.object(vpp_setup_env_helper, 'set_sriov_numvfs') as \ + mock_set_sriov_numvfs: + mock_get_pci_dev_driver.return_value = 'igb_uio' + self.assertIsNone( + vpp_setup_env_helper.crypto_device_init('0000:ff:06.0', 32)) + mock_set_sriov_numvfs.assert_called() + + def test_get_sriov_numvfs(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '32', '' + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + self.assertEqual(32, + vpp_setup_env_helper.get_sriov_numvfs('0000:ff:06.0')) + + def test_get_sriov_numvfs_error(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, 'err', '' + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + self.assertEqual(0, + vpp_setup_env_helper.get_sriov_numvfs('0000:ff:06.0')) + + def test_set_sriov_numvfs(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper.set_sriov_numvfs('0000:ff:06.0') + self.assertEqual(ssh_helper.execute.call_count, 1) + + def test_pci_driver_unbind(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper.pci_driver_unbind('0000:ff:06.0') + self.assertEqual(ssh_helper.execute.call_count, 1) + + def test_get_pci_dev_driver(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = \ + 0, 'Slot: ff:07.0\n' \ + 'Class: Ethernet controller\n' \ + 'Vendor: Intel Corporation\n' \ + 'Device: 82599 Ethernet Controller Virtual Function\n' \ + 'SVendor: Intel Corporation\n' \ + 'SDevice: 82599 Ethernet Controller Virtual Function\n' \ + 'Rev: 01\n' \ + 'Driver: igb_uio\n' \ + 'Module: ixgbevf', '' + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + self.assertEqual('igb_uio', vpp_setup_env_helper.get_pci_dev_driver( + '0000:ff:06.0')) + + def test_get_pci_dev_driver_error(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 1, 'err', '' + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + with self.assertRaises(RuntimeError) as raised: + vpp_setup_env_helper.get_pci_dev_driver( + '0000:ff:06.0') + + self.assertIn("'lspci -vmmks 0000:ff:06.0' failed", + str(raised.exception)) + + def test_get_pci_dev_driver_output_error(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = \ + 0, 'Slot: ff:07.0\n' \ + '\n\t' \ + 'Vendor: Intel Corporation\n' \ + 'Device: 82599 Ethernet Controller Virtual Function\n' \ + 'SVendor: Intel Corporation\n' \ + 'SDevice: 82599 Ethernet Controller Virtual Function\n' \ + 'Rev: 01\n' \ + 'Driver_err: igb_uio\n' \ + 'Module: ixgbevf', '' + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + self.assertIsNone( + vpp_setup_env_helper.get_pci_dev_driver('0000:ff:06.0')) + + def test_vpp_create_ipsec_tunnels(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '', '' + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + self.assertIsNone( + vpp_setup_env_helper.vpp_create_ipsec_tunnels('10.10.10.2', + '10.10.10.1', 'xe0', + 1, 1, mock.Mock(), + 'crypto_key', + mock.Mock(), + 'integ_key', + '20.20.20.0')) + self.assertGreaterEqual(ssh_helper.execute.call_count, 2) + + def test_vpp_create_ipsec_1000_tunnels(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '', '' + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + self.assertIsNone( + vpp_setup_env_helper.vpp_create_ipsec_tunnels('10.10.10.2', + '10.10.10.1', 'xe0', + 1000, 128000, + mock.Mock(), + 'crypto_key', + mock.Mock(), + 'integ_key', + '20.20.20.0')) + self.assertGreaterEqual(ssh_helper.execute.call_count, 2) + + def test_apply_config(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '', '' + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + self.assertIsNone(vpp_setup_env_helper.apply_config(mock.Mock())) + self.assertGreaterEqual(ssh_helper.execute.call_count, 2) + + def test_apply_config_error(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 1, '', '' + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + with self.assertRaises(RuntimeError) as raised: + vpp_setup_env_helper.apply_config(mock.Mock()) + + self.assertIn('Writing config file failed', str(raised.exception)) + + def test_vpp_route_add(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = '' + self.assertIsNone( + vpp_setup_env_helper.vpp_route_add('xe0', '10.10.10.1', 24)) + + def test_vpp_route_add_without_index(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = '' + self.assertIsNone( + vpp_setup_env_helper.vpp_route_add('xe0', '10.10.10.1', 24, + interface='xe0', + use_sw_index=False)) + + def test_add_arp_on_dut(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = '' + self.assertEqual('', vpp_setup_env_helper.add_arp_on_dut('xe0', + '10.10.10.1', + '00:00:00:00:00:00')) + + def test_set_ip(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = '' + self.assertEqual('', + vpp_setup_env_helper.set_ip('xe0', '10.10.10.1', + 24)) + + def test_set_interface_state(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = '' + self.assertEqual('', + vpp_setup_env_helper.set_interface_state('xe0', + 'up')) + + def test_set_interface_state_error(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = '' + with self.assertRaises(ValueError) as raised: + vpp_setup_env_helper.set_interface_state('xe0', 'error') + self.assertIn('Unexpected interface state: error', + str(raised.exception)) + + def test_set_interface_down_state(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = '' + self.assertEqual('', + vpp_setup_env_helper.set_interface_state('xe0', + 'down')) + + def test_vpp_set_interface_mtu(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = '' + self.assertIsNone( + vpp_setup_env_helper.vpp_set_interface_mtu('xe0', 9200)) + + def test_vpp_interfaces_ready_wait(self): + json_output = [self.VPP_INTERFACES_DUMP] + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = json_output + self.assertIsNone(vpp_setup_env_helper.vpp_interfaces_ready_wait()) + + def test_vpp_interfaces_ready_wait_timeout(self): + json_output = [[ + { + "sw_if_index": 0, + "sup_sw_if_index": 0, + "l2_address_length": 0, + "l2_address": [0, 0, 0, 0, 0, 0, 0, 0], + "interface_name": "xe0", + "admin_up_down": 1, + "link_up_down": 0, + "link_duplex": 0, + "link_speed": 0, + "mtu": 0, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + }]] + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = json_output + with self.assertRaises(RuntimeError) as raised: + vpp_setup_env_helper.vpp_interfaces_ready_wait(5) + self.assertIn('timeout, not up [\'xe0\']', str(raised.exception)) + + def test_vpp_get_interface_data(self): + json_output = [self.VPP_INTERFACES_DUMP] + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = json_output + self.assertEqual(json_output[0], + vpp_setup_env_helper.vpp_get_interface_data()) + + def test_vpp_get_interface_data_ifname(self): + json_output = [self.VPP_INTERFACES_DUMP] + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = json_output + self.assertEqual(json_output[0][2], + vpp_setup_env_helper.vpp_get_interface_data( + 'VirtualFunctionEthernetff/7/0')) + + def test_vpp_get_interface_data_ifname_error(self): + json_output = [self.VPP_INTERFACES_DUMP] + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = json_output + self.assertEqual({}, vpp_setup_env_helper.vpp_get_interface_data( + 'error')) + + def test_vpp_get_interface_data_ifindex(self): + json_output = [self.VPP_INTERFACES_DUMP] + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = json_output + self.assertEqual(json_output[0][1], + vpp_setup_env_helper.vpp_get_interface_data(1)) + + def test_vpp_get_interface_data_error(self): + json_output = [self.VPP_INTERFACES_DUMP] + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = json_output + with self.assertRaises(TypeError) as raised: + vpp_setup_env_helper.vpp_get_interface_data(1.0) + self.assertEqual('', str(raised.exception)) + + def test_update_vpp_interface_data(self): + output = '{}\n{}'.format(self.VPP_INTERFACES_DUMP, + 'dump_interface_table:6019: JSON output ' \ + 'supported only for VPE API calls and dump_stats_table\n' \ + '/opt/nsb_bin/vpp/templates/dump_interfaces.vat(2): \n' \ + 'dump_interface_table error: Misc') + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, output.replace("\'", "\""), '' + ssh_helper.join_bin_path.return_value = '/opt/nsb_bin/vpp/templates' + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + self.assertIsNone(vpp_setup_env_helper.update_vpp_interface_data()) + self.assertGreaterEqual(ssh_helper.execute.call_count, 1) + self.assertEqual('TenGigabitEthernetff/6/0', + vpp_setup_env_helper.get_value_by_interface_key( + 'xe0', 'vpp_name')) + self.assertEqual(1, vpp_setup_env_helper.get_value_by_interface_key( + 'xe0', 'vpp_sw_index')) + self.assertEqual('VirtualFunctionEthernetff/7/0', + vpp_setup_env_helper.get_value_by_interface_key( + 'xe1', 'vpp_name')) + self.assertEqual(2, vpp_setup_env_helper.get_value_by_interface_key( + 'xe1', 'vpp_sw_index')) + + def test_update_vpp_interface_data_error(self): + output = '{}\n{}'.format(self.VPP_INTERFACES_DUMP_MAC_ERR, + 'dump_interface_table:6019: JSON output ' \ + 'supported only for VPE API calls and dump_stats_table\n' \ + '/opt/nsb_bin/vpp/templates/dump_interfaces.vat(2): \n' \ + 'dump_interface_table error: Misc') + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, output.replace("\'", "\""), '' + ssh_helper.join_bin_path.return_value = '/opt/nsb_bin/vpp/templates' + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + self.assertIsNone(vpp_setup_env_helper.update_vpp_interface_data()) + self.assertGreaterEqual(ssh_helper.execute.call_count, 1) + + def test_iface_update_numa(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + self.assertIsNone(vpp_setup_env_helper.iface_update_numa()) + self.assertGreaterEqual(ssh_helper.execute.call_count, 2) + self.assertEqual(0, vpp_setup_env_helper.get_value_by_interface_key( + 'xe0', 'numa_node')) + self.assertEqual(0, vpp_setup_env_helper.get_value_by_interface_key( + 'xe1', 'numa_node')) + + def test_iface_update_numa_error(self): + vnfd_helper = VnfdHelper(self.VNFD_1) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '-1', '' + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + sys_cores = cpu.CpuSysCores(ssh_helper) + vpp_setup_env_helper._update_vnfd_helper( + sys_cores.get_cpu_layout()) + self.assertIsNone(vpp_setup_env_helper.iface_update_numa()) + self.assertGreaterEqual(ssh_helper.execute.call_count, 2) + self.assertEqual(0, vpp_setup_env_helper.get_value_by_interface_key( + 'xe0', 'numa_node')) + self.assertEqual(0, vpp_setup_env_helper.get_value_by_interface_key( + 'xe1', 'numa_node')) + + def test_iface_update_without_numa(self): + vnfd_helper = VnfdHelper(self.VNFD_2) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '-1', '' + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout: + mock_get_cpu_layout.return_value = self.CPU_SMT + sys_cores = cpu.CpuSysCores(ssh_helper) + vpp_setup_env_helper._update_vnfd_helper( + sys_cores.get_cpu_layout()) + self.assertIsNone(vpp_setup_env_helper.iface_update_numa()) + self.assertGreaterEqual(ssh_helper.execute.call_count, 2) + self.assertIsNone(vpp_setup_env_helper.get_value_by_interface_key( + 'xe0', 'numa_node')) + self.assertIsNone(vpp_setup_env_helper.get_value_by_interface_key( + 'xe1', 'numa_node')) + + def test_execute_script(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper.execute_script('dump_interfaces.vat', True, True) + self.assertGreaterEqual(ssh_helper.put_file.call_count, 1) + self.assertGreaterEqual(ssh_helper.execute.call_count, 1) + + def test_execute_script_error(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.side_effect = Exception + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + with self.assertRaises(Exception) as raised: + vpp_setup_env_helper.execute_script('dump_interfaces.vat', True, + True) + self.assertIn( + 'VAT script execution failed: vpp_api_test json in dump_interfaces.vat script', + str(raised.exception)) + self.assertGreaterEqual(ssh_helper.put_file.call_count, 1) + + def test_execute_script_json_out(self): + json_output = [ + { + "sw_if_index": 0, + "sup_sw_if_index": 0 + }, + { + "l2_address_length": 6, + "l2_address": [144, 226, 186, 124, 65, 168, 0, 0] + }, + { + "interface_name": "VirtualFunctionEthernetff/7/0", + "admin_up_down": 0 + } + ] + output = '{}\n{}'.format(json_output, + 'dump_interface_table:6019: JSON output ' \ + 'supported only for VPE API calls and dump_stats_table\n' \ + '/opt/nsb_bin/vpp/templates/dump_interfaces.vat(2): \n' \ + 'dump_interface_table error: Misc') + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, output, '' + ssh_helper.join_bin_path.return_value = '/opt/nsb_bin/vpp/templates' + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + self.assertEqual(str(json_output), + vpp_setup_env_helper.execute_script_json_out( + 'dump_interfaces.vat')) + + def test_self_cleanup_vat_json_output(self): + json_output = [ + { + "sw_if_index": 0, + "sup_sw_if_index": 0 + }, + { + "l2_address_length": 6, + "l2_address": [144, 226, 186, 124, 65, 168, 0, 0] + }, + { + "interface_name": "VirtualFunctionEthernetff/7/0", + "admin_up_down": 0 + } + ] + + output = '{}\n{}'.format(json_output, + 'dump_interface_table:6019: JSON output ' \ + 'supported only for VPE API calls and dump_stats_table\n' \ + '/opt/nsb_bin/vpp/templates/dump_interfaces.vat(2): \n' \ + 'dump_interface_table error: Misc') + self.assertEqual(str(json_output), + VppSetupEnvHelper.cleanup_vat_json_output(output, + '/opt/nsb_bin/vpp/templates/dump_interfaces.vat')) + + def test__convert_mac_to_number_list(self): + self.assertEqual([144, 226, 186, 124, 65, 168], + VppSetupEnvHelper._convert_mac_to_number_list( + '90:e2:ba:7c:41:a8')) + + def test_get_vpp_interface_by_mac(self): + mac_address = '90:e2:ba:7c:41:a8' + self.assertEqual({'admin_up_down': 0, + 'interface_name': 'TenGigabitEthernetff/6/0', + 'l2_address': [144, 226, 186, 124, 65, 168, 0, 0], + 'l2_address_length': 6, + 'link_duplex': 2, + 'link_speed': 32, + 'link_up_down': 0, + 'mtu': 9202, + 'sub_default': 0, + 'sub_dot1ad': 0, + 'sub_exact_match': 0, + 'sub_id': 0, + 'sub_inner_vlan_id': 0, + 'sub_inner_vlan_id_any': 0, + 'sub_number_of_tags': 0, + 'sub_outer_vlan_id': 0, + 'sub_outer_vlan_id_any': 0, + 'sup_sw_if_index': 1, + 'sw_if_index': 1, + 'vtr_op': 0, + 'vtr_push_dot1q': 0, + 'vtr_tag1': 0, + 'vtr_tag2': 0}, + VppSetupEnvHelper.get_vpp_interface_by_mac( + self.VPP_INTERFACES_DUMP, mac_address)) + + def test_get_vpp_interface_by_mac_error(self): + mac_address = '90:e2:ba:7c:41:a9' + with self.assertRaises(ValueError) as raised: + VppSetupEnvHelper.get_vpp_interface_by_mac( + [{ + "sw_if_index": 1, + "sup_sw_if_index": 1, + "l2_address_length": 7, + "l2_address": [144, 226, 186, 124, 65, 169, 0, 0], + "interface_name": "TenGigabitEthernetff/6/0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 2, + "link_speed": 32, + "mtu": 9202, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + }], mac_address) + + self.assertIn('l2_address_length value is not 6.', + str(raised.exception)) + + def test_get_vpp_interface_by_mac_l2_error(self): + mac_address = '90:e2:ba:7c:41:a7' + with self.assertRaises(KeyError) as raised: + VppSetupEnvHelper.get_vpp_interface_by_mac( + [{ + "sw_if_index": 1, + "sup_sw_if_index": 1, + "l2_address_length": 6, + "l2_address_err": [144, 226, 186, 124, 65, 167, 0, 0], + "interface_name": "TenGigabitEthernetff/6/0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 2, + "link_speed": 32, + "mtu": 9202, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + }], mac_address) + + self.assertIn( + 'key l2_address not found in interface dict.Probably input list ' \ + 'is not parsed from correct VAT json output.', + str(raised.exception)) + + def test_get_vpp_interface_by_mac_l2_length_error(self): + mac_address = '90:e2:ba:7c:41:a6' + with self.assertRaises(KeyError) as raised: + VppSetupEnvHelper.get_vpp_interface_by_mac( + [{ + "sw_if_index": 1, + "sup_sw_if_index": 1, + "l2_address_length_err": 6, + "l2_address": [144, 226, 186, 124, 65, 166, 0, 0], + "interface_name": "TenGigabitEthernetff/6/0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 2, + "link_speed": 32, + "mtu": 9202, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + }], mac_address) + + self.assertIn( + 'key l2_address_length not found in interface dict. Probably ' \ + 'input list is not parsed from correct VAT json output.', + str(raised.exception)) + + def test_get_prefix_length(self): + start_ip = '10.10.10.0' + end_ip = '10.10.10.127' + ips = [ipaddress.ip_address(ip) for ip in + [str(ipaddress.ip_address(start_ip)), str(end_ip)]] + lowest_ip, highest_ip = min(ips), max(ips) + + self.assertEqual(25, + VppSetupEnvHelper.get_prefix_length(int(lowest_ip), + int(highest_ip), + lowest_ip.max_prefixlen)) + + def test_get_prefix_length_zero_prefix(self): + start_ip = '10.0.0.0' + end_ip = '10.0.0.0' + ips = [ipaddress.ip_address(ip) for ip in + [str(ipaddress.ip_address(start_ip)), str(end_ip)]] + lowest_ip, highest_ip = min(ips), max(ips) + + self.assertEqual(0, + VppSetupEnvHelper.get_prefix_length(int(lowest_ip), + int(highest_ip), + 0)) + + +class TestVatTerminal(unittest.TestCase): + + def test___init___error(self): + ssh_helper = mock.Mock() + ssh_helper.interactive_terminal_open.side_effect = exceptions.SSHTimeout + + with self.assertRaises(RuntimeError) as raised: + VatTerminal(ssh_helper, json_param=True) + self.assertIn('Cannot open interactive terminal', + str(raised.exception)) + + def test___init___exec_error(self): + ssh_helper = mock.Mock() + ssh_helper.interactive_terminal_exec_command.side_effect = exceptions.SSHTimeout + VatTerminal(ssh_helper, json_param=True) + + def test_vat_terminal_exec_cmd(self): + ssh_helper = mock.Mock() + ssh_helper.interactive_terminal_exec_command.return_value = str( + {'empty': 'value'}).replace("\'", "\"") + vat_terminal = VatTerminal(ssh_helper, json_param=True) + + self.assertEqual({'empty': 'value'}, + vat_terminal.vat_terminal_exec_cmd( + "hw_interface_set_mtu sw_if_index 1 mtu 9200")) + + def test_vat_terminal_exec_cmd_array(self): + ssh_helper = mock.Mock() + ssh_helper.interactive_terminal_exec_command.return_value = str( + [{'empty': 'value'}]).replace("\'", "\"") + vat_terminal = VatTerminal(ssh_helper, json_param=True) + + self.assertEqual([{'empty': 'value'}], + vat_terminal.vat_terminal_exec_cmd( + "hw_interface_set_mtu sw_if_index 1 mtu 9200")) + + def test_vat_terminal_exec_cmd_without_output(self): + ssh_helper = mock.Mock() + ssh_helper.interactive_terminal_exec_command.return_value = str( + {'empty': 'value'}).replace("\'", "\"") + vat_terminal = VatTerminal(ssh_helper, json_param=False) + + self.assertIsNone(vat_terminal.vat_terminal_exec_cmd( + "hw_interface_set_mtu sw_if_index 1 mtu 9200")) + + def test_vat_terminal_exec_cmd_error(self): + ssh_helper = mock.Mock() + ssh_helper.interactive_terminal_exec_command.return_value = str( + {'empty': 'value'}).replace("\'", "\"") + ssh_helper.interactive_terminal_exec_command.side_effect = exceptions.SSHTimeout + + vat_terminal = VatTerminal(ssh_helper, json_param=True) + + with self.assertRaises(RuntimeError) as raised: + vat_terminal.vat_terminal_exec_cmd( + "hw_interface_set_mtu sw_if_index 1 mtu 9200") + self.assertIn( + 'VPP is not running on node. VAT command hw_interface_set_mtu ' \ + 'sw_if_index 1 mtu 9200 execution failed', + str(raised.exception)) + + def test_vat_terminal_exec_cmd_output_error(self): + ssh_helper = mock.Mock() + ssh_helper.interactive_terminal_exec_command.return_value = str( + 'empty: value').replace("\'", "\"") + + vat_terminal = VatTerminal(ssh_helper, json_param=True) + + with self.assertRaises(RuntimeError) as raised: + vat_terminal.vat_terminal_exec_cmd( + "hw_interface_set_mtu sw_if_index 1 mtu 9200") + self.assertIn( + 'VAT command hw_interface_set_mtu sw_if_index 1 mtu 9200: no JSON data.', + str(raised.exception)) + + def test_vat_terminal_close(self): + ssh_helper = mock.Mock() + vat_terminal = VatTerminal(ssh_helper, json_param=False) + self.assertIsNone(vat_terminal.vat_terminal_close()) + + def test_vat_terminal_close_error(self): + ssh_helper = mock.Mock() + ssh_helper.interactive_terminal_exec_command.side_effect = exceptions.SSHTimeout + vat_terminal = VatTerminal(ssh_helper, json_param=False) + with self.assertRaises(RuntimeError) as raised: + vat_terminal.vat_terminal_close() + self.assertIn('Failed to close VAT console', str(raised.exception)) + + def test_vat_terminal_close_vat_error(self): + ssh_helper = mock.Mock() + ssh_helper.interactive_terminal_close.side_effect = exceptions.SSHTimeout + vat_terminal = VatTerminal(ssh_helper, json_param=False) + with self.assertRaises(RuntimeError) as raised: + vat_terminal.vat_terminal_close() + self.assertIn('Cannot close interactive terminal', + str(raised.exception)) + + def test_vat_terminal_exec_cmd_from_template(self): + ssh_helper = mock.Mock() + vat_terminal = VatTerminal(ssh_helper, json_param=False) + + with mock.patch.object(vat_terminal, 'vat_terminal_exec_cmd') as \ + mock_vat_terminal_exec_cmd: + mock_vat_terminal_exec_cmd.return_value = 'empty' + self.assertEqual(['empty'], + vat_terminal.vat_terminal_exec_cmd_from_template( + "hw_interface_set_mtu.vat", sw_if_index=1, + mtu=9200)) |