diff options
122 files changed, 5114 insertions, 2637 deletions
diff --git a/ansible/multi_port_baremetal_ixia_correlated_test.yaml b/ansible/multi_port_baremetal_ixia_correlated_test.yaml index ba92b5cd3..0d223181d 100644 --- a/ansible/multi_port_baremetal_ixia_correlated_test.yaml +++ b/ansible/multi_port_baremetal_ixia_correlated_test.yaml @@ -42,7 +42,6 @@ lib_path: "/opt/ixia/ixos-api/8.01.0.2/lib/ixTcl1.0" root_dir: "/opt/ixia/ixos-api/8.01.0.2/" py_bin_path: "/opt/ixia/ixload/8.01.106.3/bin/" - py_lib_path: "/opt/ixia/ixnetwork/8.01.1029.14/lib/PythonApi" dut_result_dir: "/mnt/results" version: "8.01.106.3" pcis: diff --git a/ansible/multi_port_baremetal_ixia_test.yaml b/ansible/multi_port_baremetal_ixia_test.yaml index 52bc40b43..d2dfaa3c4 100644 --- a/ansible/multi_port_baremetal_ixia_test.yaml +++ b/ansible/multi_port_baremetal_ixia_test.yaml @@ -42,7 +42,6 @@ lib_path: "/opt/ixia/ixos-api/8.01.0.2/lib/ixTcl1.0" root_dir: "/opt/ixia/ixos-api/8.01.0.2/" py_bin_path: "/opt/ixia/ixload/8.01.106.3/bin/" - py_lib_path: "/opt/ixia/ixnetwork/8.01.1029.14/lib/PythonApi" dut_result_dir: "/mnt/results" version: "8.01.106.3" pcis: diff --git a/ansible/roles/download_trex/defaults/main.yml b/ansible/roles/download_trex/defaults/main.yml index 6e8fa7020..cbaae1d84 100644 --- a/ansible/roles/download_trex/defaults/main.yml +++ b/ansible/roles/download_trex/defaults/main.yml @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. --- -trex_version: v2.28 +trex_version: v2.41 trex_url: "https://trex-tgn.cisco.com/trex/release/{{ trex_version }}.tar.gz" trex_file: "{{ trex_url|basename }}" trex_unarchive: "{{ trex_file|regex_replace('[.]tar.gz$', '') }}" @@ -20,3 +20,4 @@ trex_dest: "{{ clone_dest }}/" trex_sha256s: "v2.20": "sha256:eb5a069f758a36133a185c7e27af10834ca03d11441165403529fbd7844658fb" "v2.28": "sha256:c3f08aabbd69dddb09843984d41acbe9ba1af6a6ef3380a7830f7c9e33134207" + "v2.41": "sha256:aa4122d82cc7b25a16a20d6dd465eccd89e31c51816d4103765b86d06a8b9810" diff --git a/ansible/roles/install_trex/defaults/main.yml b/ansible/roles/install_trex/defaults/main.yml index a5555e355..79a04fedd 100644 --- a/ansible/roles/install_trex/defaults/main.yml +++ b/ansible/roles/install_trex/defaults/main.yml @@ -13,6 +13,6 @@ # limitations under the License. --- #TREX_DOWNLOAD: "https://trex-tgn.cisco.com/trex/release/v2.05.tar.gz" -TREX_VERSION: v2.28 +TREX_VERSION: v2.41 TREX_DOWNLOAD: "{{ nsb_mirror_url|ternary(nsb_mirror_url, 'https://trex-tgn.cisco.com/trex/release' }}/{{ TREX_VERSION }}.tar.gz" INSTALL_BIN_PATH: "/opt/nsb_bin" diff --git a/ansible/standalone_ovs_scale_out_ixia_correlated_test.yaml b/ansible/standalone_ovs_scale_out_ixia_correlated_test.yaml index 516676576..b54ea9b57 100644 --- a/ansible/standalone_ovs_scale_out_ixia_correlated_test.yaml +++ b/ansible/standalone_ovs_scale_out_ixia_correlated_test.yaml @@ -51,13 +51,12 @@ user: "" password: "" key_filename: ~ - tg_config: + tg_config: ixchassis: "1.1.1.127" #ixia chassis ip tcl_port: "8009" # tcl server port lib_path: "/opt/ixia/ixos-api/8.01.0.2/lib/ixTcl1.0" root_dir: "/opt/ixia/ixos-api/8.01.0.2/" py_bin_path: "/opt/ixia/ixload/8.01.106.3/bin/" - py_lib_path: "/opt/ixia/ixnetwork/8.01.1029.14/lib/PythonApi" dut_result_dir: "/mnt/results" version: "8.01.106.3" pcis: diff --git a/ansible/standalone_ovs_scale_out_ixia_test.yaml b/ansible/standalone_ovs_scale_out_ixia_test.yaml index ff665377f..cae373432 100644 --- a/ansible/standalone_ovs_scale_out_ixia_test.yaml +++ b/ansible/standalone_ovs_scale_out_ixia_test.yaml @@ -60,7 +60,6 @@ lib_path: "/opt/ixia/ixos-api/8.01.0.2/lib/ixTcl1.0" root_dir: "/opt/ixia/ixos-api/8.01.0.2/" py_bin_path: "/opt/ixia/ixload/8.01.106.3/bin/" - py_lib_path: "/opt/ixia/ixnetwork/8.01.1029.14/lib/PythonApi" dut_result_dir: "/mnt/results" version: "8.01.106.3" pcis: diff --git a/ansible/standalone_sriov_scale_out_ixia_correlated_test.yaml b/ansible/standalone_sriov_scale_out_ixia_correlated_test.yaml index 45a4a498b..0e3a0af55 100644 --- a/ansible/standalone_sriov_scale_out_ixia_correlated_test.yaml +++ b/ansible/standalone_sriov_scale_out_ixia_correlated_test.yaml @@ -43,13 +43,12 @@ user: "" password: "" key_filename: ~ - tg_config: + tg_config: ixchassis: "1.1.1.127" #ixia chassis ip tcl_port: "8009" # tcl server port lib_path: "/opt/ixia/ixos-api/8.01.0.2/lib/ixTcl1.0" root_dir: "/opt/ixia/ixos-api/8.01.0.2/" py_bin_path: "/opt/ixia/ixload/8.01.106.3/bin/" - py_lib_path: "/opt/ixia/ixnetwork/8.01.1029.14/lib/PythonApi" dut_result_dir: "/mnt/results" version: "8.01.106.3" pcis: diff --git a/ansible/standalone_sriov_scale_out_ixia_test.yaml b/ansible/standalone_sriov_scale_out_ixia_test.yaml index 659dbef07..8fb09d9b9 100644 --- a/ansible/standalone_sriov_scale_out_ixia_test.yaml +++ b/ansible/standalone_sriov_scale_out_ixia_test.yaml @@ -49,7 +49,6 @@ lib_path: "/opt/ixia/ixos-api/8.01.0.2/lib/ixTcl1.0" root_dir: "/opt/ixia/ixos-api/8.01.0.2/" py_bin_path: "/opt/ixia/ixload/8.01.106.3/bin/" - py_lib_path: "/opt/ixia/ixnetwork/8.01.1029.14/lib/PythonApi" dut_result_dir: "/mnt/results" version: "8.01.106.3" pcis: diff --git a/docker/Dockerfile b/docker/Dockerfile index 709db317e..95ec1857e 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -47,8 +47,8 @@ ADD http://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-amd64-d COPY ./exec_tests.sh /usr/local/bin/ -ENV NSB_DIR="/opt/nsb_bin" \ - PYTHONPATH="${PYTHONPATH}:${NSB_DIR}/trex_client:${NSB_DIR}/trex_client/stl" +ENV NSB_DIR="/opt/nsb_bin" +ENV PYTHONPATH="${PYTHONPATH}:${NSB_DIR}/trex_client:${NSB_DIR}/trex_client/stl" WORKDIR ${REPOS_DIR} CMD ["/usr/bin/supervisord"] diff --git a/docs/release/results/euphrates_fraser_comparison.rst b/docs/release/results/euphrates_fraser_comparison.rst new file mode 100644 index 000000000..53dfb994f --- /dev/null +++ b/docs/release/results/euphrates_fraser_comparison.rst @@ -0,0 +1,602 @@ +.. This work is licensed under a Creative Commons Attribution 4.0 International +.. License. +.. http://creativecommons.org/licenses/by/4.0 + +======================================================= +Test results analysis for Euphrates and Fraser releases +======================================================= + +TC002 +----- + +The round-trip-time (RTT) between 2 VMs on different blades is measured using +ping. + +Most test run measurements result on average between 0.39 and 4.00 ms. +Compared with Euphrates release, the average RTT result of the same pod experiences +a slight decline in Fraser release. For example, the average RTT of arm-pod5 is +1.518 in Ehphrates and 1.714 in Fraser. The average RTT of intel-pod18 is 1.6575 +ms in Ehphrates and 1.856 ms in Fraser. + +{ + + "huawei-pod2:stable/euphrates": [0.3925], + + "lf-pod2:stable/euphrates": [0.5315], + + "lf-pod1:stable/euphrates": [0.62], + + "huawei-pod2:stable/fraser": [0.677], + + "lf-pod1:stable/fraser": [0.725], + + "flex-pod2:stable/euphrates": [0.795], + + "huawei-pod12:stable/euphrates": [0.87], + + "ericsson-pod1:stable/fraser": [0.9165], + + "huawei-pod12:stable/fraser": [1.0465], + + "lf-pod2:stable/fraser": [1.2325], + + "intel-pod5:stable/euphrates": [1.25], + + "ericsson-virtual3:stable/euphrates": [1.2655], + + "ericsson-pod1:stable/euphrates": [1.372], + + "zte-pod2:stable/fraser": [1.395], + + "arm-pod5:stable/euphrates": [1.518], + + "huawei-virtual4:stable/euphrates": [1.5355], + + "ericsson-virtual4:stable/fraser": [1.582], + + "huawei-virtual3:stable/euphrates": [1.606], + + "intel-pod18:stable/euphrates": [1.6575], + + "huawei-virtual4:stable/fraser": [1.697], + + "huawei-virtual8:stable/euphrates": [1.709], + + "arm-pod5:stable/fraser": [1.714], + + "huawei-virtual3:stable/fraser": [1.716], + + "intel-pod18:stable/fraser": [1.856], + + "huawei-virtual2:stable/euphrates": [1.872], + + "arm-pod6:stable/euphrates": [1.895], + + "huawei-virtual2:stable/fraser": [1.964], + + "huawei-virtual1:stable/fraser": [1.9765], + + "huawei-virtual9:stable/euphrates": [2.0745], + + "arm-pod6:stable/fraser": [2.209], + + "huawei-virtual1:stable/euphrates": [2.495], + + "ericsson-virtual2:stable/euphrates": [2.7895], + + "ericsson-virtual4:stable/euphrates": [3.768], + + "ericsson-virtual1:stable/euphrates": [3.8035], + + "ericsson-virtual3:stable/fraser": [3.9175], + + "ericsson-virtual2:stable/fraser": [4.004] + +} + +TC010 +----- + +The tool we use to measure memory read latency is lmbench, which is a series of +micro benchmarks intended to measure basic operating system and hardware system +metrics. Compared with Euphrates release, the memory read latency of the same pod +also experience a slight decline. Virtual pods seem to have a higher memory read +latency than physical pods. Compared with X86 pods, the memory read latency of +arm pods is significant higher. + +{ + + "ericsson-pod1:stable/euphrates": [5.7785], + + "flex-pod2:stable/euphrates": [5.908], + + "ericsson-virtual1:stable/euphrates": [6.412], + + "intel-pod18:stable/euphrates": [6.5905], + + "intel-pod5:stable/euphrates": [6.6975], + + "ericsson-pod1:stable/fraser": [7.0645], + + "ericsson-virtual4:stable/euphrates": [7.183], + + "intel-pod18:stable/fraser": [7.4465], + + "zte-pod2:stable/fraser": [8.1865], + + "ericsson-virtual2:stable/euphrates": [8.4985], + + "huawei-pod2:stable/euphrates": [8.877], + + "huawei-pod12:stable/euphrates": [9.091], + + "huawei-pod2:stable/fraser": [9.236], + + "huawei-pod12:stable/fraser": [9.615], + + "ericsson-virtual3:stable/euphrates": [9.719], + + "ericsson-virtual2:stable/fraser": [9.8925], + + "huawei-virtual4:stable/euphrates": [10.1195], + + "huawei-virtual3:stable/euphrates": [10.19], + + "huawei-virtual2:stable/fraser": [10.22], + + "huawei-virtual1:stable/euphrates": [10.3045], + + "huawei-virtual9:stable/euphrates": [10.318], + + "ericsson-virtual4:stable/fraser": [10.5465], + + "ericsson-virtual3:stable/fraser": [10.9355], + + "huawei-virtual3:stable/fraser": [10.95], + + "huawei-virtual2:stable/euphrates": [11.274], + + "huawei-virtual4:stable/fraser": [11.557], + + "lf-pod1:stable/euphrates": [15.7025], + + "lf-pod2:stable/euphrates": [15.8495], + + "lf-pod2:stable/fraser": [16.5595], + + "lf-pod1:stable/fraser": [16.8395], + + "arm-pod5:stable/euphrates": [18.092], + + "arm-pod5:stable/fraser": [18.744], + + "huawei-virtual1:stable/fraser": [19.8235], + + "huawei-virtual8:stable/euphrates": [33.999], + + "arm-pod6:stable/euphrates": [41.5605], + + "arm-pod6:stable/fraser": [55.804] + +} + +TC011 +----- + +Iperf3 is a tool for evaluating the packet delay variation between 2 VMs on +different blades. In general, the packet delay variation of the two releases +look similar. + +{ + + "arm-pod6:stable/fraser": [1], + + "ericsson-pod1:stable/fraser": [1], + + "ericsson-virtual2:stable/fraser": [1], + + "ericsson-virtual3:stable/fraser": [1], + + "lf-pod2:stable/fraser": [1], + + "huawei-virtual1:stable/fraser": [2997], + + "huawei-virtual2:stable/euphrates": [2997], + + "flex-pod2:stable/euphrates": [2997.5], + + "huawei-virtual3:stable/euphrates": [2998], + + "huawei-virtual3:stable/fraser": [2999], + + "huawei-virtual9:stable/euphrates": [3000], + + "huawei-virtual8:stable/euphrates": [3001], + + "huawei-virtual4:stable/euphrates": [3002], + + "huawei-virtual4:stable/fraser": [3002], + + "ericsson-virtual3:stable/euphrates": [3006], + + "huawei-virtual1:stable/euphrates": [3007], + + "ericsson-virtual2:stable/euphrates": [3009], + + "intel-pod18:stable/euphrates": [3010], + + "ericsson-virtual4:stable/euphrates": [3017], + + "lf-pod2:stable/euphrates": [3021], + + "arm-pod5:stable/euphrates": [3022], + + "arm-pod6:stable/euphrates": [3022], + + "ericsson-pod1:stable/euphrates": [3022], + + "huawei-pod12:stable/euphrates": [3022], + + "huawei-pod12:stable/fraser": [3022], + + "huawei-pod2:stable/euphrates": [3022], + + "huawei-pod2:stable/fraser": [3022], + + "intel-pod18:stable/fraser": [3022], + + "intel-pod5:stable/euphrates": [3022], + + "lf-pod1:stable/euphrates": [3022], + + "lf-pod1:stable/fraser": [3022], + + "zte-pod2:stable/fraser": [3022], + + "huawei-virtual2:stable/fraser": [3025] + +} + +TC012 +----- + +Lmbench is also used to measure the memory read and write bandwidth. +Like TC010, compared with Euphrates release, the memory read and write bandwidth +of the same pod also experience a slight decline. And compared with X86 pods, the memory +read and write bandwidth of arm pods is significant lower. + +{ + + "lf-pod1:stable/euphrates": [22912.39], + + "lf-pod2:stable/euphrates": [22637.67], + + "lf-pod1:stable/fraser": [20552.9], + + "flex-pod2:stable/euphrates": [20229.99], + + "lf-pod2:stable/fraser": [20058.925], + + "ericsson-pod1:stable/fraser": [18930.78], + + "intel-pod18:stable/fraser": [18757.545], + + "ericsson-virtual1:stable/euphrates": [17474.965], + + "ericsson-pod1:stable/euphrates": [17127.38], + + "ericsson-virtual4:stable/euphrates": [16219.97], + + "ericsson-virtual2:stable/euphrates": [15652.28], + + "ericsson-virtual3:stable/euphrates": [15551.26], + + "ericsson-virtual4:stable/fraser": [15389.465], + + "ericsson-virtual2:stable/fraser": [15343.79], + + "huawei-pod2:stable/euphrates": [15017.2], + + "huawei-pod2:stable/fraser": [14870.78], + + "huawei-virtual4:stable/euphrates": [14266.34], + + "huawei-virtual1:stable/euphrates": [14233.035], + + "huawei-virtual3:stable/euphrates": [14227.63], + + "zte-pod2:stable/fraser": [14157.99], + + "huawei-pod12:stable/euphrates": [14147.245], + + "huawei-pod12:stable/fraser": [14126.99], + + "intel-pod18:stable/euphrates": [14058.33], + + "huawei-virtual3:stable/fraser": [13929.67], + + "huawei-virtual2:stable/euphrates": [13862.85], + + "huawei-virtual4:stable/fraser": [13847.155], + + "huawei-virtual2:stable/fraser": [13702.92], + + "huawei-virtual1:stable/fraser": [13496.45], + + "intel-pod5:stable/euphrates": [13280.32], + + "ericsson-virtual3:stable/fraser": [12733.19], + + "huawei-virtual9:stable/euphrates": [12559.445], + + "huawei-virtual8:stable/euphrates": [8998.02], + + "arm-pod5:stable/euphrates": [4388.875], + + "arm-pod5:stable/fraser": [4326.11], + + "arm-pod6:stable/euphrates": [4260.2], + + "arm-pod6:stable/fraser": [3809.885] + +} + +TC014 +----- + +The Unixbench is used to evaluate the IaaS processing speed with regards to +score of single CPU running and parallel running. Below are the single CPU running +scores. It can be seen that the processing test results vary from scores 715 to 3737. +In general, the single CPU score of the two releases look similar. + +{ + + "lf-pod2:stable/fraser": [3737.6], + + "lf-pod2:stable/euphrates": [3723.95], + + "lf-pod1:stable/fraser": [3702.7], + + "lf-pod1:stable/euphrates": [3669], + + "intel-pod5:stable/euphrates": [3388.6], + + "intel-pod18:stable/euphrates": [3298.4], + + "flex-pod2:stable/euphrates": [3208.6], + + "ericsson-pod1:stable/fraser": [3131.6], + + "intel-pod18:stable/fraser": [3098.1], + + "ericsson-virtual1:stable/euphrates": [2988.9], + + "zte-pod2:stable/fraser": [2831.4], + + "ericsson-pod1:stable/euphrates": [2669.1], + + "ericsson-virtual4:stable/euphrates": [2598.5], + + "ericsson-virtual2:stable/fraser": [2559.7], + + "ericsson-virtual3:stable/euphrates": [2553.15], + + "huawei-pod2:stable/euphrates": [2531.2], + + "huawei-pod2:stable/fraser": [2528.9], + + "ericsson-virtual4:stable/fraser": [2527.8], + + "ericsson-virtual2:stable/euphrates": [2526.9], + + "huawei-virtual4:stable/euphrates": [2407.4], + + "huawei-virtual3:stable/fraser": [2379.1], + + "huawei-virtual3:stable/euphrates": [2374.6], + + "huawei-virtual4:stable/fraser": [2362.1], + + "huawei-virtual2:stable/euphrates": [2326.4], + + "huawei-virtual9:stable/euphrates": [2324.95], + + "huawei-virtual1:stable/euphrates": [2302.6], + + "huawei-virtual2:stable/fraser": [2299.3], + + "huawei-pod12:stable/euphrates": [2232.2], + + "huawei-pod12:stable/fraser": [2229], + + "huawei-virtual1:stable/fraser": [2171.3], + + "ericsson-virtual3:stable/fraser": [2104.8], + + "huawei-virtual8:stable/euphrates": [2085.3], + + "arm-pod5:stable/fraser": [1764.2], + + "arm-pod5:stable/euphrates": [1754.4], + + "arm-pod6:stable/euphrates": [716.15], + + "arm-pod6:stable/fraser": [715.4] + +} + +TC069 +----- + +With the block size changing from 1 kb to 512 kb, the memory write bandwidth +tends to become larger first and then smaller within every run test. Below are +the scores for 32mb block array. + +{ + + "intel-pod18:stable/euphrates": [18871.79], + + "intel-pod18:stable/fraser": [16939.24], + + "intel-pod5:stable/euphrates": [16055.79], + + "arm-pod6:stable/euphrates": [13327.02], + + "arm-pod6:stable/fraser": [11895.71], + + "flex-pod2:stable/euphrates": [9384.585], + + "zte-pod2:stable/fraser": [9375.33], + + "ericsson-pod1:stable/euphrates": [9331.535], + + "huawei-pod12:stable/euphrates": [9164.88], + + "ericsson-pod1:stable/fraser": [9140.42], + + "huawei-pod2:stable/euphrates": [9026.52], + + "huawei-pod12:stable/fraser": [8993.37], + + "huawei-virtual9:stable/euphrates": [8825.805], + + "huawei-pod2:stable/fraser": [8794.01], + + "huawei-virtual2:stable/fraser": [7670.21], + + "ericsson-virtual1:stable/euphrates": [7615.97], + + "ericsson-virtual4:stable/euphrates": [7539.23], + + "arm-pod5:stable/fraser": [7479.32], + + "arm-pod5:stable/euphrates": [7403.38], + + "huawei-virtual3:stable/euphrates": [7247.89], + + "ericsson-virtual2:stable/fraser": [7219.21], + + "huawei-virtual2:stable/euphrates": [7205.35], + + "huawei-virtual1:stable/euphrates": [7196.405], + + "ericsson-virtual3:stable/euphrates": [7173.72], + + "huawei-virtual4:stable/euphrates": [7131.47], + + "ericsson-virtual2:stable/euphrates": [7129.08], + + "huawei-virtual4:stable/fraser": [7059.045], + + "huawei-virtual3:stable/fraser": [7023.57], + + "lf-pod1:stable/euphrates": [6928.18], + + "lf-pod2:stable/euphrates": [6875.88], + + "lf-pod2:stable/fraser": [6834.7], + + "lf-pod1:stable/fraser": [6775.27], + + "ericsson-virtual4:stable/fraser": [6522.86], + + "ericsson-virtual3:stable/fraser": [5835.59], + + "huawei-virtual8:stable/euphrates": [5729.705], + + "huawei-virtual1:stable/fraser": [5617.12] + +} + +TC082 +----- + +For this test case, we use perf to measure context-switches under load. +High context switch rates are not themselves an issue, but they may point the +way to a more significant problem. + +{ + + "zte-pod2:stable/fraser": [306.5], + + "huawei-pod12:stable/euphrates": [316], + + "lf-pod2:stable/fraser": [337.5], + + "intel-pod18:stable/euphrates": [340], + + "intel-pod18:stable/fraser": [343.5], + + "intel-pod5:stable/euphrates": [357.5], + + "ericsson-pod1:stable/euphrates": [384], + + "lf-pod2:stable/euphrates": [394.5], + + "huawei-pod12:stable/fraser": [399], + + "lf-pod1:stable/euphrates": [435], + + "lf-pod1:stable/fraser": [454], + + "flex-pod2:stable/euphrates": [476], + + "huawei-pod2:stable/euphrates": [518], + + "huawei-pod2:stable/fraser": [544.5], + + "arm-pod5:stable/euphrates": [869.5], + + "huawei-virtual9:stable/euphrates": [1002], + + "huawei-virtual4:stable/fraser": [1138], + + "huawei-virtual4:stable/euphrates": [1174], + + "huawei-virtual3:stable/euphrates": [1239], + + "ericsson-pod1:stable/fraser": [1305], + + "huawei-virtual2:stable/euphrates": [1430], + + "huawei-virtual3:stable/fraser": [1433], + + "huawei-virtual1:stable/fraser": [1470], + + "huawei-virtual1:stable/euphrates": [1489], + + "arm-pod6:stable/fraser": [1738.5], + + "arm-pod6:stable/euphrates": [1883.5] + +} + +TC083 +----- + +TC083 measures network latency and throughput between VMs using netperf. +The test results shown below are for UDP throughout. + +{ + + "lf-pod1:stable/euphrates": [2204.42], + + "lf-pod2:stable/fraser": [1893.39], + + "intel-pod18:stable/euphrates": [1835.55], + + "lf-pod2:stable/euphrates": [1676.705], + + "intel-pod5:stable/euphrates": [1612.555], + + "zte-pod2:stable/fraser": [1543.995], + + "lf-pod1:stable/fraser": [1480.86], + + "intel-pod18:stable/fraser": [1417.015], + + "flex-pod2:stable/euphrates": [1370.23], + + "huawei-pod12:stable/euphrates": [1300.12] + +} diff --git a/docs/release/results/euphrates_fraser_comparsion.rst b/docs/release/results/euphrates_fraser_comparsion.rst deleted file mode 100644 index 222dc8bb0..000000000 --- a/docs/release/results/euphrates_fraser_comparsion.rst +++ /dev/null @@ -1,8 +0,0 @@ -.. This work is licensed under a Creative Commons Attribution 4.0 International -.. License. -.. http://creativecommons.org/licenses/by/4.0 - -======================================================= -Test results analysis for Euphrates and Fraser releases -======================================================= - diff --git a/docs/release/results/images/tc002_pod_fraser.png b/docs/release/results/images/tc002_pod_fraser.png Binary files differnew file mode 100644 index 000000000..797dc3136 --- /dev/null +++ b/docs/release/results/images/tc002_pod_fraser.png diff --git a/docs/release/results/images/tc002_scenario_fraser.png b/docs/release/results/images/tc002_scenario_fraser.png Binary files differnew file mode 100644 index 000000000..ff42e6516 --- /dev/null +++ b/docs/release/results/images/tc002_scenario_fraser.png diff --git a/docs/release/results/images/tc010_pod_fraser.png b/docs/release/results/images/tc010_pod_fraser.png Binary files differnew file mode 100644 index 000000000..23367d34a --- /dev/null +++ b/docs/release/results/images/tc010_pod_fraser.png diff --git a/docs/release/results/images/tc010_scenario_fraser.png b/docs/release/results/images/tc010_scenario_fraser.png Binary files differnew file mode 100644 index 000000000..a481a595f --- /dev/null +++ b/docs/release/results/images/tc010_scenario_fraser.png diff --git a/docs/release/results/images/tc011_pod_fraser.png b/docs/release/results/images/tc011_pod_fraser.png Binary files differnew file mode 100644 index 000000000..82dc9c763 --- /dev/null +++ b/docs/release/results/images/tc011_pod_fraser.png diff --git a/docs/release/results/images/tc011_scenario_fraser.png b/docs/release/results/images/tc011_scenario_fraser.png Binary files differnew file mode 100644 index 000000000..226d0b856 --- /dev/null +++ b/docs/release/results/images/tc011_scenario_fraser.png diff --git a/docs/release/results/images/tc012_pod_fraser.png b/docs/release/results/images/tc012_pod_fraser.png Binary files differnew file mode 100644 index 000000000..66e79be85 --- /dev/null +++ b/docs/release/results/images/tc012_pod_fraser.png diff --git a/docs/release/results/images/tc012_scenario_fraser.png b/docs/release/results/images/tc012_scenario_fraser.png Binary files differnew file mode 100644 index 000000000..4ef44119a --- /dev/null +++ b/docs/release/results/images/tc012_scenario_fraser.png diff --git a/docs/release/results/images/tc014_pod_fraseer.png b/docs/release/results/images/tc014_pod_fraseer.png Binary files differnew file mode 100644 index 000000000..697201d76 --- /dev/null +++ b/docs/release/results/images/tc014_pod_fraseer.png diff --git a/docs/release/results/images/tc014_scenario_fraser.png b/docs/release/results/images/tc014_scenario_fraser.png Binary files differnew file mode 100644 index 000000000..f7865dcdc --- /dev/null +++ b/docs/release/results/images/tc014_scenario_fraser.png diff --git a/docs/release/results/images/tc069_pod_fraser.png b/docs/release/results/images/tc069_pod_fraser.png Binary files differnew file mode 100644 index 000000000..1cba192d7 --- /dev/null +++ b/docs/release/results/images/tc069_pod_fraser.png diff --git a/docs/release/results/images/tc069_scenario_fraser.png b/docs/release/results/images/tc069_scenario_fraser.png Binary files differnew file mode 100644 index 000000000..f988b90c6 --- /dev/null +++ b/docs/release/results/images/tc069_scenario_fraser.png diff --git a/docs/release/results/images/tc082_pod_fraser.png b/docs/release/results/images/tc082_pod_fraser.png Binary files differnew file mode 100644 index 000000000..d54ab901a --- /dev/null +++ b/docs/release/results/images/tc082_pod_fraser.png diff --git a/docs/release/results/images/tc083_pod_fraser.png b/docs/release/results/images/tc083_pod_fraser.png Binary files differnew file mode 100644 index 000000000..942cc2074 --- /dev/null +++ b/docs/release/results/images/tc083_pod_fraser.png diff --git a/docs/release/results/index.rst b/docs/release/results/index.rst index 3ec9e1cff..63445fd1a 100644 --- a/docs/release/results/index.rst +++ b/docs/release/results/index.rst @@ -14,4 +14,4 @@ Yardstick test results .. include:: ./overview.rst .. include:: ./results.rst -.. include:: ./euphrates_fraser_comparsion.rst +.. include:: ./euphrates_fraser_comparison.rst diff --git a/docs/release/results/results.rst b/docs/release/results/results.rst index c75f5ae94..0ed92f867 100644 --- a/docs/release/results/results.rst +++ b/docs/release/results/results.rst @@ -23,6 +23,7 @@ Scenario Results The following documents contain results of Yardstick test cases executed on OPNFV labs, triggered by OPNFV CI pipeline, documented per test case. +For hardware details of OPNFV labs, please visit: https://wiki.opnfv.org/display/pharos/Community+Labs .. toctree:: :maxdepth: 1 @@ -38,6 +39,7 @@ OPNFV labs, triggered by OPNFV CI pipeline, documented per test case. Test results of executed tests are avilable in Dashboard_ and logs in Jenkins_. +Test results for Fraser release are collected from April 10, 2018 to May 13, 2018. Feature Test Results ==================== diff --git a/docs/release/results/tc002-network-latency.rst b/docs/release/results/tc002-network-latency.rst index 722423473..064983bec 100644 --- a/docs/release/results/tc002-network-latency.rst +++ b/docs/release/results/tc002-network-latency.rst @@ -315,3 +315,211 @@ The influence of the POD Fraser release -------------- + +Test results per scenario and pod (lower is better): + +{ + + "os-odl_l3-nofeature-ha:huawei-pod2:compass": [0.42], + + "os-odl-sfc-ha:huawei-pod2:compass": [0.557], + + "os-nosdn-ovs-ha:ericsson-pod1:fuel": [0.5765], + + "os-nosdn-kvm-ha:huawei-pod2:compass": [0.582], + + "os-odl-bgpvpn-ha:lf-pod1:apex": [0.678], + + "os-nosdn-nofeature-ha:lf-pod1:apex": [0.7075], + + "os-nosdn-calipso-noha:lf-pod1:apex": [0.713], + + "os-nosdn-nofeature-noha:lf-pod1:apex": [0.7155], + + "os-nosdn-bar-ha:lf-pod1:apex": [0.732], + + "os-nosdn-bar-noha:lf-pod1:apex": [0.7415], + + "os-odl-nofeature-noha:lf-pod1:apex": [0.7565], + + "os-nosdn-ovs-ha:arm-pod6:fuel": [0.8015], + + "os-nosdn-nofeature-ha:huawei-pod2:compass": [0.908], + + "os-odl-nofeature-ha:ericsson-pod1:fuel": [0.9165], + + "os-nosdn-bar-ha:huawei-pod2:compass": [0.969], + + "os-nosdn-ovs-noha:ericsson-virtual2:fuel": [0.9765], + + "os-nosdn-nofeature-noha:huawei-pod12:joid": [1.0245], + + "os-nosdn-nofeature-ha:huawei-pod12:joid": [1.0495], + + "os-odl-sfc-noha:huawei-virtual4:compass": [1.1645], + + "os-nosdn-nofeature-ha:lf-pod2:fuel": [1.206], + + "os-odl-sfc-noha:huawei-virtual3:compass": [1.236], + + "os-nosdn-ovs-noha:ericsson-virtual4:fuel": [1.241], + + "os-nosdn-nofeature-ha:zte-pod2:daisy": [1.2805], + + "os-odl-nofeature-ha:lf-pod2:fuel": [1.286], + + "os-odl_l3-nofeature-noha:huawei-virtual3:compass": [1.299], + + "os-odl-sfc-ha:huawei-virtual4:compass": [1.305], + + "os-odl_l3-nofeature-noha:huawei-virtual4:compass": [1.309], + + "os-nosdn-kvm-noha:huawei-virtual4:compass": [1.314], + + "os-nosdn-nofeature-noha:huawei-virtual4:compass": [1.431], + + "os-nosdn-nofeature-ha:ericsson-pod1:fuel": [1.457], + + "os-odl-nofeature-ha:zte-pod2:daisy": [1.517], + + "os-nosdn-kvm-noha:huawei-virtual3:compass": [1.576], + + "os-nosdn-nofeature-noha:huawei-virtual3:compass": [1.592], + + "os-odl-nofeature-ha:arm-pod5:fuel": [1.714], + + "os-nosdn-nofeature-noha:intel-pod18:joid": [1.809], + + "os-nosdn-bar-noha:huawei-virtual4:compass": [1.81], + + "os-nosdn-nofeature-ha:intel-pod18:joid": [1.8505], + + "os-nosdn-nofeature-ha:huawei-virtual4:compass": [1.8895], + + "os-nosdn-nofeature-ha:huawei-virtual3:compass": [1.909], + + "os-odl_l3-nofeature-ha:huawei-virtual4:compass": [1.925], + + "os-nosdn-nofeature-noha:huawei-virtual2:compass": [1.964], + + "os-nosdn-openbaton-ha:intel-pod18:joid": [1.9755], + + "os-nosdn-nofeature-ha:huawei-virtual1:compass": [1.9765], + + "os-nosdn-bar-noha:huawei-virtual3:compass": [1.9915], + + "os-odl_l3-nofeature-ha:huawei-virtual3:compass": [1.9925], + + "os-nosdn-kvm-ha:huawei-virtual4:compass": [2.0265], + + "os-odl-nofeature-ha:arm-pod6:fuel": [2.106], + + "os-odl-sfc-ha:huawei-virtual3:compass": [2.124], + + "os-nosdn-kvm-ha:huawei-virtual3:compass": [2.185], + + "os-nosdn-nofeature-ha:arm-pod6:fuel": [2.281], + + "os-nosdn-bar-ha:huawei-virtual4:compass": [2.432], + + "os-odl-nofeature-noha:ericsson-virtual4:fuel": [2.483], + + "os-nosdn-bar-ha:huawei-virtual3:compass": [2.524], + + "os-nosdn-nofeature-noha:ericsson-virtual3:fuel": [3.9175], + + "os-odl-nofeature-noha:ericsson-virtual2:fuel": [4.338], + + "os-nosdn-nofeature-noha:ericsson-virtual2:fuel": [4.641] + +} + + +The influence of the scenario +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. image:: images/tc002_scenario_fraser.png + :width: 800px + :alt: TC002 influence of scenario + +{ + + "os-odl-bgpvpn-ha": [0.678], + + "os-nosdn-calipso-noha": [0.713], + + "os-nosdn-ovs-ha": [0.7245], + + "os-odl_l3-nofeature-ha": [0.7435], + + "os-odl-sfc-ha": [0.796], + + "os-nosdn-kvm-ha": [1.059], + + "os-nosdn-bar-ha": [1.083], + + "os-nosdn-ovs-noha": [1.09], + + "os-odl-sfc-noha": [1.196], + + "os-nosdn-nofeature-noha": [1.26], + + "os-nosdn-nofeature-ha": [1.291], + + "os-odl_l3-nofeature-noha": [1.308], + + "os-nosdn-bar-noha": [1.4125], + + "os-nosdn-kvm-noha": [1.4475], + + "os-odl-nofeature-ha": [1.508], + + "os-odl-nofeature-noha": [1.914], + + "os-nosdn-openbaton-ha": [1.9755] + +} + + +The influence of the POD +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. image:: images/tc002_pod_fraser.png + :width: 800px + :alt: TC002 influence of the POD + +{ + + "huawei-pod2": [0.677], + + "lf-pod1": [0.725], + + "ericsson-pod1": [0.9165], + + "huawei-pod12": [1.0465], + + "lf-pod2": [1.2325], + + "zte-pod2": [1.395], + + "ericsson-virtual4": [1.582], + + "huawei-virtual4": [1.697], + + "arm-pod5": [1.714], + + "huawei-virtual3": [1.716], + + "intel-pod18": [1.856], + + "huawei-virtual2": [1.964], + + "huawei-virtual1": [1.9765], + + "arm-pod6": [2.209], + + "ericsson-virtual3": [3.9175], + + "ericsson-virtual2": [4.004] + +} diff --git a/docs/release/results/tc010-memory-read-latency.rst b/docs/release/results/tc010-memory-read-latency.rst index 9a296b7a0..81559d647 100644 --- a/docs/release/results/tc010-memory-read-latency.rst +++ b/docs/release/results/tc010-memory-read-latency.rst @@ -297,3 +297,214 @@ The influence of the POD Fraser release -------------- + +Test results per scenario and pod (lower is better): + +{ + + "os-odl-nofeature-ha:ericsson-pod1:fuel": [6.8675], + + "os-nosdn-nofeature-noha:intel-pod18:joid": [6.991], + + "os-nosdn-openbaton-ha:intel-pod18:joid": [7.5535], + + "os-nosdn-nofeature-ha:intel-pod18:joid": [7.571], + "os-nosdn-ovs-ha:ericsson-pod1:fuel": [7.635], + + "os-nosdn-nofeature-ha:zte-pod2:daisy": [8.153], + + "os-odl-nofeature-ha:zte-pod2:daisy": [8.1935], + + "os-nosdn-bar-ha:huawei-pod2:compass": [9.1715], + + "os-odl-sfc-ha:huawei-pod2:compass": [9.1875], + + "os-odl_l3-nofeature-ha:huawei-pod2:compass": [9.241], + + "os-nosdn-nofeature-ha:huawei-pod2:compass": [9.255], + + "os-nosdn-kvm-ha:huawei-pod2:compass": [9.388], + + "os-nosdn-nofeature-noha:huawei-virtual4:compass": [9.5825], + + "os-nosdn-nofeature-noha:huawei-pod12:joid": [9.5875], + + "os-nosdn-nofeature-ha:huawei-pod12:joid": [9.6345], + + "os-odl-sfc-noha:huawei-virtual4:compass": [9.6535], + + "os-nosdn-nofeature-noha:ericsson-virtual2:fuel": [9.743], + + "os-odl-sfc-noha:huawei-virtual3:compass": [9.82], + + "os-odl-nofeature-noha:ericsson-virtual2:fuel": [9.8715], + + "os-odl_l3-nofeature-noha:huawei-virtual3:compass": [9.982], + + "os-nosdn-bar-noha:huawei-virtual4:compass": [10.0195], + + "os-odl_l3-nofeature-noha:huawei-virtual4:compass": [10.1285], + + "os-nosdn-nofeature-noha:huawei-virtual3:compass": [10.1335], + + "os-nosdn-nofeature-noha:huawei-virtual2:compass": [10.22], + + "os-nosdn-bar-noha:huawei-virtual3:compass": [10.2845], + + "os-nosdn-ovs-noha:ericsson-virtual4:fuel": [10.4185], + + "os-nosdn-ovs-noha:ericsson-virtual2:fuel": [10.4555], + + "os-nosdn-kvm-noha:huawei-virtual3:compass": [10.5635], + + "os-nosdn-kvm-noha:huawei-virtual4:compass": [10.6515], + + "os-nosdn-nofeature-noha:ericsson-virtual3:fuel": [10.9355], + + "os-odl-nofeature-noha:ericsson-virtual4:fuel": [11.2015], + + "os-odl_l3-nofeature-ha:huawei-virtual3:compass": [12.984], + + "os-nosdn-bar-ha:huawei-virtual3:compass": [13.306], + + "os-nosdn-nofeature-ha:huawei-virtual3:compass": [13.721], + + "os-nosdn-bar-ha:huawei-virtual4:compass": [14.133], + + "os-nosdn-nofeature-ha:ericsson-pod1:fuel": [14.158], + + "os-odl_l3-nofeature-ha:huawei-virtual4:compass": [14.375], + + "os-nosdn-nofeature-ha:huawei-virtual4:compass": [14.396], + + "os-nosdn-kvm-ha:huawei-virtual4:compass": [14.9375], + + "os-odl-sfc-ha:huawei-virtual3:compass": [14.957], + + "os-nosdn-calipso-noha:lf-pod1:apex": [16.3445], + + "os-nosdn-ovs-ha:lf-pod2:fuel": [16.478], + + "os-nosdn-nofeature-ha:lf-pod2:fuel": [16.4895], + + "os-odl-nofeature-noha:lf-pod1:apex": [16.55], + + "os-nosdn-nofeature-noha:lf-pod1:apex": [16.5665], + + "os-odl-sfc-noha:lf-pod1:apex": [16.598], + + "os-ovn-nofeature-noha:lf-pod1:apex": [16.805], + + "os-odl-nofeature-ha:lf-pod1:apex": [16.9095], + + "os-nosdn-bar-ha:lf-pod1:apex": [17.494], + + "os-nosdn-bar-noha:lf-pod1:apex": [17.4995], + + "os-nosdn-nofeature-ha:lf-pod1:apex": [18.094], + + "os-odl-nofeature-ha:arm-pod5:fuel": [18.744], + + "os-nosdn-nofeature-ha:huawei-virtual1:compass": [19.8235], + + "os-odl-nofeature-ha:lf-pod2:fuel": [20.758], + + "os-nosdn-kvm-ha:huawei-virtual3:compass": [26.5245], + + "os-nosdn-nofeature-ha:arm-pod6:fuel": [55.667], + + "os-odl-nofeature-ha:arm-pod6:fuel": [56.175], + + "os-nosdn-ovs-ha:arm-pod6:fuel": [57.86] + +} + + +The influence of the scenario +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. image:: images/tc010_scenario_fraser.png + :width: 800px + :alt: TC010 influence of scenario + +{ + + "os-nosdn-openbaton-ha": [7.5535], + + "os-odl-nofeature-ha": [8.2535], + + "os-odl-sfc-ha": [9.251], + + "os-nosdn-nofeature-ha": [9.464], + + "os-odl-sfc-noha": [9.8265], + + "os-odl_l3-nofeature-ha": [9.836], + + "os-odl_l3-nofeature-noha": [10.0565], + + "os-nosdn-nofeature-noha": [10.079], + + "os-nosdn-kvm-ha": [10.418], + + "os-nosdn-ovs-noha": [10.43], + + "os-nosdn-kvm-noha": [10.603], + + "os-nosdn-bar-noha": [11.067], + + "os-nosdn-bar-ha": [13.911], + + "os-odl-nofeature-noha": [14.046], + + "os-nosdn-calipso-noha": [16.3445], + + "os-nosdn-ovs-ha": [16.478], + + "os-ovn-nofeature-noha": [16.805] + +} + + +The influence of the POD +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. image:: images/tc010_pod_fraser.png + :width: 800px + :alt: TC010 influence of the POD + +{ + + "ericsson-pod1": [7.0645], + + "intel-pod18": [7.4465], + + "zte-pod2": [8.1865], + + "huawei-pod2": [9.236], + + "huawei-pod12": [9.615], + + "ericsson-virtual2": [9.8925], + + "huawei-virtual2": [10.22], + + "ericsson-virtual4": [10.5465], + + "ericsson-virtual3": [10.9355], + + "huawei-virtual3": [10.95], + + "huawei-virtual4": [11.557], + + "lf-pod2": [16.5595], + + "lf-pod1": [16.8395], + + "arm-pod5": [18.744], + + "huawei-virtual1": [19.8235], + + "arm-pod6": [55.804] + +} diff --git a/docs/release/results/tc011-packet-delay-variation.rst b/docs/release/results/tc011-packet-delay-variation.rst index b07ea8980..f255b50ca 100644 --- a/docs/release/results/tc011-packet-delay-variation.rst +++ b/docs/release/results/tc011-packet-delay-variation.rst @@ -260,3 +260,173 @@ The influence of the POD Fraser release -------------- + +Test results per scenario and pod (lower is better): + +{ + + "os-nosdn-nofeature-ha:arm-pod6:fuel": [1], + + "os-nosdn-nofeature-ha:ericsson-pod1:fuel": [1], + + "os-nosdn-nofeature-ha:lf-pod2:fuel": [1], + + "os-nosdn-nofeature-noha:ericsson-virtual2:fuel": [1], + + "os-nosdn-nofeature-noha:ericsson-virtual3:fuel": [1], + + "os-ovn-nofeature-noha:lf-pod1:apex": [1511.5], + + "os-nosdn-kvm-noha:huawei-virtual3:compass": [2996], + + "os-nosdn-bar-ha:huawei-virtual4:compass": [2997], + + "os-nosdn-bar-noha:huawei-virtual4:compass": [2997], + + "os-nosdn-kvm-ha:huawei-virtual4:compass": [2997], + + "os-nosdn-nofeature-ha:huawei-virtual1:compass": [2997], + + "os-nosdn-nofeature-ha:huawei-virtual3:compass": [2997], + + "os-nosdn-nofeature-ha:huawei-virtual4:compass": [2997], + + "os-odl-sfc-ha:huawei-virtual3:compass": [2997], + + "os-odl_l3-nofeature-ha:huawei-virtual3:compass": [2997], + + "os-odl_l3-nofeature-noha:huawei-virtual3:compass": [3000], + + "os-odl_l3-nofeature-ha:huawei-virtual4:compass": [3003], + + "os-nosdn-bar-noha:huawei-virtual3:compass": [3011], + + "os-nosdn-bar-ha:huawei-virtual3:compass": [3015.5], + + "os-nosdn-kvm-noha:huawei-virtual4:compass": [3019], + + "os-nosdn-nofeature-noha:huawei-virtual4:compass": [3021], + + "os-odl-sfc-ha:huawei-virtual4:compass": [3021], + + "os-nosdn-bar-ha:huawei-pod2:compass": [3022], + + "os-nosdn-bar-ha:lf-pod1:apex": [3022], + + "os-nosdn-bar-noha:lf-pod1:apex": [3022], + + "os-nosdn-calipso-noha:lf-pod1:apex": [3022], + + "os-nosdn-kvm-ha:huawei-pod2:compass": [3022], + + "os-nosdn-nofeature-ha:huawei-pod12:joid": [3022], + + "os-nosdn-nofeature-ha:huawei-pod2:compass": [3022], + + "os-nosdn-nofeature-ha:intel-pod18:joid": [3022], + + "os-nosdn-nofeature-ha:lf-pod1:apex": [3022], + + "os-nosdn-nofeature-ha:zte-pod2:daisy": [3022], + + "os-nosdn-nofeature-noha:huawei-pod12:joid": [3022], + + "os-nosdn-nofeature-noha:intel-pod18:joid": [3022], + + "os-nosdn-nofeature-noha:lf-pod1:apex": [3022], + + "os-nosdn-openbaton-ha:intel-pod18:joid": [3022], + + "os-odl-sfc-ha:huawei-pod2:compass": [3022], + + "os-odl_l3-nofeature-ha:huawei-pod2:compass": [3022], + + "os-odl_l3-nofeature-noha:huawei-virtual4:compass": [3022], + + "os-odl-sfc-noha:huawei-virtual4:compass": [3022.5], + + "os-nosdn-nofeature-noha:huawei-virtual3:compass": [3023], + + "os-odl-sfc-noha:huawei-virtual3:compass": [3023], + + "os-nosdn-nofeature-noha:huawei-virtual2:compass": [3025] + +} + + +The influence of the scenario +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. image:: images/tc011_scenario_fraser.png + :width: 800px + :alt: TC011 influence of scenario + +{ + + "os-ovn-nofeature-noha": [1511.5], + + "os-nosdn-kvm-noha": [2997], + + "os-odl-sfc-ha": [3021], + + "os-nosdn-bar-ha": [3022], + + "os-nosdn-bar-noha": [3022], + + "os-nosdn-calipso-noha": [3022], + + "os-nosdn-kvm-ha": [3022], + + "os-nosdn-nofeature-ha": [3022], + + "os-nosdn-nofeature-noha": [3022], + + "os-nosdn-openbaton-ha": [3022], + + "os-odl_l3-nofeature-ha": [3022], + + "os-odl_l3-nofeature-noha": [3022], + + "os-odl-sfc-noha": [3023] + +} + + +The influence of the POD +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. image:: images/tc011_pod_fraser.png + :width: 800px + :alt: TC011 influence of the POD + +{ + + "arm-pod6": [1], + + "ericsson-pod1": [1], + + "ericsson-virtual2": [1], + + "ericsson-virtual3": [1], + + "lf-pod2": [1], + + "huawei-virtual1": [2997], + + "huawei-virtual3": [2999], + + "huawei-virtual4": [3002], + + "huawei-pod12": [3022], + + "huawei-pod2": [3022], + + "intel-pod18": [3022], + + "lf-pod1": [3022], + + "zte-pod2": [3022], + + "huawei-virtual2": [3025] + +} diff --git a/docs/release/results/tc012-memory-read-write-bandwidth.rst b/docs/release/results/tc012-memory-read-write-bandwidth.rst index c28eb1f3c..71d69cde9 100644 --- a/docs/release/results/tc012-memory-read-write-bandwidth.rst +++ b/docs/release/results/tc012-memory-read-write-bandwidth.rst @@ -297,3 +297,217 @@ The influence of the POD Fraser release -------------- + +Test results per scenario and pod (higher is better): + +{ + + "os-nosdn-nofeature-ha:lf-pod2:fuel": [21421.795], + + "os-odl-sfc-noha:lf-pod1:apex": [21075], + + "os-odl-sfc-ha:lf-pod1:apex": [21017.44], + + "os-nosdn-bar-noha:lf-pod1:apex": [20991.46], + + "os-nosdn-bar-ha:lf-pod1:apex": [20812.405], + + "os-ovn-nofeature-noha:lf-pod1:apex": [20694.035], + + "os-nosdn-nofeature-noha:lf-pod1:apex": [20672.765], + + "os-odl-nofeature-ha:lf-pod2:fuel": [20269.65], + + "os-nosdn-calipso-noha:lf-pod1:apex": [20186.32], + + "os-odl-nofeature-noha:lf-pod1:apex": [19959.915], + + "os-nosdn-ovs-ha:lf-pod2:fuel": [19719.38], + + "os-odl-nofeature-ha:lf-pod1:apex": [19654.505], + + "os-nosdn-nofeature-ha:lf-pod1:apex": [19391.145], + + "os-nosdn-nofeature-noha:intel-pod18:joid": [19378.64], + + "os-odl-nofeature-ha:ericsson-pod1:fuel": [19103.43], + + "os-nosdn-nofeature-ha:intel-pod18:joid": [18688.695], + + "os-nosdn-openbaton-ha:intel-pod18:joid": [18557.95], + + "os-nosdn-nofeature-ha:ericsson-pod1:fuel": [17088.61], + + "os-nosdn-ovs-ha:ericsson-pod1:fuel": [17040.78], + + "os-nosdn-ovs-noha:ericsson-virtual2:fuel": [16057.235], + + "os-odl-nofeature-noha:ericsson-virtual4:fuel": [15622.355], + + "os-nosdn-nofeature-noha:ericsson-virtual2:fuel": [15422.235], + + "os-odl-sfc-ha:huawei-pod2:compass": [15403.09], + + "os-odl-nofeature-noha:ericsson-virtual2:fuel": [15141.58], + + "os-nosdn-bar-ha:huawei-pod2:compass": [14922.37], + + "os-odl_l3-nofeature-ha:huawei-pod2:compass": [14864.195], + + "os-nosdn-nofeature-ha:huawei-pod2:compass": [14856.295], + + "os-nosdn-kvm-ha:huawei-pod2:compass": [14796.035], + + "os-odl-sfc-noha:huawei-virtual4:compass": [14484.375], + + "os-nosdn-nofeature-ha:huawei-pod12:joid": [14441.955], + + "os-odl-sfc-noha:huawei-virtual3:compass": [14373], + + "os-nosdn-nofeature-noha:huawei-virtual4:compass": [14330.44], + + "os-nosdn-ovs-noha:ericsson-virtual4:fuel": [14320.305], + + "os-odl_l3-nofeature-noha:huawei-virtual3:compass": [14253.715], + + "os-nosdn-nofeature-ha:huawei-virtual4:compass": [14203.655], + + "os-nosdn-nofeature-noha:huawei-virtual3:compass": [14179.93], + + "os-odl-nofeature-ha:zte-pod2:daisy": [14177.135], + + "os-nosdn-nofeature-ha:zte-pod2:daisy": [14150.825], + + "os-nosdn-nofeature-noha:huawei-pod12:joid": [14100.87], + + "os-nosdn-bar-noha:huawei-virtual4:compass": [14033.36], + + "os-odl_l3-nofeature-noha:huawei-virtual4:compass": [13963.73], + + "os-nosdn-kvm-noha:huawei-virtual3:compass": [13874.775], + + "os-nosdn-kvm-noha:huawei-virtual4:compass": [13805.65], + + "os-odl_l3-nofeature-ha:huawei-virtual3:compass": [13754.63], + + "os-nosdn-nofeature-noha:huawei-virtual2:compass": [13702.92], + + "os-nosdn-bar-ha:huawei-virtual3:compass": [13638.115], + + "os-odl-sfc-ha:huawei-virtual3:compass": [13637.83], + + "os-odl_l3-nofeature-ha:huawei-virtual4:compass": [13635.66], + + "os-nosdn-bar-noha:huawei-virtual3:compass": [13635.58], + + "os-nosdn-bar-ha:huawei-virtual4:compass": [13544.95], + + "os-nosdn-nofeature-ha:huawei-virtual3:compass": [13514.27], + + "os-nosdn-nofeature-ha:huawei-virtual1:compass": [13496.45], + + "os-odl-sfc-ha:huawei-virtual4:compass": [13475.38], + + "os-nosdn-nofeature-noha:ericsson-virtual3:fuel": [12733.19], + + "os-nosdn-kvm-ha:huawei-virtual4:compass": [12682.805], + + "os-odl-nofeature-ha:arm-pod5:fuel": [4326.11], + + "os-nosdn-nofeature-ha:arm-pod6:fuel": [3824.13], + + "os-odl-nofeature-ha:arm-pod6:fuel": [3797.795], + + "os-nosdn-ovs-ha:arm-pod6:fuel": [3749.91] + +} + + +The influence of the scenario +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. image:: images/tc012_scenario_fraser.png + :width: 800px + :alt: TC012 influence of scenario + +{ + + "os-ovn-nofeature-noha": [20694.035], + + "os-nosdn-calipso-noha": [20186.32], + + "os-nosdn-openbaton-ha": [18557.95], + + "os-nosdn-ovs-ha": [17048.17], + + "os-odl-nofeature-noha": [16191.125], + + "os-nosdn-ovs-noha": [15790.32], + + "os-nosdn-bar-ha": [14833.97], + + "os-odl-sfc-ha": [14828.72], + + "os-odl_l3-nofeature-ha": [14801.25], + + "os-nosdn-kvm-ha": [14700.1], + + "os-nosdn-nofeature-ha": [14610.48], + + "os-nosdn-nofeature-noha": [14555.975], + + "os-odl-sfc-noha": [14508.14], + + "os-nosdn-bar-noha": [14395.22], + + "os-odl-nofeature-ha": [14231.245], + + "os-odl_l3-nofeature-noha": [14161.58], + + "os-nosdn-kvm-noha": [13845.685] + +} + + +The influence of the POD +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. image:: images/tc012_pod_fraser.png + :width: 800px + :alt: TC012 influence of the POD + +{ + + "lf-pod1": [20552.9], + + "lf-pod2": [20058.925], + + "ericsson-pod1": [18930.78], + + "intel-pod18": [18757.545], + + "ericsson-virtual4": [15389.465], + + "ericsson-virtual2": [15343.79], + + "huawei-pod2": [14870.78], + + "zte-pod2": [14157.99], + + "huawei-pod12": [14126.99], + + "huawei-virtual3": [13929.67], + + "huawei-virtual4": [13847.155], + + "huawei-virtual2": [13702.92], + + "huawei-virtual1": [13496.45], + + "ericsson-virtual3": [12733.19], + + "arm-pod5": [4326.11], + + "arm-pod6": [3809.885] + +} diff --git a/docs/release/results/tc014-cpu-processing-speed.rst b/docs/release/results/tc014-cpu-processing-speed.rst index 34d4ad0f9..a2eeb6302 100644 --- a/docs/release/results/tc014-cpu-processing-speed.rst +++ b/docs/release/results/tc014-cpu-processing-speed.rst @@ -296,3 +296,217 @@ The influence of the POD Fraser release -------------- + +Test results per scenario and pod (higher is better): + +{ + + "os-nosdn-nofeature-ha:lf-pod2:fuel": [3747.3], + + "os-nosdn-calipso-noha:lf-pod1:apex": [3727.2], + + "os-odl-nofeature-ha:lf-pod1:apex": [3726.5], + + "os-ovn-nofeature-noha:lf-pod1:apex": [3723.8], + + "os-odl-nofeature-noha:lf-pod1:apex": [3718.9], + + "os-nosdn-nofeature-noha:lf-pod1:apex": [3717.75], + + "os-nosdn-nofeature-ha:lf-pod1:apex": [3706.5], + + "os-odl-nofeature-ha:lf-pod2:fuel": [3704.9], + + "os-nosdn-ovs-ha:lf-pod2:fuel": [3687.7], + + "os-nosdn-bar-noha:lf-pod1:apex": [3635.4], + + "os-nosdn-bar-ha:lf-pod1:apex": [3632.55], + + "os-odl-sfc-noha:lf-pod1:apex": [3569], + + "os-nosdn-nofeature-noha:intel-pod18:joid": [3432.1], + + "os-odl-nofeature-ha:ericsson-pod1:fuel": [3133.85], + + "os-nosdn-ovs-ha:ericsson-pod1:fuel": [3079.8], + + "os-nosdn-nofeature-ha:intel-pod18:joid": [3074.75], + + "os-nosdn-openbaton-ha:intel-pod18:joid": [2976.2], + + "os-nosdn-nofeature-ha:zte-pod2:daisy": [2910.95], + + "os-odl-nofeature-ha:zte-pod2:daisy": [2801.1], + + "os-nosdn-ovs-noha:ericsson-virtual2:fuel": [2603], + + "os-odl-nofeature-noha:ericsson-virtual2:fuel": [2559.7], + + "os-nosdn-nofeature-noha:ericsson-virtual2:fuel": [2539.1], + + "os-odl_l3-nofeature-ha:huawei-pod2:compass": [2530.5], + + "os-nosdn-nofeature-ha:huawei-pod2:compass": [2529.4], + + "os-odl-sfc-ha:huawei-pod2:compass": [2528.9], + + "os-odl-nofeature-noha:ericsson-virtual4:fuel": [2527.8], + + "os-nosdn-bar-ha:huawei-pod2:compass": [2527.4], + + "os-nosdn-kvm-ha:huawei-pod2:compass": [2517.8], + + "os-nosdn-nofeature-noha:huawei-virtual4:compass": [2472.4], + + "os-nosdn-nofeature-ha:huawei-virtual4:compass": [2469.1], + + "os-odl-sfc-noha:huawei-virtual3:compass": [2452.05], + + "os-odl-sfc-noha:huawei-virtual4:compass": [2438.7], + + "os-odl_l3-nofeature-noha:huawei-virtual3:compass": [2418.4], + + "os-nosdn-ovs-noha:ericsson-virtual4:fuel": [2404.35], + + "os-nosdn-kvm-noha:huawei-virtual3:compass": [2391], + + "os-nosdn-kvm-noha:huawei-virtual4:compass": [2376.75], + + "os-odl_l3-nofeature-noha:huawei-virtual4:compass": [2376.2], + + "os-nosdn-nofeature-noha:huawei-virtual3:compass": [2359.45], + + "os-nosdn-bar-noha:huawei-virtual4:compass": [2353.3], + + "os-odl-sfc-ha:huawei-virtual3:compass": [2351.9], + + "os-nosdn-bar-ha:huawei-virtual3:compass": [2339.4], + + "os-odl-sfc-ha:huawei-virtual4:compass": [2335.6], + + "os-nosdn-bar-ha:huawei-virtual4:compass": [2328], + + "os-odl_l3-nofeature-ha:huawei-virtual3:compass": [2324.5], + + "os-nosdn-bar-noha:huawei-virtual3:compass": [2317.3], + + "os-nosdn-nofeature-ha:huawei-virtual3:compass": [2313.95], + + "os-odl_l3-nofeature-ha:huawei-virtual4:compass": [2308.1], + + "os-nosdn-nofeature-noha:huawei-virtual2:compass": [2299.3], + + "os-nosdn-kvm-ha:huawei-virtual4:compass": [2250.4], + + "os-nosdn-nofeature-noha:huawei-pod12:joid": [2229.7], + + "os-nosdn-nofeature-ha:huawei-pod12:joid": [2228.8], + + "os-nosdn-nofeature-ha:huawei-virtual1:compass": [2171.3], + + "os-nosdn-nofeature-noha:ericsson-virtual3:fuel": [2104.8], + + "os-nosdn-nofeature-ha:ericsson-pod1:fuel": [1961.35], + + "os-nosdn-ovs-ha:arm-pod5:fuel": [1764.2], + + "os-odl-nofeature-ha:arm-pod5:fuel": [1730.95], + + "os-nosdn-ovs-ha:arm-pod6:fuel": [715.55], + + "os-odl-nofeature-ha:arm-pod6:fuel": [715.4], + + "os-nosdn-nofeature-ha:arm-pod6:fuel": [715.25] + +} + + +The influence of the scenario +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. image:: images/tc014_scenario_fraser.png + :width: 800px + :alt: TC014 influence of scenario + +{ + + "os-nosdn-calipso-noha": [3727.2], + + "os-ovn-nofeature-noha": [3723.8], + + "os-odl-nofeature-noha": [3128.05], + + "os-nosdn-openbaton-ha": [2976.2], + + "os-nosdn-ovs-ha": [2814.5], + + "os-odl-nofeature-ha": [2801.4], + + "os-nosdn-nofeature-ha": [2649.7], + + "os-nosdn-ovs-noha": [2587.3], + + "os-odl_l3-nofeature-ha": [2528.45], + + "os-odl-sfc-ha": [2527.6], + + "os-nosdn-bar-ha": [2526.55], + + "os-nosdn-kvm-ha": [2516.95], + + "os-odl-sfc-noha": [2453.65], + + "os-nosdn-bar-noha": [2447.7], + + "os-nosdn-nofeature-noha": [2443.85], + + "os-odl_l3-nofeature-noha": [2394.3], + + "os-nosdn-kvm-noha": [2379.7] + +} + + +The influence of the POD +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. image:: images/tc014_pod_fraser.png + :width: 800px + :alt: TC014 influence of the POD + +{ + + "lf-pod2": [3737.6], + + "lf-pod1": [3702.7], + + "ericsson-pod1": [3131.6], + + "intel-pod18": [3098.1], + + "zte-pod2": [2831.4], + + "ericsson-virtual2": [2559.7], + + "huawei-pod2": [2528.9], + + "ericsson-virtual4": [2527.8], + + "huawei-virtual3": [2379.1], + + "huawei-virtual4": [2362.1], + + "huawei-virtual2": [2299.3], + + "huawei-pod12": [2229], + + "huawei-virtual1": [2171.3], + + "ericsson-virtual3": [2104.8], + + "arm-pod5": [1764.2], + + "arm-pod6": [715.4] + +} diff --git a/docs/release/results/tc069-memory-write-bandwidth.rst b/docs/release/results/tc069-memory-write-bandwidth.rst index 06e2ec922..4cd3be3b0 100644 --- a/docs/release/results/tc069-memory-write-bandwidth.rst +++ b/docs/release/results/tc069-memory-write-bandwidth.rst @@ -298,3 +298,219 @@ The influence of the POD Fraser release -------------- + +Test results per scenario and pod (higher is better): + +{ + + "os-nosdn-nofeature-noha:intel-pod18:joid": [18382.49], + + "os-nosdn-openbaton-ha:intel-pod18:joid": [16774.52], + + "os-nosdn-nofeature-ha:intel-pod18:joid": [16680.305], + + "os-nosdn-ovs-ha:arm-pod6:fuel": [11925.22], + + "os-nosdn-nofeature-ha:arm-pod6:fuel": [11895.71], + + "os-odl-nofeature-ha:arm-pod6:fuel": [11880.7], + + "os-nosdn-nofeature-ha:ericsson-pod1:fuel": [9471.095], + + "os-odl-nofeature-ha:zte-pod2:daisy": [9375.33], + + "os-nosdn-nofeature-ha:zte-pod2:daisy": [9372.95], + + "os-odl-nofeature-ha:ericsson-pod1:fuel": [9174.36], + + "os-nosdn-nofeature-noha:huawei-pod12:joid": [9051.57], + + "os-nosdn-nofeature-ha:huawei-pod12:joid": [8894.74], + + "os-odl_l3-nofeature-ha:huawei-pod2:compass": [8857.23], + + "os-nosdn-nofeature-ha:huawei-pod2:compass": [8855.8], + + "os-nosdn-bar-ha:huawei-pod2:compass": [8840.94], + + "os-odl-sfc-ha:huawei-pod2:compass": [8826.23], + + "os-nosdn-nofeature-noha:huawei-virtual4:compass": [8039.48], + + "os-nosdn-nofeature-noha:huawei-virtual2:compass": [7670.21], + + "os-nosdn-ovs-ha:arm-pod5:fuel": [7590.9], + + "os-odl-sfc-noha:huawei-virtual4:compass": [7579.625], + + "os-nosdn-bar-noha:huawei-virtual3:compass": [7511.775], + + "os-odl-nofeature-ha:arm-pod5:fuel": [7475.16], + + "os-nosdn-bar-noha:huawei-virtual4:compass": [7435.08], + + "os-nosdn-nofeature-noha:ericsson-virtual2:fuel": [7426.79], + + "os-nosdn-nofeature-ha:huawei-virtual4:compass": [7362.8], + + "os-nosdn-kvm-noha:huawei-virtual4:compass": [7263.45], + + "os-nosdn-nofeature-noha:huawei-virtual3:compass": [7262.72], + + "os-odl_l3-nofeature-noha:huawei-virtual3:compass": [7241.07], + + "os-odl-nofeature-noha:ericsson-virtual2:fuel": [7219.21], + + "os-nosdn-kvm-noha:huawei-virtual3:compass": [7174.33], + + "os-odl-sfc-noha:huawei-virtual3:compass": [7170.795], + + "os-odl-nofeature-noha:lf-pod1:apex": [7158.335], + + "os-nosdn-kvm-ha:huawei-pod2:compass": [7122.45], + + "os-odl-sfc-ha:huawei-virtual4:compass": [7104.9], + + "os-nosdn-ovs-noha:ericsson-virtual2:fuel": [7044.37], + + "os-nosdn-bar-ha:huawei-virtual3:compass": [7011.075], + + "os-nosdn-ovs-ha:ericsson-pod1:fuel": [6950.28], + + "os-nosdn-ovs-noha:ericsson-virtual4:fuel": [6918.31], + + "os-nosdn-bar-ha:huawei-virtual4:compass": [6903.11], + + "os-nosdn-nofeature-ha:lf-pod2:fuel": [6880.98], + + "os-odl-sfc-ha:lf-pod1:apex": [6863.39], + + "os-odl_l3-nofeature-ha:huawei-virtual3:compass": [6851.54], + + "os-nosdn-nofeature-noha:lf-pod1:apex": [6834.75], + + "os-nosdn-calipso-noha:lf-pod1:apex": [6833.92], + + "os-nosdn-ovs-ha:lf-pod2:fuel": [6814.68], + + "os-ovn-nofeature-noha:lf-pod1:apex": [6809.44], + + "os-odl_l3-nofeature-ha:huawei-virtual4:compass": [6784.48], + + "os-nosdn-nofeature-ha:lf-pod1:apex": [6737.64], + + "os-nosdn-bar-noha:lf-pod1:apex": [6708.61], + + "os-nosdn-bar-ha:lf-pod1:apex": [6697.2], + + "os-odl-nofeature-ha:lf-pod1:apex": [6626.51], + + "os-odl-sfc-noha:lf-pod1:apex": [6609.57], + + "os-odl-sfc-ha:huawei-virtual3:compass": [6606.87], + + "os-odl_l3-nofeature-noha:huawei-virtual4:compass": [6547.39], + + "os-odl-nofeature-ha:lf-pod2:fuel": [6465.48], + + "os-odl-nofeature-noha:ericsson-virtual4:fuel": [6413], + + "os-nosdn-kvm-ha:huawei-virtual4:compass": [6409.075], + + "os-nosdn-nofeature-ha:huawei-virtual3:compass": [6128.79], + + "os-nosdn-nofeature-noha:ericsson-virtual3:fuel": [5835.59], + + "os-nosdn-nofeature-ha:huawei-virtual1:compass": [5617.12] + +} + + +The influence of the scenario +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. image:: images/tc069_scenario_fraser.png + :width: 800px + :alt: TC069 influence of scenario + +{ + + "os-nosdn-openbaton-ha": [16774.52], + + "os-odl-nofeature-ha": [9363.69], + + "os-nosdn-nofeature-ha": [8878.01], + + "os-odl_l3-nofeature-ha": [8748.4], + + "os-odl-sfc-ha": [8708.045], + + "os-nosdn-nofeature-noha": [7426.79], + + "os-nosdn-kvm-noha": [7230.79], + + "os-odl-sfc-noha": [7224.11], + + "os-odl-nofeature-noha": [7187.84], + + "os-nosdn-ovs-noha": [7044.37], + + "os-nosdn-bar-ha": [6947.87], + + "os-odl_l3-nofeature-noha": [6895.96], + + "os-nosdn-kvm-ha": [6890.92], + + "os-nosdn-calipso-noha": [6833.92], + + "os-nosdn-ovs-ha": [6833.495], + + "os-nosdn-bar-noha": [6811.66], + + "os-ovn-nofeature-noha": [6809.44] + +} + + +The influence of the POD +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. image:: images/tc069_pod_fraser.png + :width: 800px + :alt: TC069 influence of the POD + +{ + + "intel-pod18": [16939.24], + + "arm-pod6": [11895.71], + + "zte-pod2": [9375.33], + + "ericsson-pod1": [9140.42], + + "huawei-pod12": [8993.37], + + "huawei-pod2": [8794.01], + + "huawei-virtual2": [7670.21], + + "arm-pod5": [7479.32], + + "ericsson-virtual2": [7219.21], + + "huawei-virtual4": [7059.045], + + "huawei-virtual3": [7023.57], + + "lf-pod2": [6834.7], + + "lf-pod1": [6775.27], + + "ericsson-virtual4": [6522.86], + + "ericsson-virtual3": [5835.59], + + "huawei-virtual1": [5617.12] + +} diff --git a/docs/release/results/tc082-context-switches-under-load.rst b/docs/release/results/tc082-context-switches-under-load.rst index d8a9f5493..92bc69907 100644 --- a/docs/release/results/tc082-context-switches-under-load.rst +++ b/docs/release/results/tc082-context-switches-under-load.rst @@ -70,8 +70,6 @@ The influence of the scenario :width: 800px :alt: TC082 influence of scenario -the influence of the scenario - { "os-nosdn-nofeature-ha": [505], @@ -88,8 +86,6 @@ The influence of the POD :width: 800px :alt: TC082 influence of the POD -the influence of the POD - { "huawei-pod12": [316], @@ -127,3 +123,65 @@ the influence of the POD Fraser release -------------- + +Test results per scenario and pod (lower is better): + +{ + + "os-nosdn-nofeature-ha:zte-pod2:daisy": [306.5], + + "os-nosdn-nofeature-ha:lf-pod2:fuel": [337.5], + + "os-nosdn-nofeature-ha:intel-pod18:joid": [343.5], + + "os-nosdn-nofeature-ha:huawei-pod12:joid": [399], + + "os-nosdn-nofeature-ha:lf-pod1:apex": [454], + + "os-nosdn-nofeature-ha:huawei-pod2:compass": [544.5], + + "os-nosdn-nofeature-ha:huawei-virtual4:compass": [1138], + + "os-nosdn-nofeature-ha:ericsson-pod1:fuel": [1305], + + "os-nosdn-nofeature-ha:huawei-virtual3:compass": [1433], + + "os-nosdn-nofeature-ha:huawei-virtual1:compass": [1470], + + "os-nosdn-nofeature-ha:arm-pod6:fuel": [1738.5] + +} + + +The influence of the POD +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. image:: images/tc082_pod_fraser.png + :width: 800px + :alt: TC082 influence of the POD + +{ + + "zte-pod2": [306.5], + + "lf-pod2": [337.5], + + "intel-pod18": [343.5], + + "huawei-pod12": [399], + + "lf-pod1": [454], + + "huawei-pod2": [544.5], + + "huawei-virtual4": [1138], + + "ericsson-pod1": [1305], + + "huawei-virtual3": [1433], + + "huawei-virtual1": [1470], + + "arm-pod6": [1738.5] + +} diff --git a/docs/release/results/tc083-network-throughput-between-vm.rst b/docs/release/results/tc083-network-throughput-between-vm.rst index f846571a5..0389eaafe 100644 --- a/docs/release/results/tc083-network-throughput-between-vm.rst +++ b/docs/release/results/tc083-network-throughput-between-vm.rst @@ -70,8 +70,6 @@ The influence of the scenario :width: 800px :alt: TC083 influence of scenario -the influence of the scenario - { "os-nosdn-nofeature-ha": [1109.12], @@ -88,8 +86,6 @@ The influence of the POD :width: 800px :alt: TC083 influence of the POD -the influence of the POD - { "lf-pod1": [2204.42], @@ -127,3 +123,65 @@ the influence of the POD Fraser release -------------- + +Test results per scenario and pod (higher is better): + +{ + + "os-nosdn-nofeature-ha:lf-pod2:fuel": [1893.39], + + "os-nosdn-nofeature-ha:zte-pod2:daisy": [1543.995], + + "os-nosdn-nofeature-ha:lf-pod1:apex": [1480.86], + + "os-nosdn-nofeature-ha:intel-pod18:joid": [1417.015], + + "os-nosdn-nofeature-ha:huawei-pod12:joid": [1028.55], + + "os-nosdn-nofeature-ha:huawei-pod2:compass": [1007.65], + + "os-nosdn-nofeature-ha:ericsson-pod1:fuel": [811.795], + + "os-nosdn-nofeature-ha:huawei-virtual4:compass": [552.95], + + "os-nosdn-nofeature-ha:arm-pod6:fuel": [227.655], + + "os-nosdn-nofeature-ha:huawei-virtual1:compass": [216.63], + + "os-nosdn-nofeature-ha:huawei-virtual3:compass": [59.255] + +} + + +The influence of the POD +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. image:: images/tc083_pod_fraser.png + :width: 800px + :alt: TC083 influence of the POD + +{ + + "lf-pod2": [1893.39], + + "zte-pod2": [1543.995], + + "lf-pod1": [1480.86], + + "intel-pod18": [1417.015], + + "huawei-pod12": [1028.55], + + "huawei-pod2": [1007.65], + + "ericsson-pod1": [811.795], + + "huawei-virtual4": [552.95], + + "arm-pod6": [227.655], + + "huawei-virtual1": [216.63], + + "huawei-virtual3": [59.255] + +} diff --git a/docs/testing/user/userguide/13-nsb-installation.rst b/docs/testing/user/userguide/13-nsb-installation.rst index 1f6c79b0b..3e0ed0bfb 100644 --- a/docs/testing/user/userguide/13-nsb-installation.rst +++ b/docs/testing/user/userguide/13-nsb-installation.rst @@ -1058,40 +1058,8 @@ IxLoad Config ``pod_ixia.yaml`` - .. code-block:: yaml - - nodes: - - - name: trafficgen_1 - role: IxNet - ip: 1.2.1.1 #ixia machine ip - user: user - password: r00t - key_filename: /root/.ssh/id_rsa - tg_config: - ixchassis: "1.2.1.7" #ixia chassis ip - tcl_port: "8009" # tcl server port - lib_path: "/opt/ixia/ixos-api/8.01.0.2/lib/ixTcl1.0" - root_dir: "/opt/ixia/ixos-api/8.01.0.2/" - py_bin_path: "/opt/ixia/ixload/8.01.106.3/bin/" - py_lib_path: "/opt/ixia/ixnetwork/8.01.1029.14/lib/PythonApi" - dut_result_dir: "/mnt/ixia" - version: 8.1 - interfaces: - xe0: # logical name from topology.yaml and vnfd.yaml - vpci: "2:5" # Card:port - driver: "none" - dpdk_port_num: 0 - local_ip: "152.16.100.20" - netmask: "255.255.0.0" - local_mac: "00:98:10:64:14:00" - xe1: # logical name from topology.yaml and vnfd.yaml - vpci: "2:6" # [(Card, port)] - driver: "none" - dpdk_port_num: 1 - local_ip: "152.40.40.20" - netmask: "255.255.0.0" - local_mac: "00:98:28:28:14:00" + .. literalinclude:: code/pod_ixia.yaml + :language: console for sriov/ovs_dpdk pod files, please refer to above Standalone Virtualization for ovs-dpdk/sriov configuration @@ -1113,10 +1081,10 @@ IxLoad IxNetwork --------- -1. Software needed: ``IxNetworkAPI<ixnetwork verson>Linux64.bin.tgz`` - (Download from ixia support site) - Install - ``IxNetworkAPI<ixnetwork verson>Linux64.bin.tgz`` -2. Update pod_ixia.yaml file with ixia details. +IxNetwork testcases use IxNetwork API Python Bindings module, which is +installed as part of the requirements of the project. + +1. Update ``pod_ixia.yaml`` file with ixia details. .. code-block:: console @@ -1124,44 +1092,12 @@ IxNetwork Config pod_ixia.yaml - .. code-block:: yaml - - nodes: - - - name: trafficgen_1 - role: IxNet - ip: 1.2.1.1 #ixia machine ip - user: user - password: r00t - key_filename: /root/.ssh/id_rsa - tg_config: - ixchassis: "1.2.1.7" #ixia chassis ip - tcl_port: "8009" # tcl server port - lib_path: "/opt/ixia/ixos-api/8.01.0.2/lib/ixTcl1.0" - root_dir: "/opt/ixia/ixos-api/8.01.0.2/" - py_bin_path: "/opt/ixia/ixload/8.01.106.3/bin/" - py_lib_path: "/opt/ixia/ixnetwork/8.01.1029.14/lib/PythonApi" - dut_result_dir: "/mnt/ixia" - version: 8.1 - interfaces: - xe0: # logical name from topology.yaml and vnfd.yaml - vpci: "2:5" # Card:port - driver: "none" - dpdk_port_num: 0 - local_ip: "152.16.100.20" - netmask: "255.255.0.0" - local_mac: "00:98:10:64:14:00" - xe1: # logical name from topology.yaml and vnfd.yaml - vpci: "2:6" # [(Card, port)] - driver: "none" - dpdk_port_num: 1 - local_ip: "152.40.40.20" - netmask: "255.255.0.0" - local_mac: "00:98:28:28:14:00" + .. literalinclude:: code/pod_ixia.yaml + :language: console for sriov/ovs_dpdk pod files, please refer to above Standalone Virtualization for ovs-dpdk/sriov configuration -3. Start IxNetwork TCL Server +2. Start IxNetwork TCL Server You will also need to configure the IxNetwork machine to start the IXIA IxNetworkTclServer. This can be started like so: @@ -1170,6 +1106,5 @@ IxNetwork ``Start->Programs->Ixia->IxNetwork->IxNetwork 7.21.893.14 GA->IxNetworkTclServer`` (or ``IxNetworkApiServer``) -4. Execute testcase in samplevnf folder e.g. +3. Execute testcase in samplevnf folder e.g. ``<repo>/samples/vnf_samples/nsut/vfw/tc_baremetal_rfc2544_ipv4_1rule_1flow_64B_ixia.yaml`` - diff --git a/docs/testing/user/userguide/14-nsb-operation.rst b/docs/testing/user/userguide/14-nsb-operation.rst index d157914a9..851c6528e 100644 --- a/docs/testing/user/userguide/14-nsb-operation.rst +++ b/docs/testing/user/userguide/14-nsb-operation.rst @@ -84,6 +84,116 @@ In this example we have ``TRex xe0 <-> xe0 VNF xe1 <-> xe0 UDP_Replay`` downlink_0: - xe0 + +Availability zone +^^^^^^^^^^^^^^^^^ + +The configuration of the availability zone is requred in cases where location +of exact compute host/group of compute hosts needs to be specified for SampleVNF +or traffic generator in the heat test case. If this is the case, please follow +the instructions below. + +.. _`Create a host aggregate`: + +1. Create a host aggregate in the OpenStack and add the available compute hosts + into the aggregate group. + + .. note:: Change the ``<AZ_NAME>`` (availability zone name), ``<AGG_NAME>`` + (host aggregate name) and ``<HOST>`` (host name of one of the compute) in the + commands below. + + .. code-block:: bash + + # create host aggregate + openstack aggregate create --zone <AZ_NAME> --property availability_zone=<AZ_NAME> <AGG_NAME> + # show available hosts + openstack compute service list --service nova-compute + # add selected host into the host aggregate + openstack aggregate add host <AGG_NAME> <HOST> + +2. To specify the OpenStack location (the exact compute host or group of the hosts) + of SampleVNF or traffic generator in the heat test case, the ``availability_zone`` server + configuration option should be used. For example: + + .. note:: The ``<AZ_NAME>`` (availability zone name) should be changed according + to the name used during the host aggregate creation steps above. + + .. code-block:: yaml + + context: + name: yardstick + image: yardstick-samplevnfs + ... + servers: + vnf__0: + ... + availability_zone: <AZ_NAME> + ... + tg__0: + ... + availability_zone: <AZ_NAME> + ... + networks: + ... + +There are two example of SampleVNF scale out test case which use the availability zone +feature to specify the exact location of scaled VNFs and traffic generators. + +Those are: + +.. code-block:: console + + <repo>/samples/vnf_samples/nsut/prox/tc_prox_heat_context_l2fwd_multiflow-2-scale-out.yaml + <repo>/samples/vnf_samples/nsut/vfw/tc_heat_rfc2544_ipv4_1rule_1flow_64B_trex_scale_out.yaml + +.. note:: This section describes the PROX scale-out testcase, but the same + procedure is used for the vFW test case. + +1. Before running the scale-out test case, make sure the host aggregates are + configured in the OpenStack environment. To check this, run the following + command: + + .. code-block:: console + + # show configured host aggregates (example) + openstack aggregate list + +----+------+-------------------+ + | ID | Name | Availability Zone | + +----+------+-------------------+ + | 4 | agg0 | AZ_NAME_0 | + | 5 | agg1 | AZ_NAME_1 | + +----+------+-------------------+ + +2. If no host aggregates are configured, please use `steps above`__ to + configure them. + +__ `Create a host aggregate`_ + + +3. Run the SampleVNF PROX scale-out test case, specifying the availability + zone of each VNF and traffic generator as a task arguments. + + .. note:: The ``az_0`` and ``az_1`` should be changed according to the host + aggregates created in the OpenStack. + + .. code-block:: console + + yardstick -d task start\ + <repo>/samples/vnf_samples/nsut/prox/tc_prox_heat_context_l2fwd_multiflow-2-scale-out.yaml\ + --task-args='{ + "num_vnfs": 4, "availability_zone": { + "vnf_0": "az_0", "tg_0": "az_1", + "vnf_1": "az_0", "tg_1": "az_1", + "vnf_2": "az_0", "tg_2": "az_1", + "vnf_3": "az_0", "tg_3": "az_1" + } + }' + + ``num_vnfs`` specifies how many VNFs are going to be deployed in the + ``heat`` contexts. ``vnf_X`` and ``tg_X`` arguments configure the + availability zone where the VNF and traffic generator is going to be deployed. + + Collectd KPIs ------------- @@ -242,7 +352,7 @@ Baremetal file: /etc/yardstick/nodes/pod.yaml Scale-Out --------------------- +--------- VNFs performance data with scale-out helps diff --git a/docs/testing/user/userguide/code/pod_ixia.yaml b/docs/testing/user/userguide/code/pod_ixia.yaml new file mode 100644 index 000000000..4ab56fe4e --- /dev/null +++ b/docs/testing/user/userguide/code/pod_ixia.yaml @@ -0,0 +1,31 @@ +nodes: +- + name: trafficgen_1 + role: IxNet + ip: 1.2.1.1 #ixia machine ip + user: user + password: r00t + key_filename: /root/.ssh/id_rsa + tg_config: + ixchassis: "1.2.1.7" #ixia chassis ip + tcl_port: "8009" # tcl server port + lib_path: "/opt/ixia/ixos-api/8.01.0.2/lib/ixTcl1.0" + root_dir: "/opt/ixia/ixos-api/8.01.0.2/" + py_bin_path: "/opt/ixia/ixload/8.01.106.3/bin/" + dut_result_dir: "/mnt/ixia" + version: 8.1 + interfaces: + xe0: # logical name from topology.yaml and vnfd.yaml + vpci: "2:5" # Card:port + driver: "none" + dpdk_port_num: 0 + local_ip: "152.16.100.20" + netmask: "255.255.0.0" + local_mac: "00:98:10:64:14:00" + xe1: # logical name from topology.yaml and vnfd.yaml + vpci: "2:6" # [(Card, port)] + driver: "none" + dpdk_port_num: 1 + local_ip: "152.40.40.20" + netmask: "255.255.0.0" + local_mac: "00:98:28:28:14:00" diff --git a/etc/yardstick/nodes/pod.yaml.nsb.sample.ixia b/etc/yardstick/nodes/pod.yaml.nsb.sample.ixia index 57a83058e..1f755dc4e 100644 --- a/etc/yardstick/nodes/pod.yaml.nsb.sample.ixia +++ b/etc/yardstick/nodes/pod.yaml.nsb.sample.ixia @@ -26,7 +26,6 @@ nodes: lib_path: "/opt/ixia/ixos-api/8.01.0.2/lib/ixTcl1.0" root_dir: "/opt/ixia/ixos-api/8.01.0.2/" py_bin_path: "/opt/ixia/ixload/8.01.106.3/bin/" - py_lib_path: "/opt/ixia/ixnetwork/8.01.1029.14/lib/PythonApi" dut_result_dir: "/mnt/ixia" version: 8.1 interfaces: diff --git a/etc/yardstick/nodes/standalone/ixia_correlated_template.yaml b/etc/yardstick/nodes/standalone/ixia_correlated_template.yaml index 7250c4ce3..ef63ea04c 100644 --- a/etc/yardstick/nodes/standalone/ixia_correlated_template.yaml +++ b/etc/yardstick/nodes/standalone/ixia_correlated_template.yaml @@ -26,13 +26,12 @@ nodes: user: {{gen.user}} password: {{gen.password}} key_filename: {{gen.key_filename}} - tg_config: + tg_config: ixchassis: "{{gen.tg_config.ixchassis}}" #ixia chassis ip tcl_port: "{{gen.tg_config.tcl_port}}" # tcl server port lib_path: "{{gen.tg_config.lib_path}}" root_dir: "{{gen.tg_config.root_dir}}" py_bin_path: "{{gen.tg_config.py_bin_path}}" - py_lib_path: "{{gen.tg_config.py_lib_path}}" dut_result_dir: "{{gen.tg_config.dut_result_dir}}" version: "{{gen.tg_config.version}}" interfaces: diff --git a/etc/yardstick/nodes/standalone/ixia_template.yaml b/etc/yardstick/nodes/standalone/ixia_template.yaml index 617a65162..98ed8c5c2 100644 --- a/etc/yardstick/nodes/standalone/ixia_template.yaml +++ b/etc/yardstick/nodes/standalone/ixia_template.yaml @@ -32,7 +32,6 @@ nodes: lib_path: "{{gen.tg_config.lib_path}}" root_dir: "{{gen.tg_config.root_dir}}" py_bin_path: "{{gen.tg_config.py_bin_path}}" - py_lib_path: "{{gen.tg_config.py_lib_path}}" dut_result_dir: "{{gen.tg_config.dut_result_dir}}" version: "{{gen.tg_config.version}}" interfaces: diff --git a/requirements.txt b/requirements.txt index 02545de1d..2cde89c0e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,6 +14,7 @@ PTable==0.9.2 # BSD (3 clause); OSI Approved BSD License ansible==2.4.2 # GPLv3; OSI Approved GNU General Public License v3 or later (GPLv3+) backport-ipaddress==0.1; python_version <= "2.7" # OSI Approved Python Software Foundation License chainmap==1.0.2 # Python Software Foundation License; OSI Approved Python Software Foundation License +cmd2==0.8.6 # MIT License; OSI Approved MIT License django==1.8.17 # BSD; OSI Approved BSD License # NOTE(ralonsoh): django must be bumped to 1.11.8; consider the migration notes [1] # [1] https://docs.djangoproject.com/ja/1.11/ref/templates/upgrading/ @@ -26,6 +27,7 @@ flask==0.11.1 # BSD; OSI Approved BSD License functools32==3.2.3.post2; python_version <= "2.7" # PSF license futures==3.1.1;python_version=='2.7' # BSD; OSI Approved BSD License influxdb==4.1.1 # MIT License; OSI Approved MIT License +IxNetwork==8.40.1124.9 # MIT License; OSI Approved MIT License jinja2schema==0.1.4 # OSI Approved BSD License keystoneauth1==3.1.0 # OSI Approved Apache Software License kubernetes==3.0.0a1 # OSI Approved Apache Software License @@ -37,6 +39,7 @@ os-client-config==1.28.0 # OSI Approved Apache Software License osc-lib==1.7.0 # OSI Approved Apache Software License oslo.config==4.11.1 # OSI Approved Apache Software License oslo.i18n==3.17.0 # OSI Approved Apache Software License +oslo.messaging===5.36.0 # OSI Approved Apache Software License oslo.privsep===1.22.1 # OSI Approved Apache Software License oslo.serialization==2.20.1 # OSI Approved Apache Software License oslo.utils==3.28.0 # OSI Approved Apache Software License diff --git a/samples/vnf_samples/nsut/acl/acl_1rule.yaml b/samples/vnf_samples/nsut/acl/acl_1rule.yaml index b184a29e2..49066e924 100644 --- a/samples/vnf_samples/nsut/acl/acl_1rule.yaml +++ b/samples/vnf_samples/nsut/acl/acl_1rule.yaml @@ -11,37 +11,29 @@ # 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. - -access-list1: - acl: - access-list-entries: - - ace: - ace-oper-data: - match-counter: 0 - actions: drop,count - matches: - destination-ipv4-network: 152.16.40.20/24 - destination-port-range: - lower-port: 0 - upper-port: 65535 - source-ipv4-network: 0.0.0.0/0 - source-port-range: - lower-port: 0 - upper-port: 65535 - rule-name: rule1588 - - ace: - ace-oper-data: - match-counter: 0 - actions: drop,count - matches: - destination-ipv4-network: 0.0.0.0/0 - destination-port-range: - lower-port: 0 - upper-port: 65535 - source-ipv4-network: 152.16.100.20/24 - source-port-range: - lower-port: 0 - upper-port: 65535 - rule-name: rule1589 - acl-name: sample-ipv4-acl - acl-type: ipv4-acl +--- +access-list-entries: + - + actions: [drop,count] + matches: + destination-ipv4-network: 152.16.40.20/24 + destination-port-range: + lower-port: 0 + upper-port: 65535 + source-ipv4-network: 0.0.0.0/0 + source-port-range: + lower-port: 0 + upper-port: 65535 + rule-name: rule1588 + - + actions: [drop,count] + matches: + destination-ipv4-network: 0.0.0.0/0 + destination-port-range: + lower-port: 0 + upper-port: 65535 + source-ipv4-network: 152.16.100.20/24 + source-port-range: + lower-port: 0 + upper-port: 65535 + rule-name: rule1589 diff --git a/samples/vnf_samples/nsut/acl/acl_rules.yaml b/samples/vnf_samples/nsut/acl/acl_rules.yaml index b184a29e2..49066e924 100644 --- a/samples/vnf_samples/nsut/acl/acl_rules.yaml +++ b/samples/vnf_samples/nsut/acl/acl_rules.yaml @@ -11,37 +11,29 @@ # 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. - -access-list1: - acl: - access-list-entries: - - ace: - ace-oper-data: - match-counter: 0 - actions: drop,count - matches: - destination-ipv4-network: 152.16.40.20/24 - destination-port-range: - lower-port: 0 - upper-port: 65535 - source-ipv4-network: 0.0.0.0/0 - source-port-range: - lower-port: 0 - upper-port: 65535 - rule-name: rule1588 - - ace: - ace-oper-data: - match-counter: 0 - actions: drop,count - matches: - destination-ipv4-network: 0.0.0.0/0 - destination-port-range: - lower-port: 0 - upper-port: 65535 - source-ipv4-network: 152.16.100.20/24 - source-port-range: - lower-port: 0 - upper-port: 65535 - rule-name: rule1589 - acl-name: sample-ipv4-acl - acl-type: ipv4-acl +--- +access-list-entries: + - + actions: [drop,count] + matches: + destination-ipv4-network: 152.16.40.20/24 + destination-port-range: + lower-port: 0 + upper-port: 65535 + source-ipv4-network: 0.0.0.0/0 + source-port-range: + lower-port: 0 + upper-port: 65535 + rule-name: rule1588 + - + actions: [drop,count] + matches: + destination-ipv4-network: 0.0.0.0/0 + destination-port-range: + lower-port: 0 + upper-port: 65535 + source-ipv4-network: 152.16.100.20/24 + source-port-range: + lower-port: 0 + upper-port: 65535 + rule-name: rule1589 diff --git a/samples/vnf_samples/nsut/acl/acl_worstcaserules.yaml b/samples/vnf_samples/nsut/acl/acl_worstcaserules.yaml index b184a29e2..6f09bb848 100644 --- a/samples/vnf_samples/nsut/acl/acl_worstcaserules.yaml +++ b/samples/vnf_samples/nsut/acl/acl_worstcaserules.yaml @@ -11,37 +11,33 @@ # 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. - -access-list1: - acl: - access-list-entries: - - ace: - ace-oper-data: - match-counter: 0 - actions: drop,count - matches: - destination-ipv4-network: 152.16.40.20/24 - destination-port-range: - lower-port: 0 - upper-port: 65535 - source-ipv4-network: 0.0.0.0/0 - source-port-range: - lower-port: 0 - upper-port: 65535 - rule-name: rule1588 - - ace: - ace-oper-data: - match-counter: 0 - actions: drop,count - matches: - destination-ipv4-network: 0.0.0.0/0 - destination-port-range: - lower-port: 0 - upper-port: 65535 - source-ipv4-network: 152.16.100.20/24 - source-port-range: - lower-port: 0 - upper-port: 65535 - rule-name: rule1589 - acl-name: sample-ipv4-acl - acl-type: ipv4-acl +--- +access-list-entries: + - + ace-oper-data: + match-counter: 0 + actions: [drop,count] + matches: + destination-ipv4-network: 152.16.40.20/24 + destination-port-range: + lower-port: 0 + upper-port: 65535 + source-ipv4-network: 0.0.0.0/0 + source-port-range: + lower-port: 0 + upper-port: 65535 + rule-name: rule1588 + - + ace-oper-data: + match-counter: 0 + actions: [drop,count] + matches: + destination-ipv4-network: 0.0.0.0/0 + destination-port-range: + lower-port: 0 + upper-port: 65535 + source-ipv4-network: 152.16.100.20/24 + source-port-range: + lower-port: 0 + upper-port: 65535 + rule-name: rule1589 diff --git a/samples/vnf_samples/nsut/prox/prox-tg-topology-scale-out.yaml b/samples/vnf_samples/nsut/prox/prox-tg-topology-scale-out.yaml new file mode 100644 index 000000000..5f01ecb7e --- /dev/null +++ b/samples/vnf_samples/nsut/prox/prox-tg-topology-scale-out.yaml @@ -0,0 +1,53 @@ +# Copyright (c) 2018 Intel 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_vnfs = get(extra_args, 'num_vnfs', 1) %} +--- +nsd:nsd-catalog: + nsd: + - id: prox-tg-topology + name: prox-tg-topology + short-name: prox-tg-topology + description: prox-tg-topology + constituent-vnfd: +{% for vnf_num in range(num_vnfs|int) %} + - member-vnf-index: '{{ (vnf_num * 2) + 1 }}' + vnfd-id-ref: tg__{{ vnf_num }} + VNF model: ../../vnf_descriptors/tg_prox_tpl.yaml + - member-vnf-index: '{{ (vnf_num * 2) + 2 }}' + vnfd-id-ref: vnf__{{ vnf_num }} + VNF model: ../../vnf_descriptors/prox_vnf.yaml +{% endfor %} + vld: +{% for vnf_num in range(num_vnfs|int) %} + - id: uplink_{{ vnf_num }} + name: tg__{{ vnf_num }} to vnf__{{ vnf_num }} link {{ (vnf_num * 2) + 1 }} + type: ELAN + vnfd-connection-point-ref: + - member-vnf-index-ref: '{{ (vnf_num * 2) + 1 }}' + vnfd-connection-point-ref: xe0 + vnfd-id-ref: tg__{{ vnf_num }} + - member-vnf-index-ref: '{{ (vnf_num * 2) + 2 }}' + vnfd-connection-point-ref: xe0 + vnfd-id-ref: vnf__{{ vnf_num }} + - id: downlink_{{ vnf_num }} + name: vnf__{{ vnf_num }} to tg__{{ vnf_num }} link {{ (vnf_num * 2) + 2 }} + type: ELAN + vnfd-connection-point-ref: + - member-vnf-index-ref: '{{ (vnf_num * 2) + 1 }}' + vnfd-connection-point-ref: xe1 + vnfd-id-ref: vnf__{{ vnf_num }} + - member-vnf-index-ref: '{{ (vnf_num * 2) + 2 }}' + vnfd-connection-point-ref: xe1 + vnfd-id-ref: tg__{{ vnf_num }} +{% endfor %} diff --git a/samples/vnf_samples/nsut/prox/tc_prox_heat_context_l2fwd_multiflow-2-scale-out.yaml b/samples/vnf_samples/nsut/prox/tc_prox_heat_context_l2fwd_multiflow-2-scale-out.yaml new file mode 100644 index 000000000..88581d2da --- /dev/null +++ b/samples/vnf_samples/nsut/prox/tc_prox_heat_context_l2fwd_multiflow-2-scale-out.yaml @@ -0,0 +1,113 @@ +# Copyright (c) 2018 Intel 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_vnfs = num_vnfs or 1 %} +{% set availability_zone = availability_zone or {} %} +--- +schema: "yardstick:task:0.1" +scenarios: +- + type: NSPerf + traffic_profile: ../../traffic_profiles/prox_binsearch.yaml + topology: prox-tg-topology-scale-out.yaml + extra_args: + num_vnfs: {{ num_vnfs }} + + nodes: +{% for vnf_num in range(num_vnfs|int) %} + tg__{{ vnf_num }}: tg_{{ vnf_num }}.yardstick + vnf__{{ vnf_num }}: vnf_{{ vnf_num }}.yardstick +{% endfor %} + + options: +{% for vnf_num in range(num_vnfs|int) %} + vnf__{{ vnf_num }}: + prox_path: /opt/nsb_bin/prox + prox_config: "configs/handle_l2fwd_multiflow-2.cfg" + prox_args: + "-t": "" + + tg__{{ vnf_num }}: + prox_path: /opt/nsb_bin/prox + prox_config: "configs/gen_l2fwd_multiflow-2.cfg" + prox_args: + "-e": "" + "-t": "" +{% endfor %} + + runner: + type: Duration + # we kill after duration, independent of test duration, so set this high + duration: 300 + +context: + name: yardstick + image: yardstick-samplevnfs + user: ubuntu + flavor: + vcpus: 8 + ram: 20480 + disk: 10 + extra_specs: + hw:cpu_sockets: 1 + hw:cpu_cores: 8 + hw:cpu_threads: 1 + placement_groups: + pgrp1: + policy: "availability" + + servers: +{% for vnf_num in range(num_vnfs|int) %} + vnf_{{ vnf_num }}: + floating_ip: true + placement: "pgrp1" + {% if 'vnf_%s'|format(vnf_num) in availability_zone %} + availability_zone: "{{ availability_zone['vnf_%s'|format(vnf_num)] }}" + {% endif %} + network_ports: + mgmt: + - mgmt + uplink_{{ vnf_num }}: + - xe0 + downlink_{{ vnf_num }}: + - xe1 + tg_{{ vnf_num }}: + floating_ip: true + placement: "pgrp1" + {% if 'tg_%s'|format(vnf_num) in availability_zone %} + availability_zone: "{{ availability_zone['tg_%s'|format(vnf_num)] }}" + {% endif %} + network_ports: + mgmt: + - mgmt + uplink_{{ vnf_num }}: + - xe0 + downlink_{{ vnf_num }}: + - xe1 +{% endfor %} + + networks: + mgmt: + cidr: '10.0.1.0/24' +{% for vnf_num in range(num_vnfs|int) %} + uplink_{{ vnf_num }}: + cidr: '10.0.{{ (vnf_num * 2) + 2 }}.0/24' + gateway_ip: 'null' + port_security_enabled: False + enable_dhcp: 'false' + downlink_{{ vnf_num }}: + cidr: '10.0.{{ (vnf_num * 2) + 3 }}.0/24' + gateway_ip: 'null' + port_security_enabled: False + enable_dhcp: 'false' +{% endfor %} diff --git a/samples/vnf_samples/nsut/vfw/acl_1rule.yaml b/samples/vnf_samples/nsut/vfw/acl_1rule.yaml index 6753645ba..f7569b32c 100644 --- a/samples/vnf_samples/nsut/vfw/acl_1rule.yaml +++ b/samples/vnf_samples/nsut/vfw/acl_1rule.yaml @@ -11,37 +11,29 @@ # 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. - -access-list1: - acl: - access-list-entries: - - ace: - ace-oper-data: - match-counter: 0 - actions: drop,count - matches: - destination-ipv4-network: 152.16.0.0/24 - destination-port-range: - lower-port: 0 - upper-port: 65535 - source-ipv4-network: 0.0.0.0/0 - source-port-range: - lower-port: 0 - upper-port: 65535 - rule-name: rule1588 - - ace: - ace-oper-data: - match-counter: 0 - actions: drop,count - matches: - destination-ipv4-network: 0.0.0.0/0 - destination-port-range: - lower-port: 0 - upper-port: 65535 - source-ipv4-network: 152.16.0.0/24 - source-port-range: - lower-port: 0 - upper-port: 65535 - rule-name: rule1589 - acl-name: sample-ipv4-acl - acl-type: ipv4-acl +--- +access-list-entries: + - + actions: [drop,count] + matches: + destination-ipv4-network: 152.16.0.0/24 + destination-port-range: + lower-port: 0 + upper-port: 65535 + source-ipv4-network: 0.0.0.0/0 + source-port-range: + lower-port: 0 + upper-port: 65535 + rule-name: rule1588 + - + actions: [drop,count] + matches: + destination-ipv4-network: 0.0.0.0/0 + destination-port-range: + lower-port: 0 + upper-port: 65535 + source-ipv4-network: 152.16.0.0/24 + source-port-range: + lower-port: 0 + upper-port: 65535 + rule-name: rule1589 diff --git a/samples/vnf_samples/nsut/vfw/tc_heat_rfc2544_ipv4_1rule_1flow_64B_trex_scale_out.yaml b/samples/vnf_samples/nsut/vfw/tc_heat_rfc2544_ipv4_1rule_1flow_64B_trex_scale_out.yaml new file mode 100644 index 000000000..f1408931f --- /dev/null +++ b/samples/vnf_samples/nsut/vfw/tc_heat_rfc2544_ipv4_1rule_1flow_64B_trex_scale_out.yaml @@ -0,0 +1,114 @@ +# Copyright (c) 2018 Intel 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_vnfs = num_vnfs or 1 %} +{% set availability_zone = availability_zone or {} %} +--- +schema: yardstick:task:0.1 +scenarios: +- type: NSPerf + traffic_profile: ../../traffic_profiles/ipv4_throughput_scale_out.yaml + topology: vfw_tg_topology_scale_out.yaml + extra_args: + num_vnfs: {{ num_vnfs }} + nodes: +{% for vnf_num in range(num_vnfs|int) %} + tg__{{ vnf_num }}: tg_{{ vnf_num }}.yardstick + vnf__{{ vnf_num }}: vnf_{{ vnf_num }}.yardstick +{% endfor %} + options: + framesize: + uplink: {64B: 100} + downlink: {64B: 100} + flow: + src_ip: +{% for vnf_num in range(num_vnfs|int) %} + - {'tg__{{ vnf_num }}': 'xe0'} +{% endfor %} + dst_ip: +{% for vnf_num in range(num_vnfs|int) %} + - {'tg__{{ vnf_num }}': 'xe1'} +{% endfor %} + count: 1 + traffic_type: 4 + rfc2544: + allowed_drop_rate: 0.0001 - 0.0001 +{% for vnf_num in range(num_vnfs|int) %} + vnf__{{ vnf_num }}: + rules: acl_1rule.yaml + vnf_config: {lb_config: 'SW', lb_count: 1, worker_config: '1C/1T', worker_threads: 1} +{% endfor %} + runner: + type: Iteration + iterations: 10 + interval: 35 +context: + # put node context first, so we don't HEAT deploy if node has errors + name: yardstick + image: yardstick-samplevnfs + flavor: + vcpus: 10 + ram: 20480 + disk: 6 + extra_specs: + hw:cpu_sockets: 1 + hw:cpu_cores: 10 + hw:cpu_threads: 1 + user: ubuntu + placement_groups: + pgrp1: + policy: "availability" + servers: +{% for vnf_num in range(num_vnfs|int) %} + vnf_{{ vnf_num }}: + floating_ip: true + placement: "pgrp1" + {% if 'vnf_%s'|format(vnf_num) in availability_zone %} + availability_zone: "{{ availability_zone['vnf_%s'|format(vnf_num)] }}" + {% endif %} + network_ports: + mgmt: + - mgmt + uplink_{{ vnf_num }}: + - xe0 + downlink_{{ vnf_num }}: + - xe1 + tg_{{ vnf_num }}: + floating_ip: true + placement: "pgrp1" + {% if 'tg_%s'|format(vnf_num) in availability_zone %} + availability_zone: "{{ availability_zone['tg_%s'|format(vnf_num)] }}" + {% endif %} + network_ports: + mgmt: + - mgmt + uplink_{{ vnf_num }}: + - xe0 + downlink_{{ vnf_num }}: + - xe1 +{% endfor %} + networks: + mgmt: + cidr: '10.0.1.0/24' +{% for vnf_num in range(num_vnfs|int) %} + uplink_{{ vnf_num }}: + cidr: '10.0.{{ (vnf_num * 2) + 2 }}.0/24' + gateway_ip: 'null' + port_security_enabled: False + enable_dhcp: 'false' + downlink_{{ vnf_num }}: + cidr: '10.0.{{ (vnf_num * 2) + 3 }}.0/24' + gateway_ip: 'null' + port_security_enabled: False + enable_dhcp: 'false' +{% endfor %} diff --git a/samples/vnf_samples/nsut/vfw/vfw_tg_topology_scale_out.yaml b/samples/vnf_samples/nsut/vfw/vfw_tg_topology_scale_out.yaml new file mode 100644 index 000000000..8bd01b7f2 --- /dev/null +++ b/samples/vnf_samples/nsut/vfw/vfw_tg_topology_scale_out.yaml @@ -0,0 +1,53 @@ +# Copyright (c) 2018 Intel 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_vnfs = get(extra_args, 'num_vnfs', 1) %} +--- +nsd:nsd-catalog: + nsd: + - id: 3tg-topology + name: 3tg-topology + short-name: 3tg-topology + description: 3tg-topology + constituent-vnfd: +{% for vnf_num in range(num_vnfs|int) %} + - member-vnf-index: '{{ (vnf_num * 2) + 1 }}' + vnfd-id-ref: tg__{{ vnf_num }} + VNF model: ../../vnf_descriptors/tg_rfc2544_tpl.yaml + - member-vnf-index: '{{ (vnf_num * 2) + 2 }}' + vnfd-id-ref: vnf__{{ vnf_num }} + VNF model: ../../vnf_descriptors/vfw_vnf.yaml +{% endfor %} + vld: +{% for vnf_num in range(num_vnfs|int) %} + - id: uplink_{{ vnf_num }} + name: tg__{{ vnf_num }} to vnf__{{ vnf_num }} link {{ (vnf_num * 2) + 1 }} + type: ELAN + vnfd-connection-point-ref: + - member-vnf-index-ref: '{{ (vnf_num * 2) + 1 }}' + vnfd-connection-point-ref: xe0 + vnfd-id-ref: tg__{{ vnf_num }} + - member-vnf-index-ref: '{{ (vnf_num * 2) + 2 }}' + vnfd-connection-point-ref: xe0 + vnfd-id-ref: vnf__{{ vnf_num }} + - id: downlink_{{ vnf_num }} + name: vnf__{{ vnf_num }} to tg__{{ vnf_num }} link {{ (vnf_num * 2) + 2 }} + type: ELAN + vnfd-connection-point-ref: + - member-vnf-index-ref: '{{ (vnf_num * 2) + 2 }}' + vnfd-connection-point-ref: xe1 + vnfd-id-ref: vnf__{{ vnf_num }} + - member-vnf-index-ref: '{{ (vnf_num * 2) + 1 }}' + vnfd-connection-point-ref: xe1 + vnfd-id-ref: tg__{{ vnf_num }} +{% endfor %} diff --git a/samples/vnf_samples/traffic_profiles/ipv4_throughput_scale_out.yaml b/samples/vnf_samples/traffic_profiles/ipv4_throughput_scale_out.yaml new file mode 100644 index 000000000..71e9e817b --- /dev/null +++ b/samples/vnf_samples/traffic_profiles/ipv4_throughput_scale_out.yaml @@ -0,0 +1,102 @@ +# Copyright (c) 2018 Intel 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. + +# flow definition for ACL tests - 1K flows - ipv4 only +# +# the number of flows defines the widest range of parameters +# for example if srcip_range=1.0.0.1-1.0.0.255 and dst_ip_range=10.0.0.1-10.0.1.255 +# and it should define only 16 flows +# +#there is assumption that packets generated will have a random sequences of following addresses pairs +# in the packets +# 1. src=1.x.x.x(x.x.x =random from 1..255) dst=10.x.x.x (random from 1..512) +# 2. src=1.x.x.x(x.x.x =random from 1..255) dst=10.x.x.x (random from 1..512) +# ... +# 512. src=1.x.x.x(x.x.x =random from 1..255) dst=10.x.x.x (random from 1..512) +# +# not all combination should be filled +# Any other field with random range will be added to flow definition +# +# the example.yaml provides all possibilities for traffic generation +# +# the profile defines a public and private side to make limited traffic correlation +# between private and public side same way as it is made by IXIA solution. +# +{% set num_vnfs = get(extra_args, 'num_vnfs', 1) %} +--- +schema: "nsb:traffic_profile:0.1" +name: rfc2544 +description: Traffic profile to run RFC2544 latency +traffic_profile: + traffic_type : RFC2544Profile # defines traffic behavior - constant or look for highest possible throughput + frame_rate : 100 # pc of linerate + # that specifies a range (e.g. ipv4 address, port) +{% for vnf_num in range(num_vnfs|int) %} +uplink_{{ vnf_num }}: + ipv4: + id: {{ (vnf_num * 2) + 1 }} + outer_l2: + framesize: + 64B: "{{get(imix, 'imix.uplink.64B', '0') }}" + 128B: "{{get(imix, 'imix.uplink.128B', '0') }}" + 256B: "{{get(imix, 'imix.uplink.256B', '0') }}" + 373B: "{{get(imix, 'imix.uplink.373B', '0') }}" + 512B: "{{get(imix, 'imix.uplink.512B', '0') }}" + 570B: "{{get(imix, 'imix.uplink.570B', '0') }}" + 1024B: "{{get(imix, 'imix.uplink.1024B', '0') }}" + 1280B: "{{get(imix, 'imix.uplink.1280B', '0') }}" + 1400B: "{{get(imix, 'imix.uplink.1400B', '0') }}" + 1500B: "{{get(imix, 'imix.uplink.1500B', '0') }}" + 1518B: "{{get(imix, 'imix.uplink.1518B', '0') }}" + + outer_l3v4: + proto: "udp" + srcip4: "{{get(flow, 'flow.src_ip_{{ vnf_num }}', '10.0.2.1-10.0.2.255') }}" + dstip4: "{{get(flow, 'flow.dst_ip_{{ vnf_num }}', '10.0.3.1-10.0.3.255') }}" + count: "{{get(flow, 'flow.count', '1') }}" + ttl: 32 + dscp: 0 + outer_l4: + srcport: "{{get(flow, 'flow.src_port_{{ vnf_num }}', '1234-4321') }}" + dstport: "{{get(flow, 'flow.dst_port_{{ vnf_num }}', '2001-4001') }}" + count: "{{get(flow, 'flow.count', '1') }}" +downlink_{{ vnf_num }}: + ipv4: + id: {{ (vnf_num * 2) + 2 }} + outer_l2: + framesize: + 64B: "{{ get(imix, 'imix.downlink.64B', '0') }}" + 128B: "{{ get(imix, 'imix.downlink.128B', '0') }}" + 256B: "{{ get(imix, 'imix.downlink.256B', '0') }}" + 373b: "{{ get(imix, 'imix.downlink.373B', '0') }}" + 512B: "{{ get(imix, 'imix.downlink.512B', '0') }}" + 570B: "{{get(imix, 'imix.downlink.570B', '0') }}" + 1024B: "{{get(imix, 'imix.downlink.1024B', '0') }}" + 1280B: "{{get(imix, 'imix.downlink.1280B', '0') }}" + 1400B: "{{get(imix, 'imix.downlink.1400B', '0') }}" + 1500B: "{{get(imix, 'imix.downlink.1500B', '0') }}" + 1518B: "{{get(imix, 'imix.downlink.1518B', '0') }}" + + outer_l3v4: + proto: "udp" + srcip4: "{{get(flow, 'flow.dst_ip_{{ vnf_num }}', '10.0.3.1-10.0.3.255') }}" + dstip4: "{{get(flow, 'flow.src_ip_{{ vnf_num }}', '10.0.2.1-10.0.2.255') }}" + count: "{{get(flow, 'flow.count', '1') }}" + ttl: 32 + dscp: 0 + outer_l4: + srcport: "{{get(flow, 'flow.dst_port_{{ vnf_num }}', '1234-4321') }}" + dstport: "{{get(flow, 'flow.src_port_{{ vnf_num }}', '2001-4001') }}" + count: "{{get(flow, 'flow.count', '1') }}" +{% endfor %} diff --git a/samples/vnf_samples/vnf_descriptors/ixia_rfc2544_tpl.yaml b/samples/vnf_samples/vnf_descriptors/ixia_rfc2544_tpl.yaml index 9b2a152f3..aca7c2102 100644 --- a/samples/vnf_samples/vnf_descriptors/ixia_rfc2544_tpl.yaml +++ b/samples/vnf_samples/vnf_descriptors/ixia_rfc2544_tpl.yaml @@ -29,7 +29,6 @@ vnfd:vnfd-catalog: tcl_port: '{{tg_config.tcl_port}}' # tcl server port lib_path: '{{tg_config.lib_path}}' root_dir: '{{tg_config.root_dir}}' - py_lib_path: '{{tg_config.py_lib_path}}' py_bin_path: '{{tg_config.py_bin_path}}' dut_result_dir: '{{tg_config.dut_result_dir}}' version: '{{tg_config.version}}' diff --git a/samples/vnf_samples/vnf_descriptors/tg_ixload.yaml b/samples/vnf_samples/vnf_descriptors/tg_ixload.yaml index ad4953fce..0324bb8fd 100644 --- a/samples/vnf_samples/vnf_descriptors/tg_ixload.yaml +++ b/samples/vnf_samples/vnf_descriptors/tg_ixload.yaml @@ -29,7 +29,6 @@ vnfd:vnfd-catalog: tcl_port: '{{tg_config.tcl_port}}' # tcl server port lib_path: '{{tg_config.lib_path}}' root_dir: '{{tg_config.root_dir}}' - py_lib_path: '{{tg_config.py_lib_path}}' py_bin_path: '{{tg_config.py_bin_path}}' dut_result_dir: '{{tg_config.dut_result_dir}}' version: '{{tg_config.version}}' diff --git a/samples/vnf_samples/vnf_descriptors/tg_ixload_4port.yaml b/samples/vnf_samples/vnf_descriptors/tg_ixload_4port.yaml index ffbfbdec0..def8cdcef 100644 --- a/samples/vnf_samples/vnf_descriptors/tg_ixload_4port.yaml +++ b/samples/vnf_samples/vnf_descriptors/tg_ixload_4port.yaml @@ -28,7 +28,6 @@ vnfd:vnfd-catalog: tcl_port: '{{tg_config.tcl_port}}' # tcl server port lib_path: '{{tg_config.lib_path}}' root_dir: '{{tg_config.root_dir}}' - py_lib_path: '{{tg_config.py_lib_path}}' dut_result_dir: '{{tg_config.dut_result_dir}}' version: '{{tg_config.version}}' vdu: diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py index 5935abbac..95b2b8a4e 100644 --- a/tests/unit/__init__.py +++ b/tests/unit/__init__.py @@ -12,9 +12,15 @@ # See the License for the specific language governing permissions and
# limitations under the License.
-from yardstick import tests
+import sys
+
+import mock
+from yardstick import tests
# NOTE(ralonsoh): to be removed. Replace all occurrences of
# tests.unit.STL_MOCKS with yardstick.tests.STL_MOCKS
STL_MOCKS = tests.STL_MOCKS
+
+mock_stl = mock.patch.dict(sys.modules, tests.STL_MOCKS)
+mock_stl.start()
diff --git a/tests/unit/network_services/helpers/test_dpdkbindnic_helper.py b/tests/unit/network_services/helpers/test_dpdkbindnic_helper.py index 367072e84..9d94e3d0b 100644 --- a/tests/unit/network_services/helpers/test_dpdkbindnic_helper.py +++ b/tests/unit/network_services/helpers/test_dpdkbindnic_helper.py @@ -17,9 +17,7 @@ import unittest import os -from yardstick.error import IncorrectConfig, SSHError -from yardstick.error import IncorrectNodeSetup -from yardstick.error import IncorrectSetup +from yardstick.common import exceptions from yardstick.network_services.helpers.dpdkbindnic_helper import DpdkInterface from yardstick.network_services.helpers.dpdkbindnic_helper import DpdkNode from yardstick.network_services.helpers.dpdkbindnic_helper import DpdkBindHelper @@ -142,12 +140,13 @@ class TestDpdkInterface(unittest.TestCase): def test_probe_missing_values_negative(self): mock_dpdk_node = mock.Mock() - mock_dpdk_node.netdevs.values.side_effect = IncorrectNodeSetup + mock_dpdk_node.netdevs.values.side_effect = ( + exceptions.IncorrectNodeSetup(error_msg='')) interface = {'local_mac': '0a:de:ad:be:ef:f5'} dpdk_intf = DpdkInterface(mock_dpdk_node, interface) - with self.assertRaises(IncorrectConfig): + with self.assertRaises(exceptions.IncorrectConfig): dpdk_intf.probe_missing_values() @@ -213,7 +212,7 @@ class TestDpdkNode(unittest.TestCase): def test_check(self): def update(): if not mock_force_rebind.called: - raise IncorrectConfig + raise exceptions.IncorrectConfig(error_msg='') interfaces[0]['virtual-interface'].update({ 'vpci': '0000:01:02.1', @@ -244,11 +243,11 @@ class TestDpdkNode(unittest.TestCase): mock_ssh_helper = mock.Mock() mock_ssh_helper.execute.return_value = 0, '', '' - mock_intf_type().check.side_effect = SSHError + mock_intf_type().check.side_effect = exceptions.SSHError dpdk_node = DpdkNode(NAME, self.INTERFACES, mock_ssh_helper) - with self.assertRaises(IncorrectSetup): + with self.assertRaises(exceptions.IncorrectSetup): dpdk_node.check() def test_probe_netdevs(self): diff --git a/tests/unit/network_services/helpers/test_samplevnf_helper.py b/tests/unit/network_services/helpers/test_samplevnf_helper.py index 3b6c89d3a..4145b46d0 100644 --- a/tests/unit/network_services/helpers/test_samplevnf_helper.py +++ b/tests/unit/network_services/helpers/test_samplevnf_helper.py @@ -227,7 +227,7 @@ class TestMultiPortConfig(unittest.TestCase): mock.Mock(return_value={'link_config': 0, 'arp_config': '', 'arp_config6': '', 'actions': '', 'arp_route_tbl': '', 'arp_route_tbl6': '', - 'rules': ''}) + 'flows': ''}) opnfv_vnf.port_pair_list = [("xe0", "xe1")] self.assertIsNotNone(opnfv_vnf.generate_script(self.VNFD)) opnfv_vnf.lb_config = 'HW' @@ -253,66 +253,6 @@ class TestMultiPortConfig(unittest.TestCase): opnfv_vnf.generate_rule_config = mock.Mock() self.assertIsNotNone(opnfv_vnf.generate_script_data()) - def test_generate_rule_config(self): - topology_file = mock.Mock() - config_tpl = mock.Mock() - tmp_file = mock.Mock() - vnfd_mock = mock.MagicMock() - opnfv_vnf = samplevnf_helper.MultiPortConfig( - topology_file, config_tpl, tmp_file, vnfd_mock) - opnfv_vnf.get_config_tpl_data = mock.MagicMock() - opnfv_vnf.socket = 0 - opnfv_vnf.start_core = 0 - opnfv_vnf.update_write_parser = mock.MagicMock() - opnfv_vnf.generate_script_data = \ - mock.Mock(return_value={'link_config': 0, 'arp_config': '', - 'arp_config6': '', 'actions': '', - 'rules': ''}) - opnfv_vnf.port_pair_list = [("xe0", "xe1")] - opnfv_vnf.get_port_pairs = mock.Mock() - opnfv_vnf.vnf_type = 'ACL' - opnfv_vnf.get_ports_gateway = mock.Mock(return_value=u'1.1.1.1') - opnfv_vnf.get_netmask_gateway = mock.Mock( - return_value=u'255.255.255.0') - opnfv_vnf.get_ports_gateway6 = mock.Mock(return_value=u'1.1.1.1') - opnfv_vnf.get_netmask_gateway6 = mock.Mock( - return_value=u'255.255.255.0') - opnfv_vnf.txrx_pipeline = '' - opnfv_vnf.vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] - opnfv_vnf.interfaces = opnfv_vnf.vnfd['vdu'][0]['external-interface'] - opnfv_vnf.rules = '' - self.assertIsNotNone(opnfv_vnf.generate_rule_config()) - opnfv_vnf.rules = 'new' - self.assertIsNotNone(opnfv_vnf.generate_rule_config()) - - def test_generate_action_config(self): - topology_file = mock.Mock() - config_tpl = mock.Mock() - tmp_file = mock.Mock() - vnfd_mock = mock.MagicMock() - opnfv_vnf = samplevnf_helper.MultiPortConfig( - topology_file, config_tpl, tmp_file, vnfd_mock) - opnfv_vnf.get_config_tpl_data = mock.MagicMock() - opnfv_vnf.socket = 0 - opnfv_vnf.start_core = 0 - opnfv_vnf.update_write_parser = mock.MagicMock() - opnfv_vnf.generate_script_data = \ - mock.Mock(return_value={'link_config': 0, 'arp_config': '', - 'arp_config6': '', 'actions': '', - 'rules': ''}) - opnfv_vnf.port_pair_list = [("xe0", "xe1")] - opnfv_vnf.get_port_pairs = mock.Mock() - opnfv_vnf.vnf_type = 'VFW' - opnfv_vnf.get_ports_gateway = mock.Mock(return_value=u'1.1.1.1') - opnfv_vnf.get_netmask_gateway = mock.Mock( - return_value=u'255.255.255.0') - opnfv_vnf.get_ports_gateway6 = mock.Mock(return_value=u'1.1.1.1') - opnfv_vnf.get_netmask_gateway6 = mock.Mock( - return_value=u'255.255.255.0') - opnfv_vnf.txrx_pipeline = '' - opnfv_vnf.rules = '' - self.assertIsNotNone(opnfv_vnf.generate_action_config()) - def test_generate_arp_config6(self): topology_file = mock.Mock() config_tpl = mock.Mock() diff --git a/tests/unit/network_services/nfvi/test_resource.py b/tests/unit/network_services/nfvi/test_resource.py index f5f7f0fe7..741f9a6cc 100644 --- a/tests/unit/network_services/nfvi/test_resource.py +++ b/tests/unit/network_services/nfvi/test_resource.py @@ -19,6 +19,8 @@ import unittest from yardstick.network_services.nfvi.resource import ResourceProfile from yardstick.network_services.nfvi import resource, collectd +from yardstick.common.exceptions import ResourceCommandError +from yardstick.common.exceptions import SSHError class TestResourceProfile(unittest.TestCase): @@ -128,8 +130,31 @@ class TestResourceProfile(unittest.TestCase): self.assertEqual(val, ('error', 'Invalid', '', '')) def test__start_collectd(self): - self.assertIsNone( - self.resource_profile._start_collectd(self.ssh_mock, "/opt/nsb_bin")) + ssh_mock = mock.Mock() + ssh_mock.execute = mock.Mock(return_value=(0, "", "")) + self.assertIsNone(self.resource_profile._start_collectd(ssh_mock, + "/opt/nsb_bin")) + + ssh_mock.execute = mock.Mock(side_effect=SSHError) + with self.assertRaises(SSHError): + self.resource_profile._start_collectd(ssh_mock, "/opt/nsb_bin") + + ssh_mock.execute = mock.Mock(return_value=(1, "", "")) + self.assertIsNone(self.resource_profile._start_collectd(ssh_mock, + "/opt/nsb_bin")) + + def test__start_rabbitmq(self): + ssh_mock = mock.Mock() + ssh_mock.execute = mock.Mock(return_value=(0, "RabbitMQ", "")) + self.assertIsNone(self.resource_profile._start_rabbitmq(ssh_mock)) + + ssh_mock.execute = mock.Mock(return_value=(0, "", "")) + with self.assertRaises(ResourceCommandError): + self.resource_profile._start_rabbitmq(ssh_mock) + + ssh_mock.execute = mock.Mock(return_value=(1, "", "")) + with self.assertRaises(ResourceCommandError): + self.resource_profile._start_rabbitmq(ssh_mock) def test__prepare_collectd_conf(self): self.assertIsNone( @@ -154,11 +179,12 @@ class TestResourceProfile(unittest.TestCase): def test_initiate_systemagent(self): self.resource_profile._start_collectd = mock.Mock() + self.resource_profile._start_rabbitmq = mock.Mock() self.assertIsNone( self.resource_profile.initiate_systemagent("/opt/nsb_bin")) def test_initiate_systemagent_raise(self): - self.resource_profile._start_collectd = mock.Mock(side_effect=RuntimeError) + self.resource_profile._start_rabbitmq = mock.Mock(side_effect=RuntimeError) with self.assertRaises(RuntimeError): self.resource_profile.initiate_systemagent("/opt/nsb_bin") diff --git a/tests/unit/network_services/vnf_generic/vnf/test_acl_vnf.py b/tests/unit/network_services/vnf_generic/vnf/test_acl_vnf.py index 2971b55eb..c21071826 100644 --- a/tests/unit/network_services/vnf_generic/vnf/test_acl_vnf.py +++ b/tests/unit/network_services/vnf_generic/vnf/test_acl_vnf.py @@ -16,10 +16,13 @@ import unittest import mock import os +import re +import copy from tests.unit import STL_MOCKS from tests.unit.network_services.vnf_generic.vnf.test_base import mock_ssh from yardstick.common import utils +from yardstick.common import exceptions STLClient = mock.MagicMock() @@ -28,7 +31,9 @@ stl_patch.start() if stl_patch: from yardstick.network_services.vnf_generic.vnf.acl_vnf import AclApproxVnf + from yardstick.network_services.vnf_generic.vnf.base import VnfdHelper from yardstick.network_services.nfvi.resource import ResourceProfile + from yardstick.network_services.vnf_generic.vnf.acl_vnf import AclApproxSetupEnvSetupEnvHelper TEST_FILE_YAML = 'nsb_test_case.yaml' @@ -310,7 +315,6 @@ class TestAclApproxVnf(unittest.TestCase): acl_approx_vnf._run() acl_approx_vnf.ssh_helper.run.assert_called_once() - @mock.patch("yardstick.network_services.vnf_generic.vnf.acl_vnf.YangModel") @mock.patch.object(utils, 'find_relative_file') @mock.patch("yardstick.network_services.vnf_generic.vnf.sample_vnf.Context") @mock.patch(SSH_HELPER) @@ -345,3 +349,169 @@ class TestAclApproxVnf(unittest.TestCase): acl_approx_vnf.dpdk_devbind = "dpdk-devbind.py" acl_approx_vnf._resource_collect_stop = mock.Mock() self.assertIsNone(acl_approx_vnf.terminate()) + + +class TestAclApproxSetupEnvSetupEnvHelper(unittest.TestCase): + + ACL_CONFIG = {"access-list-entries": [{ + "actions": [ + "count", + {"fwd": { + "port": 0 + } + } + ], + "matches": { + "destination-ipv4-network": "152.16.0.0/24", + "destination-port-range": { + "lower-port": 0, + "upper-port": 65535 + }, + "source-ipv4-network": "0.0.0.0/0", + "source-port-range": { + "lower-port": 0, + "upper-port": 65535 + }, + "protocol-mask": 255, + "protocol": 127, + "priority": 1 + }, + "rule-name": "rule1588" + } + ]} + + def test_get_default_flows(self): + """Check if default ACL SampleVNF CLI commands are + generated correctly""" + ssh_helper = mock.Mock() + vnfd_helper = VnfdHelper({'vdu': [ + {'external-interface': [ + { + 'virtual-interface': { + 'local_ip': '152.16.100.19', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'dst_ip': '152.16.100.20', + 'vld_id': 'uplink_0', + 'ifname': 'xe0', + }, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0' + }, + { + 'virtual-interface': { + 'local_ip': '152.16.40.19', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'dst_ip': '152.16.40.20', + 'vld_id': 'downlink_0', + 'ifname': 'xe1', + }, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1' + } + ]} + ]}) + setup_helper = AclApproxSetupEnvSetupEnvHelper(vnfd_helper, ssh_helper, None) + self.check_acl_commands(setup_helper.get_flows_config(), [ + # format: (<cli pattern>, <number of expected matches>) + ("^p action add [0-9]+ accept$", 2), + ("^p action add [0-9]+ count$", 2), + ("^p action add [0-9]+ fwd 1$", 1), + ("^p action add [0-9]+ fwd 0$", 1), + ("^p acl add 1 152.16.100.0 24 152.16.40.0 24 0 65535 0 65535 0 0 [0-9]+$", 1), + ("^p acl add 1 152.16.40.0 24 152.16.100.0 24 0 65535 0 65535 0 0 [0-9]+$", 1), + ("^p acl applyruleset$", 1) + ]) + + @mock.patch.object(AclApproxSetupEnvSetupEnvHelper, 'get_default_flows') + def test_get_flows_config(self, get_default_flows): + """Check if provided ACL config can be converted to + ACL SampleVNF CLI commands correctly""" + ssh_helper = mock.Mock() + setup_helper = AclApproxSetupEnvSetupEnvHelper(None, ssh_helper, None) + get_default_flows.return_value = ({}, []) + self.check_acl_commands(setup_helper.get_flows_config(self.ACL_CONFIG), [ + # format: (<cli pattern>, <number of expected matches>) + ("^p action add [0-9]+ count$", 1), + ("^p action add [0-9]+ fwd 0$", 1), + ("^p acl add 1 0.0.0.0 0 152.16.0.0 24 0 65535 0 65535 127 0 [0-9]+$", 1), + ("^p acl applyruleset$", 1) + ]) + + @mock.patch.object(AclApproxSetupEnvSetupEnvHelper, 'get_default_flows') + def test_get_flows_config_invalid_action(self, get_default_flows): + """Check if incorrect ACL config fails to convert + to ACL SampleVNF CLI commands""" + ssh_helper = mock.Mock() + setup_helper = AclApproxSetupEnvSetupEnvHelper(None, ssh_helper, None) + get_default_flows.return_value = ({}, []) + # duplicate config and add invald action + acl_config = copy.deepcopy(self.ACL_CONFIG) + acl_config['access-list-entries'][0]["actions"].append({"xnat": {}}) + self.assertRaises(exceptions.AclUknownActionTemplate, + setup_helper.get_flows_config, acl_config) + + @mock.patch.object(AclApproxSetupEnvSetupEnvHelper, 'get_default_flows') + def test_get_flows_config_invalid_action_param(self, get_default_flows): + """Check if ACL config with invalid action parameter fails to convert + to ACL SampleVNF CLI commands""" + ssh_helper = mock.Mock() + setup_helper = AclApproxSetupEnvSetupEnvHelper(None, ssh_helper, None) + get_default_flows.return_value = ({}, []) + # duplicate config and add action with invalid parameter + acl_config = copy.deepcopy(self.ACL_CONFIG) + acl_config['access-list-entries'][0]["actions"].append( + {"nat": {"xport": 0}}) + self.assertRaises(exceptions.AclMissingActionArguments, + setup_helper.get_flows_config, acl_config) + + def check_acl_commands(self, config, expected_cli_patterns): + """Check if expected ACL CLI commands (given as a list of patterns, + `expected_cli_patterns` parameter) present in SampleVNF ACL + configuration (given as a multiline string, `config` parameter)""" + # Example of expected config: + # --------------------------- + # p action add 1 accept + # p action add 1 fwd 1 + # p action add 2 accept + # p action add 2 count + # p action add 2 fwd 0 + # p acl add 1 152.16.100.0 24 152.16.40.0 24 0 65535 0 65535 0 0 1 + # p acl add 1 152.16.40.0 24 152.16.100.0 24 0 65535 0 65535 0 0 2 + # p acl applyruleset + # --------------------------- + # NOTE: The config above consists of actions ids, which are actually + # unknown (generated at runtime), thus it's incorrect just to compare + # the example ACL config above with the configuration returned by + # get_flows_config() function. It's more correct to use CLI patterns + # (RE) to find the required SampleVNF CLI commands in the multiline + # string (SampleVNF ACL configuration). + for pattern, num_of_match in expected_cli_patterns: + # format: (<cli pattern>, <number of expected matches>) + result = re.findall(pattern, config, re.MULTILINE) + self.assertEqual(len(result), num_of_match) + + @mock.patch('yardstick.network_services.vnf_generic.vnf.sample_vnf.open') + @mock.patch.object(utils, 'find_relative_file') + @mock.patch('yardstick.network_services.vnf_generic.vnf.sample_vnf.MultiPortConfig') + @mock.patch.object(utils, 'open_relative_file') + def test_build_config(self, *args): + vnfd_helper = mock.Mock() + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.vnf_cfg = {'lb_config': 'HW'} + scenario_helper.options = {} + scenario_helper.all_options = {} + + acl_approx_setup_helper = AclApproxSetupEnvSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + acl_approx_setup_helper.get_flows_config = mock.Mock() + acl_approx_setup_helper.ssh_helper.provision_tool = mock.Mock(return_value='tool_path') + acl_approx_setup_helper.ssh_helper.all_ports = mock.Mock() + acl_approx_setup_helper.vnfd_helper.port_nums = mock.Mock(return_value=[0, 1]) + expected = 'sudo tool_path -p 0x3 -f /tmp/acl_config -s /tmp/acl_script --hwlb 3' + self.assertEqual(acl_approx_setup_helper.build_config(), expected) + acl_approx_setup_helper.get_flows_config.assert_called_once() diff --git a/tests/unit/network_services/vnf_generic/vnf/test_base.py b/tests/unit/network_services/vnf_generic/vnf/test_base.py index 664373f8f..9ef6473f0 100644 --- a/tests/unit/network_services/vnf_generic/vnf/test_base.py +++ b/tests/unit/network_services/vnf_generic/vnf/test_base.py @@ -215,9 +215,11 @@ class TestGenericVNF(unittest.TestCase): with self.assertRaises(TypeError) as exc: # pylint: disable=abstract-class-instantiated base.GenericVNF('vnf1', VNFD['vnfd:vnfd-catalog']['vnfd'][0]) - msg = ("Can't instantiate abstract class GenericVNF with abstract " - "methods collect_kpi, instantiate, scale, terminate, " - "wait_for_instantiate") + + msg = ("Can't instantiate abstract class GenericVNF with abstract methods " + "collect_kpi, instantiate, scale, start_collect, " + "stop_collect, terminate, wait_for_instantiate") + self.assertEqual(msg, str(exc.exception)) diff --git a/tests/unit/network_services/vnf_generic/vnf/test_cgnapt_vnf.py b/tests/unit/network_services/vnf_generic/vnf/test_cgnapt_vnf.py index edaa0ad7e..c549da8f9 100644 --- a/tests/unit/network_services/vnf_generic/vnf/test_cgnapt_vnf.py +++ b/tests/unit/network_services/vnf_generic/vnf/test_cgnapt_vnf.py @@ -20,7 +20,7 @@ import mock from tests.unit import STL_MOCKS from tests.unit.network_services.vnf_generic.vnf.test_base import mock_ssh - +from yardstick.common import utils STLClient = mock.MagicMock() stl_patch = mock.patch.dict("sys.modules", STL_MOCKS) @@ -79,6 +79,28 @@ link 1 up with self.assertRaises(NotImplementedError): helper.scale() + @mock.patch('yardstick.network_services.vnf_generic.vnf.sample_vnf.open') + @mock.patch.object(utils, 'find_relative_file') + @mock.patch('yardstick.network_services.vnf_generic.vnf.sample_vnf.MultiPortConfig') + @mock.patch.object(utils, 'open_relative_file') + def test_build_config(self, *args): + vnfd_helper = mock.Mock() + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.vnf_cfg = {'lb_config': 'HW'} + scenario_helper.options = {} + scenario_helper.all_options = {} + + cgnat_approx_setup_helper = CgnaptApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + cgnat_approx_setup_helper.ssh_helper.provision_tool = mock.Mock(return_value='tool_path') + cgnat_approx_setup_helper.ssh_helper.all_ports = mock.Mock() + cgnat_approx_setup_helper.vnfd_helper.port_nums = mock.Mock(return_value=[0, 1]) + expected = 'sudo tool_path -p 0x3 -f /tmp/cgnapt_config -s /tmp/cgnapt_script --hwlb 3' + self.assertEqual(cgnat_approx_setup_helper.build_config(), expected) + @mock.patch("yardstick.network_services.vnf_generic.vnf.sample_vnf.Process") class TestCgnaptApproxVnf(unittest.TestCase): diff --git a/tests/unit/network_services/vnf_generic/vnf/test_sample_vnf.py b/tests/unit/network_services/vnf_generic/vnf/test_sample_vnf.py index eb59c2837..edffa1c12 100644 --- a/tests/unit/network_services/vnf_generic/vnf/test_sample_vnf.py +++ b/tests/unit/network_services/vnf_generic/vnf/test_sample_vnf.py @@ -572,6 +572,7 @@ class TestDpdkVnfSetupEnvHelper(unittest.TestCase): ssh_helper = mock.Mock() scenario_helper = mock.Mock() scenario_helper.vnf_cfg = {} + scenario_helper.options = {} scenario_helper.all_options = {} dpdk_setup_helper = DpdkVnfSetupEnvHelper(vnfd_helper, ssh_helper, scenario_helper) @@ -583,6 +584,7 @@ class TestDpdkVnfSetupEnvHelper(unittest.TestCase): self.assertGreaterEqual(mock_multi_port_config.generate_config.call_count, 1) self.assertGreaterEqual(mock_multi_port_config.generate_script.call_count, 1) + scenario_helper.options = {'rules': 'fake_file'} scenario_helper.vnf_cfg = {'file': 'fake_file'} dpdk_setup_helper = DpdkVnfSetupEnvHelper(vnfd_helper, ssh_helper, scenario_helper) mock_open_rf.side_effect = mock.mock_open(read_data='fake_data') @@ -590,7 +592,7 @@ class TestDpdkVnfSetupEnvHelper(unittest.TestCase): result = dpdk_setup_helper.build_config() - mock_open_rf.assert_called_once() + mock_open_rf.assert_called() self.assertEqual(result, expected) self.assertGreaterEqual(ssh_helper.upload_config_file.call_count, 2) self.assertGreaterEqual(mock_find.call_count, 1) @@ -607,12 +609,15 @@ class TestDpdkVnfSetupEnvHelper(unittest.TestCase): dpdk_setup_helper.CFG_SCRIPT = 'script' dpdk_setup_helper.pipeline_kwargs = {} dpdk_setup_helper.all_ports = [0, 1, 2] + dpdk_setup_helper.scenario_helper.vnf_cfg = {'lb_config': 'HW', + 'worker_threads': 1} expected = { 'cfg_file': 'config', 'script': 'script', 'port_mask_hex': '0x3', 'tool_path': 'tool_path', + 'hwlb': ' --hwlb 1', } dpdk_setup_helper._build_pipeline_kwargs() self.assertDictEqual(dpdk_setup_helper.pipeline_kwargs, expected) @@ -1714,6 +1719,64 @@ class TestSampleVnf(unittest.TestCase): self.assertIsNone(sample_vnf.instantiate(scenario_cfg, {})) self.assertEqual(sample_vnf.nfvi_context, context2) + 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}}} + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + sample_vnf = SampleVNF('vnf__0', vnfd) + sample_vnf._update_collectd_options(scenario_cfg, context_cfg) + self.assertEqual(sample_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] + sample_vnf = SampleVNF('vnf1', vnfd) + sample_vnf._update_options(options2, options1) + self.assertEqual(options2, expected) + @mock.patch("yardstick.network_services.vnf_generic.vnf.sample_vnf.time") @mock.patch("yardstick.ssh.SSH") def test_wait_for_instantiate_empty_queue(self, ssh, *args): diff --git a/tests/unit/network_services/vnf_generic/vnf/test_tg_rfc2544_ixia.py b/tests/unit/network_services/vnf_generic/vnf/test_tg_rfc2544_ixia.py index 61fc012bc..146c6c96a 100644 --- a/tests/unit/network_services/vnf_generic/vnf/test_tg_rfc2544_ixia.py +++ b/tests/unit/network_services/vnf_generic/vnf/test_tg_rfc2544_ixia.py @@ -11,7 +11,6 @@ # 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 os @@ -19,41 +18,57 @@ import mock import six import unittest -from tests.unit import STL_MOCKS - -STLClient = mock.MagicMock() -stl_patch = mock.patch.dict("sys.modules", STL_MOCKS) -stl_patch.start() +from yardstick.network_services.vnf_generic.vnf import tg_rfc2544_ixia +from yardstick.network_services.traffic_profile import base as tp_base -if stl_patch: - from yardstick.network_services.vnf_generic.vnf.tg_rfc2544_ixia import IxiaTrafficGen - from yardstick.network_services.vnf_generic.vnf.tg_rfc2544_ixia import IxiaRfc2544Helper - from yardstick.network_services.vnf_generic.vnf.tg_rfc2544_ixia import IxiaResourceHelper - from yardstick.network_services.traffic_profile.base import TrafficProfile TEST_FILE_YAML = 'nsb_test_case.yaml' NAME = "tg__1" -@mock.patch("yardstick.network_services.vnf_generic.vnf.tg_rfc2544_ixia.IxNextgen") class TestIxiaResourceHelper(unittest.TestCase): - def test___init___with_custom_rfc_helper(self, *args): - class MyRfcHelper(IxiaRfc2544Helper): + + def setUp(self): + self._mock_IxNextgen = mock.patch.object(tg_rfc2544_ixia, + 'IxNextgen') + self.mock_IxNextgen = self._mock_IxNextgen.start() + self.addCleanup(self._stop_mocks) + + def _stop_mocks(self): + self._mock_IxNextgen.stop() + + def test___init___with_custom_rfc_helper(self): + class MyRfcHelper(tg_rfc2544_ixia.IxiaRfc2544Helper): pass - ixia_resource_helper = IxiaResourceHelper(mock.Mock(), MyRfcHelper) + ixia_resource_helper = tg_rfc2544_ixia.IxiaResourceHelper( + mock.Mock(), MyRfcHelper) self.assertIsInstance(ixia_resource_helper.rfc_helper, MyRfcHelper) - def test_stop_collect_with_client(self, *args): + def test_stop_collect_with_client(self): mock_client = mock.Mock() - ixia_resource_helper = IxiaResourceHelper(mock.Mock()) + ixia_resource_helper = tg_rfc2544_ixia.IxiaResourceHelper(mock.Mock()) ixia_resource_helper.client = mock_client ixia_resource_helper.stop_collect() self.assertEqual(mock_client.ix_stop_traffic.call_count, 1) + def test_run_traffic(self): + mock_tprofile = mock.Mock() + mock_tprofile.get_drop_percentage.return_value = True, 'fake_samples' + ixia_rhelper = tg_rfc2544_ixia.IxiaResourceHelper(mock.Mock()) + ixia_rhelper.rfc_helper = mock.Mock() + ixia_rhelper.vnfd_helper = mock.Mock() + ixia_rhelper.vnfd_helper.port_pairs.all_ports = [] + with mock.patch.object(ixia_rhelper, 'generate_samples'), \ + mock.patch.object(ixia_rhelper, '_build_ports'), \ + mock.patch.object(ixia_rhelper, '_initialize_client'): + ixia_rhelper.run_traffic(mock_tprofile) + + self.assertEqual('fake_samples', ixia_rhelper._queue.get()) + @mock.patch("yardstick.network_services.vnf_generic.vnf.tg_rfc2544_ixia.IxNextgen") class TestIXIATrafficGen(unittest.TestCase): @@ -160,7 +175,7 @@ class TestIXIATrafficGen(unittest.TestCase): ssh.from_node.return_value = ssh_mock vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] # NOTE(ralonsoh): check the object returned. - IxiaTrafficGen(NAME, vnfd) + tg_rfc2544_ixia.IxiaTrafficGen(NAME, vnfd) def test_listen_traffic(self, *args): with mock.patch("yardstick.ssh.SSH") as ssh: @@ -169,7 +184,7 @@ class TestIXIATrafficGen(unittest.TestCase): mock.Mock(return_value=(0, "", "")) ssh.from_node.return_value = ssh_mock vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] - ixnet_traffic_gen = IxiaTrafficGen(NAME, vnfd) + ixnet_traffic_gen = tg_rfc2544_ixia.IxiaTrafficGen(NAME, vnfd) self.assertIsNone(ixnet_traffic_gen.listen_traffic({})) def test_instantiate(self, *args): @@ -181,7 +196,7 @@ class TestIXIATrafficGen(unittest.TestCase): mock.Mock(return_value=(0, "", "")) ssh.from_node.return_value = ssh_mock vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] - ixnet_traffic_gen = IxiaTrafficGen(NAME, vnfd) + ixnet_traffic_gen = tg_rfc2544_ixia.IxiaTrafficGen(NAME, vnfd) scenario_cfg = {'tc': "nsb_test_case", "topology": "", 'ixia_profile': "ixload.cfg"} scenario_cfg.update({'options': {'packetsize': 64, @@ -209,7 +224,7 @@ class TestIXIATrafficGen(unittest.TestCase): mock.Mock(return_value=(0, "", "")) ssh.from_node.return_value = ssh_mock vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] - ixnet_traffic_gen = IxiaTrafficGen(NAME, vnfd) + ixnet_traffic_gen = tg_rfc2544_ixia.IxiaTrafficGen(NAME, vnfd) ixnet_traffic_gen.data = {} restult = ixnet_traffic_gen.collect_kpi() self.assertEqual({}, restult) @@ -221,7 +236,7 @@ class TestIXIATrafficGen(unittest.TestCase): ssh_mock.execute = \ mock.Mock(return_value=(0, "", "")) ssh.from_node.return_value = ssh_mock - ixnet_traffic_gen = IxiaTrafficGen(NAME, vnfd) + ixnet_traffic_gen = tg_rfc2544_ixia.IxiaTrafficGen(NAME, vnfd) ixnet_traffic_gen._terminated = mock.MagicMock() ixnet_traffic_gen._terminated.value = 0 ixnet_traffic_gen._ixia_traffic_gen = mock.MagicMock() @@ -237,13 +252,12 @@ class TestIXIATrafficGen(unittest.TestCase): def test__check_status(self, *args): vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] - sut = IxiaTrafficGen('vnf1', vnfd) + sut = tg_rfc2544_ixia.IxiaTrafficGen('vnf1', vnfd) sut._check_status() - @mock.patch("yardstick.network_services.vnf_generic.vnf.tg_rfc2544_ixia.time") @mock.patch("yardstick.ssh.SSH") def test_traffic_runner(self, mock_ssh, *args): - mock_traffic_profile = mock.Mock(autospec=TrafficProfile) + mock_traffic_profile = mock.Mock(autospec=tp_base.TrafficProfile) mock_traffic_profile.get_traffic_definition.return_value = "64" mock_traffic_profile.params = self.TRAFFIC_PROFILE # traffic_profile.ports is standardized on port_num @@ -302,7 +316,7 @@ class TestIXIATrafficGen(unittest.TestCase): mock_traffic_profile.execute_traffic.return_value = ['Completed', samples] mock_traffic_profile.get_drop_percentage.return_value = ['Completed', samples] - sut = IxiaTrafficGen(name, vnfd) + sut = tg_rfc2544_ixia.IxiaTrafficGen(name, vnfd) sut.vnf_port_pairs = [[[0], [1]]] sut.tc_file_name = self._get_file_abspath(TEST_FILE_YAML) sut.topology = "" diff --git a/tests/unit/network_services/vnf_generic/vnf/test_vfw_vnf.py b/tests/unit/network_services/vnf_generic/vnf/test_vfw_vnf.py index 48fc87ed4..d141ac7a6 100644 --- a/tests/unit/network_services/vnf_generic/vnf/test_vfw_vnf.py +++ b/tests/unit/network_services/vnf_generic/vnf/test_vfw_vnf.py @@ -30,6 +30,7 @@ stl_patch.start() if stl_patch: from yardstick.network_services.vnf_generic.vnf.vfw_vnf import FWApproxVnf from yardstick.network_services.nfvi.resource import ResourceProfile + from yardstick.network_services.vnf_generic.vnf.vfw_vnf import FWApproxSetupEnvHelper TEST_FILE_YAML = 'nsb_test_case.yaml' @@ -333,7 +334,6 @@ pipeline> vfw_approx_vnf.ssh_helper.run.assert_called_once() @mock.patch.object(utils, 'find_relative_file') - @mock.patch("yardstick.network_services.vnf_generic.vnf.vfw_vnf.YangModel") @mock.patch("yardstick.network_services.vnf_generic.vnf.sample_vnf.Context") @mock.patch(SSH_HELPER) def test_instantiate(self, ssh, *args): @@ -349,3 +349,28 @@ pipeline> 'rules': ""}} self.scenario_cfg.update({"nodes": {"vnf__1": ""}}) self.assertIsNone(vfw_approx_vnf.instantiate(self.scenario_cfg, self.context_cfg)) + + +class TestFWApproxSetupEnvHelper(unittest.TestCase): + + @mock.patch('yardstick.network_services.vnf_generic.vnf.sample_vnf.open') + @mock.patch.object(utils, 'find_relative_file') + @mock.patch('yardstick.network_services.vnf_generic.vnf.sample_vnf.MultiPortConfig') + @mock.patch.object(utils, 'open_relative_file') + def test_build_config(self, *args): + vnfd_helper = mock.Mock() + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.vnf_cfg = {'lb_config': 'HW'} + scenario_helper.options = {} + scenario_helper.all_options = {} + + vfw_approx_setup_helper = FWApproxSetupEnvHelper(vnfd_helper, ssh_helper, scenario_helper) + vfw_approx_setup_helper.get_flows_config = mock.Mock() + + vfw_approx_setup_helper.ssh_helper.provision_tool = mock.Mock(return_value='tool_path') + vfw_approx_setup_helper.ssh_helper.all_ports = mock.Mock() + vfw_approx_setup_helper.vnfd_helper.port_nums = mock.Mock(return_value=[0, 1]) + expected = 'sudo tool_path -p 0x3 -f /tmp/vfw_config -s /tmp/vfw_script --hwlb 3' + self.assertEqual(vfw_approx_setup_helper.build_config(), expected) + vfw_approx_setup_helper.get_flows_config.assert_called_once() diff --git a/tests/unit/network_services/vnf_generic/vnf/test_vpe_vnf.py b/tests/unit/network_services/vnf_generic/vnf/test_vpe_vnf.py index 8c45d973e..9857e95b6 100644 --- a/tests/unit/network_services/vnf_generic/vnf/test_vpe_vnf.py +++ b/tests/unit/network_services/vnf_generic/vnf/test_vpe_vnf.py @@ -18,7 +18,7 @@ import os import time import mock -import six.moves.configparser as configparser +from six.moves import configparser import unittest from tests.unit import STL_MOCKS @@ -663,7 +663,14 @@ class TestVpeApproxVnf(unittest.TestCase): vpe_approx_vnf.ssh_helper.bin_path = mock.Mock() vpe_approx_vnf.ssh_helper.upload_config_file = mock.MagicMock() self.assertIsNone(vpe_approx_vnf._build_vnf_ports()) - self.assertIsNotNone(vpe_approx_vnf.build_config()) + + vpe_approx_vnf.ssh_helper.provision_tool = mock.Mock(return_value='tool_path') + vpe_approx_vnf.ssh_helper.all_ports = mock.Mock() + vpe_approx_vnf.vnfd_helper.port_nums = mock.Mock(return_value=[0, 1]) + vpe_approx_vnf.scenario_helper.vnf_cfg = {'lb_config': 'HW'} + + expected = 'sudo tool_path -p 0x3 -f /tmp/vpe_config -s /tmp/vpe_script --hwlb 3' + self.assertEqual(vpe_approx_vnf.build_config(), expected) @mock.patch(SSH_HELPER) def test_wait_for_instantiate(self, ssh): diff --git a/yardstick/benchmark/contexts/base.py b/yardstick/benchmark/contexts/base.py index ae8319e37..0707c1c5b 100644 --- a/yardstick/benchmark/contexts/base.py +++ b/yardstick/benchmark/contexts/base.py @@ -42,20 +42,12 @@ class Context(object): list = [] SHORT_TASK_ID_LEN = 8 - @staticmethod - def split_name(name, sep='.'): - try: - name_iter = iter(name.split(sep)) - except AttributeError: - # name is not a string - return None, None - return next(name_iter), next(name_iter, None) - - def __init__(self): + def __init__(self, host_name_separator='.'): Context.list.append(self) self._flags = Flags() self._name = None self._task_id = None + self._host_name_separator = host_name_separator def init(self, attrs): """Initiate context""" @@ -65,6 +57,12 @@ class Context(object): self._name_task_id = '{}-{}'.format( self._name, self._task_id[:self.SHORT_TASK_ID_LEN]) + def split_host_name(self, name): + if (isinstance(name, six.string_types) + and self._host_name_separator in name): + return tuple(name.split(self._host_name_separator, 1)) + return None, None + @property def name(self): if self._flags.no_setup or self._flags.no_teardown: @@ -76,6 +74,10 @@ class Context(object): def assigned_name(self): return self._name + @property + def host_name_separator(self): + return self._host_name_separator + @staticmethod def get_cls(context_type): """Return class of specified type.""" diff --git a/yardstick/benchmark/contexts/heat.py b/yardstick/benchmark/contexts/heat.py index 0d1dfb86f..0640d2c78 100644 --- a/yardstick/benchmark/contexts/heat.py +++ b/yardstick/benchmark/contexts/heat.py @@ -466,7 +466,7 @@ class HeatContext(Context): with attribute name mapping when using external heat templates """ if isinstance(attr_name, collections.Mapping): - node_name, cname = self.split_name(attr_name['name']) + node_name, cname = self.split_host_name(attr_name['name']) if cname is None or cname != self.name: return None diff --git a/yardstick/benchmark/contexts/kubernetes.py b/yardstick/benchmark/contexts/kubernetes.py index 4bea991ea..82435d40c 100644 --- a/yardstick/benchmark/contexts/kubernetes.py +++ b/yardstick/benchmark/contexts/kubernetes.py @@ -33,8 +33,7 @@ class KubernetesContext(Context): self.key_path = '' self.public_key_path = '' self.template = None - - super(KubernetesContext, self).__init__() + super(KubernetesContext, self).__init__(host_name_separator='-') def init(self, attrs): super(KubernetesContext, self).init(attrs) diff --git a/yardstick/benchmark/contexts/node.py b/yardstick/benchmark/contexts/node.py index fa619a9aa..93888ef41 100644 --- a/yardstick/benchmark/contexts/node.py +++ b/yardstick/benchmark/contexts/node.py @@ -139,7 +139,7 @@ class NodeContext(Context): """lookup server info by name from context attr_name: a name for a server listed in nodes config file """ - node_name, name = self.split_name(attr_name) + node_name, name = self.split_host_name(attr_name) if name is None or self.name != name: return None diff --git a/yardstick/benchmark/contexts/standalone/ovs_dpdk.py b/yardstick/benchmark/contexts/standalone/ovs_dpdk.py index 30b685eec..ccb0f8f99 100644 --- a/yardstick/benchmark/contexts/standalone/ovs_dpdk.py +++ b/yardstick/benchmark/contexts/standalone/ovs_dpdk.py @@ -305,7 +305,7 @@ class OvsDpdkContext(Context): Keyword arguments: attr_name -- A name for a server listed in nodes config file """ - node_name, name = self.split_name(attr_name) + node_name, name = self.split_host_name(attr_name) if name is None or self.name != name: return None diff --git a/yardstick/benchmark/contexts/standalone/sriov.py b/yardstick/benchmark/contexts/standalone/sriov.py index 5db419e6a..c5438b3cf 100644 --- a/yardstick/benchmark/contexts/standalone/sriov.py +++ b/yardstick/benchmark/contexts/standalone/sriov.py @@ -115,7 +115,7 @@ class SriovContext(Context): Keyword arguments: attr_name -- A name for a server listed in nodes config file """ - node_name, name = self.split_name(attr_name) + node_name, name = self.split_host_name(attr_name) if name is None or self.name != name: return None @@ -197,10 +197,10 @@ class SriovContext(Context): slot = index + idx + 10 vf['vpci'] = \ "{}:{}:{:02x}.{}".format(vpci.domain, vpci.bus, slot, vpci.function) - model.Libvirt.add_sriov_interfaces( - vf['vpci'], vf['vf_pci']['vf_pci'], vf['mac'], str(cfg)) self.connection.execute("ifconfig %s up" % vf['interface']) self.connection.execute(vf_spoofchk.format(vf['interface'])) + return model.Libvirt.add_sriov_interfaces( + vf['vpci'], vf['vf_pci']['vf_pci'], vf['mac'], str(cfg)) def setup_sriov_context(self): nodes = [] @@ -223,7 +223,7 @@ class SriovContext(Context): network_ports = collections.OrderedDict( {k: v for k, v in vnf["network_ports"].items() if k != 'mgmt'}) for idx, vfs in enumerate(network_ports.values()): - self._enable_interfaces(index, idx, vfs, cfg) + xml_str = self._enable_interfaces(index, idx, vfs, xml_str) # copy xml to target... model.Libvirt.write_file(cfg, xml_str) diff --git a/yardstick/benchmark/core/task.py b/yardstick/benchmark/core/task.py index 697cc007f..f050e8d0f 100644 --- a/yardstick/benchmark/core/task.py +++ b/yardstick/benchmark/core/task.py @@ -619,27 +619,22 @@ class TaskParser(object): # pragma: no cover nodes: tg__0: tg_0.yardstick vnf__0: vnf_0.yardstick + + NOTE: in Kubernetes context, the separator character between the server + name and the context name is "-": + scenario: + host: host-k8s + target: target-k8s """ def qualified_name(name): - try: - # for openstack - node_name, context_name = name.split('.') - sep = '.' - except ValueError: - # for kubernetes, some kubernetes resources don't support - # name format like 'xxx.xxx', so we use '-' instead - # need unified later - node_name, context_name = name.split('-') - sep = '-' - - try: - ctx = next((context for context in contexts - if context.assigned_name == context_name)) - except StopIteration: - raise y_exc.ScenarioConfigContextNameNotFound( - context_name=context_name) - - return '{}{}{}'.format(node_name, sep, ctx.name) + for context in contexts: + host_name, ctx_name = context.split_host_name(name) + if context.assigned_name == ctx_name: + return '{}{}{}'.format(host_name, + context.host_name_separator, + context.name) + + raise y_exc.ScenarioConfigContextNameNotFound(host_name=name) if 'host' in scenario: scenario['host'] = qualified_name(scenario['host']) diff --git a/yardstick/benchmark/scenarios/networking/vnf_generic.py b/yardstick/benchmark/scenarios/networking/vnf_generic.py index be2fa3f3b..1c3ea1f8d 100644 --- a/yardstick/benchmark/scenarios/networking/vnf_generic.py +++ b/yardstick/benchmark/scenarios/networking/vnf_generic.py @@ -13,20 +13,19 @@ # limitations under the License. import copy -import logging -import time - import ipaddress from itertools import chain +import logging import os import sys +import time import six import yaml from yardstick.benchmark.scenarios import base as scenario_base -from yardstick.error import IncorrectConfig from yardstick.common.constants import LOG_DIR +from yardstick.common import exceptions from yardstick.common.process import terminate_children from yardstick.common import utils from yardstick.network_services.collector.subscriber import Collector @@ -190,8 +189,9 @@ class NetworkServiceTestCase(scenario_base.Scenario): try: node0_data, node1_data = vld["vnfd-connection-point-ref"] except (ValueError, TypeError): - raise IncorrectConfig("Topology file corrupted, " - "wrong endpoint count for connection") + raise exceptions.IncorrectConfig( + error_msg='Topology file corrupted, wrong endpoint count ' + 'for connection') node0_name = self._find_vnf_name_from_id(node0_data["member-vnf-index-ref"]) node1_name = self._find_vnf_name_from_id(node1_data["member-vnf-index-ref"]) @@ -237,15 +237,17 @@ class NetworkServiceTestCase(scenario_base.Scenario): except KeyError: LOG.exception("") - raise IncorrectConfig("Required interface not found, " - "topology file corrupted") + raise exceptions.IncorrectConfig( + error_msg='Required interface not found, topology file ' + 'corrupted') for vld in self.topology['vld']: try: node0_data, node1_data = vld["vnfd-connection-point-ref"] except (ValueError, TypeError): - raise IncorrectConfig("Topology file corrupted, " - "wrong endpoint count for connection") + raise exceptions.IncorrectConfig( + error_msg='Topology file corrupted, wrong endpoint count ' + 'for connection') node0_name = self._find_vnf_name_from_id(node0_data["member-vnf-index-ref"]) node1_name = self._find_vnf_name_from_id(node1_data["member-vnf-index-ref"]) @@ -330,8 +332,9 @@ class NetworkServiceTestCase(scenario_base.Scenario): except StopIteration: pass - raise IncorrectConfig("No implementation for %s found in %s" % - (expected_name, classes_found)) + message = ('No implementation for %s found in %s' + % (expected_name, classes_found)) + raise exceptions.IncorrectConfig(error_msg=message) @staticmethod def create_interfaces_from_node(vnfd, node): @@ -441,7 +444,7 @@ class NetworkServiceTestCase(scenario_base.Scenario): traffic_gen.listen_traffic(self.traffic_profile) # register collector with yardstick for KPI collection. - self.collector = Collector(self.vnfs, self.context_cfg["nodes"], self.traffic_profile) + self.collector = Collector(self.vnfs) self.collector.start() # Start the actual traffic diff --git a/yardstick/common/exceptions.py b/yardstick/common/exceptions.py index fb4d33a59..9f5f8b53a 100644 --- a/yardstick/common/exceptions.py +++ b/yardstick/common/exceptions.py @@ -21,6 +21,16 @@ class ProcessExecutionError(RuntimeError): self.returncode = returncode +class ErrorClass(object): + + def __init__(self, *args, **kwargs): + if 'test' not in kwargs: + raise RuntimeError + + def __getattr__(self, item): + raise AttributeError + + class YardstickException(Exception): """Base Yardstick Exception. @@ -54,6 +64,10 @@ class YardstickException(Exception): return False +class ResourceCommandError(YardstickException): + message = 'Command: "%(command)s" Failed, stderr: "%(stderr)s"' + + class FunctionNotImplemented(YardstickException): message = ('The function "%(function_name)s" is not implemented in ' '"%(class_name)" class.') @@ -112,8 +126,28 @@ class LibvirtCreateError(YardstickException): message = 'Error creating the virtual machine. Error: %(error)s.' +class SSHError(YardstickException): + message = '%(error_msg)s' + + +class SSHTimeout(SSHError): + pass + + +class IncorrectConfig(YardstickException): + message = '%(error_msg)s' + + +class IncorrectSetup(YardstickException): + message = '%(error_msg)s' + + +class IncorrectNodeSetup(IncorrectSetup): + pass + + class ScenarioConfigContextNameNotFound(YardstickException): - message = 'Context name "%(context_name)s" not found' + message = 'Context for host name "%(host_name)s" not found' class StackCreationInterrupt(YardstickException): @@ -132,6 +166,14 @@ class TaskRenderError(YardstickException): message = 'Failed to render template:\n%(input_task)s' +class TimerTimeout(YardstickException): + message = 'Timer timeout expired, %(timeout)s seconds' + + +class WaitTimeout(YardstickException): + message = 'Wait timeout while waiting for condition' + + class ScenarioCreateNetworkError(YardstickException): message = 'Create Neutron Network Scenario failed' @@ -178,3 +220,24 @@ class UploadOpenrcError(ApiServerError): class UpdateOpenrcError(ApiServerError): message = 'Update openrc ERROR!' + + +class IxNetworkClientNotConnected(YardstickException): + message = 'IxNetwork client not connected to a TCL server' + + +class IxNetworkFlowNotPresent(YardstickException): + message = 'Flow Group "%(flow_group)s" is not present' + + +class IxNetworkFieldNotPresentInStackItem(YardstickException): + message = 'Field "%(field_name)s" not present in stack item %(stack_item)s' + + +class AclMissingActionArguments(YardstickException): + message = ('Missing ACL action parameter ' + '[action=%(action_name)s parameter=%(action_param)s]') + + +class AclUknownActionTemplate(YardstickException): + message = 'No ACL CLI template found for "%(action_name)s" action' diff --git a/yardstick/common/utils.py b/yardstick/common/utils.py index 44cc92a7c..869db469b 100644 --- a/yardstick/common/utils.py +++ b/yardstick/common/utils.py @@ -23,9 +23,11 @@ import logging import os import random import re +import signal import socket import subprocess import sys +import time import six from flask import jsonify @@ -34,6 +36,8 @@ from oslo_serialization import jsonutils from oslo_utils import encodeutils import yardstick +from yardstick.common import exceptions + logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) @@ -302,6 +306,19 @@ def get_ip_version(ip_addr): return address.version +def make_ip_addr(ip, mask): + """ + :param ip[str]: ip adddress + :param mask[str]: /24 prefix of 255.255.255.0 netmask + :return: IPv4Interface object + """ + try: + return ipaddress.ip_interface(six.text_type('/'.join([ip, mask]))) + except (TypeError, ValueError): + # None so we can skip later + return None + + def ip_to_hex(ip_addr, separator=''): try: address = ipaddress.ip_address(six.text_type(ip_addr)) @@ -405,15 +422,24 @@ class ErrorClass(object): class Timer(object): - def __init__(self): + def __init__(self, timeout=None): super(Timer, self).__init__() self.start = self.delta = None + self._timeout = int(timeout) if timeout else None + + def _timeout_handler(self, *args): + raise exceptions.TimerTimeout(timeout=self._timeout) def __enter__(self): self.start = datetime.datetime.now() + if self._timeout: + signal.signal(signal.SIGALRM, self._timeout_handler) + signal.alarm(self._timeout) return self def __exit__(self, *_): + if self._timeout: + signal.alarm(0) self.delta = datetime.datetime.now() - self.start def __getattr__(self, item): @@ -460,3 +486,22 @@ def open_relative_file(path, task_path): if e.errno == errno.ENOENT: return open(os.path.join(task_path, path)) raise + + +def wait_until_true(predicate, timeout=60, sleep=1, exception=None): + """Wait until callable predicate is evaluated as True + + :param predicate: (func) callable deciding whether waiting should continue + :param timeout: (int) timeout in seconds how long should function wait + :param sleep: (int) polling interval for results in seconds + :param exception: exception instance to raise on timeout. If None is passed + (default) then WaitTimeout exception is raised. + """ + try: + with Timer(timeout=timeout): + while not predicate(): + time.sleep(sleep) + except exceptions.TimerTimeout: + if exception and issubclass(exception, Exception): + raise exception # pylint: disable=raising-bad-type + raise exceptions.WaitTimeout diff --git a/yardstick/error.py b/yardstick/error.py deleted file mode 100644 index 9b84de1af..000000000 --- a/yardstick/error.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2016-2017 Intel 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. - - -class SSHError(Exception): - """Class handles ssh connection error exception""" - pass - - -class SSHTimeout(SSHError): - """Class handles ssh connection timeout exception""" - pass - - -class IncorrectConfig(Exception): - """Class handles incorrect configuration during setup""" - pass - - -class IncorrectSetup(Exception): - """Class handles incorrect setup during setup""" - pass - - -class IncorrectNodeSetup(IncorrectSetup): - """Class handles incorrect setup during setup""" - pass - - -class ErrorClass(object): - - def __init__(self, *args, **kwargs): - if 'test' not in kwargs: - raise RuntimeError - - def __getattr__(self, item): - raise AttributeError diff --git a/yardstick/network_services/collector/subscriber.py b/yardstick/network_services/collector/subscriber.py index 7e18302eb..322b3f5a2 100644 --- a/yardstick/network_services/collector/subscriber.py +++ b/yardstick/network_services/collector/subscriber.py @@ -14,42 +14,29 @@ """This module implements stub for publishing results in yardstick format.""" import logging -from yardstick.network_services.nfvi.resource import ResourceProfile -from yardstick.network_services.utils import get_nsb_option - LOG = logging.getLogger(__name__) class Collector(object): """Class that handles dictionary of results in yardstick-plot format.""" - def __init__(self, vnfs, nodes, traffic_profile, timeout=3600): + def __init__(self, vnfs): super(Collector, self).__init__() - self.traffic_profile = traffic_profile self.vnfs = vnfs - self.nodes = nodes - self.timeout = timeout - self.bin_path = get_nsb_option('bin_path', '') - self.resource_profiles = {node_name: ResourceProfile.make_from_node(node, self.timeout) - for node_name, node in self.nodes.items() - if node.get("collectd")} def start(self): - """Nothing to do, yet""" - for resource in self.resource_profiles.values(): - resource.initiate_systemagent(self.bin_path) - resource.start() - resource.amqp_process_for_nfvi_kpi() + for vnf in self.vnfs: + vnf.start_collect() def stop(self): - """Nothing to do, yet""" - for resource in self.resource_profiles.values(): - resource.stop() + for vnf in self.vnfs: + vnf.stop_collect() def get_kpi(self): """Returns dictionary of results in yardstick-plot format - :return: + :return: (dict) dictionary of kpis collected from the VNFs; + the keys are the names of the VNFs. """ results = {} for vnf in self.vnfs: @@ -58,17 +45,4 @@ class Collector(object): LOG.debug("collect KPI for %s", vnf.name) results[vnf.name] = vnf.collect_kpi() - for node_name, resource in self.resource_profiles.items(): - # Result example: - # {"VNF1: { "tput" : [1000, 999] }, "VNF2": { "latency": 100 }} - LOG.debug("collect KPI for %s", node_name) - if resource.check_if_system_agent_running("collectd")[0] != 0: - continue - - try: - results[node_name] = {"core": resource.amqp_collect_nfvi_kpi()} - LOG.debug("%s collect KPIs %s", node_name, results[node_name]['core']) - # NOTE(elfoley): catch a more specific error - except Exception as exc: # pylint: disable=broad-except - LOG.exception(exc) return results diff --git a/yardstick/network_services/helpers/dpdkbindnic_helper.py b/yardstick/network_services/helpers/dpdkbindnic_helper.py index 05b822c2e..1c74355ef 100644 --- a/yardstick/network_services/helpers/dpdkbindnic_helper.py +++ b/yardstick/network_services/helpers/dpdkbindnic_helper.py @@ -18,12 +18,9 @@ import re from collections import defaultdict from itertools import chain +from yardstick.common import exceptions from yardstick.common.utils import validate_non_string_sequence -from yardstick.error import IncorrectConfig -from yardstick.error import IncorrectSetup -from yardstick.error import IncorrectNodeSetup -from yardstick.error import SSHTimeout -from yardstick.error import SSHError + NETWORK_KERNEL = 'network_kernel' NETWORK_DPDK = 'network_dpdk' @@ -51,7 +48,7 @@ class DpdkInterface(object): try: assert self.local_mac except (AssertionError, KeyError): - raise IncorrectConfig + raise exceptions.IncorrectConfig(error_msg='') @property def local_mac(self): @@ -98,10 +95,12 @@ class DpdkInterface(object): # if we don't find all the keys then don't update pass - except (IncorrectNodeSetup, SSHError, SSHTimeout): - raise IncorrectConfig( - "Unable to probe missing interface fields '%s', on node %s " - "SSH Error" % (', '.join(self.missing_fields), self.dpdk_node.node_key)) + except (exceptions.IncorrectNodeSetup, exceptions.SSHError, + exceptions.SSHTimeout): + message = ('Unable to probe missing interface fields "%s", on ' + 'node %s SSH Error' % (', '.join(self.missing_fields), + self.dpdk_node.node_key)) + raise exceptions.IncorrectConfig(error_msg=message) class DpdkNode(object): @@ -118,11 +117,12 @@ class DpdkNode(object): try: self.dpdk_interfaces = {intf['name']: DpdkInterface(self, intf['virtual-interface']) for intf in self.interfaces} - except IncorrectConfig: + except exceptions.IncorrectConfig: template = "MAC address is required for all interfaces, missing on: {}" errors = (intf['name'] for intf in self.interfaces if 'local_mac' not in intf['virtual-interface']) - raise IncorrectSetup(template.format(", ".join(errors))) + raise exceptions.IncorrectSetup( + error_msg=template.format(", ".join(errors))) @property def dpdk_helper(self): @@ -176,7 +176,7 @@ class DpdkNode(object): self._probe_netdevs() try: self._probe_missing_values() - except IncorrectConfig: + except exceptions.IncorrectConfig: # ignore for now pass @@ -193,7 +193,7 @@ class DpdkNode(object): missing_fields) errors = "\n".join(errors) if errors: - raise IncorrectSetup(errors) + raise exceptions.IncorrectSetup(error_msg=errors) finally: self._dpdk_helper = None diff --git a/yardstick/network_services/helpers/samplevnf_helper.py b/yardstick/network_services/helpers/samplevnf_helper.py index 0ab10d7b7..8e6a3a3ea 100644 --- a/yardstick/network_services/helpers/samplevnf_helper.py +++ b/yardstick/network_services/helpers/samplevnf_helper.py @@ -23,8 +23,7 @@ from itertools import chain, repeat import six from six.moves.configparser import ConfigParser - -from yardstick.common.utils import ip_to_hex +from yardstick.common import utils LOG = logging.getLogger(__name__) @@ -34,19 +33,6 @@ link {0} config {1} {2} link {0} up """ -ACTION_TEMPLATE = """\ -p action add {0} accept -p action add {0} fwd {0} -p action add {0} count -""" - -FW_ACTION_TEMPLATE = """\ -p action add {0} accept -p action add {0} fwd {0} -p action add {0} count -p action add {0} conntrack -""" - # This sets up a basic passthrough with no rules SCRIPT_TPL = """ {link_config} @@ -59,9 +45,7 @@ SCRIPT_TPL = """ {arp_route_tbl6} -{actions} - -{rules} +{flows} """ @@ -182,26 +166,9 @@ class MultiPortConfig(object): return parser.get(section, key) return default - @staticmethod - def make_ip_addr(ip, mask): - """ - :param ip: ip adddress - :type ip: str - :param mask: /24 prefix of 255.255.255.0 netmask - :type mask: str - :return: interface - :rtype: IPv4Interface - """ - - try: - return ipaddress.ip_interface(six.text_type('/'.join([ip, mask]))) - except (TypeError, ValueError): - # None so we can skip later - return None - @classmethod def validate_ip_and_prefixlen(cls, ip_addr, prefixlen): - ip_addr = cls.make_ip_addr(ip_addr, prefixlen) + ip_addr = utils.make_ip_addr(ip_addr, prefixlen) return ip_addr.ip.exploded, ip_addr.network.prefixlen def __init__(self, topology_file, config_tpl, tmp_file, vnfd_helper, @@ -245,7 +212,7 @@ class MultiPortConfig(object): self.ports_len = 0 self.prv_que_handler = None self.vnfd = None - self.rules = None + self.flows = None self.pktq_out = [] @staticmethod @@ -360,7 +327,7 @@ class MultiPortConfig(object): "%s/%s" % (interface["dst_ip"], interface["netmask"]))) arp_vars = { - "port_netmask_hex": ip_to_hex(dst_port_ip.network.netmask.exploded), + "port_netmask_hex": utils.ip_to_hex(dst_port_ip.network.netmask.exploded), # this is the port num that contains port0 subnet and next_hop_ip_hex # this is LINKID which should be based on DPDK port number "port_num": dpdk_port_num, @@ -542,7 +509,7 @@ class MultiPortConfig(object): self.update_write_parser(self.loadb_tpl) self.start_core += 1 - for i in range(self.worker_threads): + for _ in range(self.worker_threads): vnf_data = self.generate_vnf_data() if not self.vnf_tpl: self.vnf_tpl = {} @@ -637,65 +604,8 @@ class MultiPortConfig(object): return '\n'.join(('p {3} arpadd {0} {1} {2}'.format(*values) for values in arp_config6)) - def generate_action_config(self): - port_list = (self.vnfd_helper.port_num(p) for p in self.all_ports) - if self.vnf_type == "VFW": - template = FW_ACTION_TEMPLATE - else: - template = ACTION_TEMPLATE - - return ''.join((template.format(port) for port in port_list)) - - def get_ip_from_port(self, port): - # we can't use gateway because in OpenStack gateways interfer with floating ip routing - # return self.make_ip_addr(self.get_ports_gateway(port), self.get_netmask_gateway(port)) - vintf = self.vnfd_helper.find_interface(name=port)["virtual-interface"] - ip = vintf["local_ip"] - netmask = vintf["netmask"] - return self.make_ip_addr(ip, netmask) - - def get_network_and_prefixlen_from_ip_of_port(self, port): - ip_addr = self.get_ip_from_port(port) - # handle cases with no gateway - if ip_addr: - return ip_addr.network.network_address.exploded, ip_addr.network.prefixlen - else: - return None, None - - def generate_rule_config(self): - cmd = 'acl' if self.vnf_type == "ACL" else "vfw" - rules_config = self.rules if self.rules else '' - new_rules = [] - new_ipv6_rules = [] - pattern = 'p {0} add {1} {2} {3} {4} {5} 0 65535 0 65535 0 0 {6}' - for src_intf, dst_intf in self.port_pair_list: - src_port = self.vnfd_helper.port_num(src_intf) - dst_port = self.vnfd_helper.port_num(dst_intf) - - src_net, src_prefix_len = self.get_network_and_prefixlen_from_ip_of_port(src_intf) - dst_net, dst_prefix_len = self.get_network_and_prefixlen_from_ip_of_port(dst_intf) - # ignore entires with empty values - if all((src_net, src_prefix_len, dst_net, dst_prefix_len)): - new_rules.append((cmd, self.txrx_pipeline, src_net, src_prefix_len, - dst_net, dst_prefix_len, dst_port)) - new_rules.append((cmd, self.txrx_pipeline, dst_net, dst_prefix_len, - src_net, src_prefix_len, src_port)) - - # src_net = self.get_ports_gateway6(port_pair[0]) - # src_prefix_len = self.get_netmask_gateway6(port_pair[0]) - # dst_net = self.get_ports_gateway6(port_pair[1]) - # dst_prefix_len = self.get_netmask_gateway6(port_pair[0]) - # # ignore entires with empty values - # if all((src_net, src_prefix_len, dst_net, dst_prefix_len)): - # new_ipv6_rules.append((cmd, self.txrx_pipeline, src_net, src_prefix_len, - # dst_net, dst_prefix_len, dst_port)) - # new_ipv6_rules.append((cmd, self.txrx_pipeline, dst_net, dst_prefix_len, - # src_net, src_prefix_len, src_port)) - - acl_apply = "\np %s applyruleset" % cmd - new_rules_config = '\n'.join(pattern.format(*values) for values - in chain(new_rules, new_ipv6_rules)) - return ''.join([rules_config, new_rules_config, acl_apply]) + def get_flows_config(self): + return self.flows if self.flows else '' def generate_script_data(self): self._port_pairs = PortPairs(self.vnfd_helper.interfaces) @@ -707,24 +617,15 @@ class MultiPortConfig(object): # disable IPv6 for now # 'arp_config6': self.generate_arp_config6(), 'arp_config6': "", - 'arp_config': self.generate_arp_config(), 'arp_route_tbl': self.generate_arp_route_tbl(), 'arp_route_tbl6': "", - 'actions': '', - 'rules': '', + 'flows': self.get_flows_config() } - - if self.vnf_type in ('ACL', 'VFW'): - script_data.update({ - 'actions': self.generate_action_config(), - 'rules': self.generate_rule_config(), - }) - return script_data - def generate_script(self, vnfd, rules=None): + def generate_script(self, vnfd, flows=None): self.vnfd = vnfd - self.rules = rules + self.flows = flows script_data = self.generate_script_data() script = SCRIPT_TPL.format(**script_data) if self.lb_config == self.HW_LB: diff --git a/yardstick/network_services/libs/ixia_libs/IxNet/IxNet.py b/yardstick/network_services/libs/ixia_libs/IxNet/IxNet.py deleted file mode 100644 index 70ce4ff03..000000000 --- a/yardstick/network_services/libs/ixia_libs/IxNet/IxNet.py +++ /dev/null @@ -1,344 +0,0 @@ -# Copyright (c) 2016-2017 Intel 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. - -from __future__ import absolute_import -from __future__ import print_function -import sys -import logging - -import re -from itertools import product - -log = logging.getLogger(__name__) - -IP_VERSION_4 = 4 -IP_VERSION_6 = 6 - - -class TrafficStreamHelper(object): - - TEMPLATE = '{0.traffic_item}/{0.stream}:{0.param_id}/{1}' - - def __init__(self, traffic_item, stream, param_id): - super(TrafficStreamHelper, self).__init__() - self.traffic_item = traffic_item - self.stream = stream - self.param_id = param_id - - def __getattr__(self, item): - return self.TEMPLATE.format(self, item) - - -class FramesizeHelper(object): - - def __init__(self): - super(FramesizeHelper, self).__init__() - self.weighted_pairs = [] - self.weighted_range_pairs = [] - - @property - def weighted_pairs_arg(self): - return '-weightedPairs', self.weighted_pairs - - @property - def weighted_range_pairs_arg(self): - return '-weightedRangePairs', self.weighted_range_pairs - - def make_args(self, *args): - return self.weighted_pairs_arg + self.weighted_range_pairs_arg + args - - def populate_data(self, framesize_data): - for key, value in framesize_data.items(): - if value == '0': - continue - - replaced = re.sub('[Bb]', '', key) - self.weighted_pairs.extend([ - replaced, - value, - ]) - pairs = [ - replaced, - replaced, - value, - ] - self.weighted_range_pairs.append(pairs) - - -class IxNextgen(object): - - STATS_NAME_MAP = { - "traffic_item": 'Traffic Item', - "Tx_Frames": 'Tx Frames', - "Rx_Frames": 'Rx Frames', - "Tx_Frame_Rate": 'Tx Frame Rate', - "Rx_Frame_Rate": 'Tx Frame Rate', - "Store-Forward_Avg_latency_ns": 'Store-Forward Avg Latency (ns)', - "Store-Forward_Min_latency_ns": 'Store-Forward Min Latency (ns)', - "Store-Forward_Max_latency_ns": 'Store-Forward Max Latency (ns)', - } - - PORT_STATS_NAME_MAP = { - "stat_name": 'Stat Name', - "Frames_Tx": 'Frames Tx.', - "Valid_Frames_Rx": 'Valid Frames Rx.', - "Frames_Tx_Rate": 'Frames Tx. Rate', - "Valid_Frames_Rx_Rate": 'Valid Frames Rx. Rate', - "Tx_Rate_Kbps": 'Tx. Rate (Kbps)', - "Rx_Rate_Kbps": 'Rx. Rate (Kbps)', - "Tx_Rate_Mbps": 'Tx. Rate (Mbps)', - "Rx_Rate_Mbps": 'Rx. Rate (Mbps)', - } - - LATENCY_NAME_MAP = { - "Store-Forward_Avg_latency_ns": 'Store-Forward Avg Latency (ns)', - "Store-Forward_Min_latency_ns": 'Store-Forward Min Latency (ns)', - "Store-Forward_Max_latency_ns": 'Store-Forward Max Latency (ns)', - } - - RANDOM_MASK_MAP = { - IP_VERSION_4: '0.0.0.255', - IP_VERSION_6: '0:0:0:0:0:0:0:ff', - } - - MODE_SEEDS_MAP = { - 0: ('uplink', ['256', '2048']), - } - - MODE_SEEDS_DEFAULT = 'downlink', ['2048', '256'] - - @staticmethod - def find_view_obj(view_name, views): - edited_view_name = '::ixNet::OBJ-/statistics/view:"{}"'.format(view_name) - return next((view for view in views if edited_view_name == view), '') - - @staticmethod - def get_config(tg_cfg): - card = [] - port = [] - external_interface = tg_cfg["vdu"][0]["external-interface"] - for intf in external_interface: - card_port0 = intf["virtual-interface"]["vpci"] - card0, port0 = card_port0.split(':')[:2] - card.append(card0) - port.append(port0) - - cfg = { - 'py_lib_path': tg_cfg["mgmt-interface"]["tg-config"]["py_lib_path"], - 'machine': tg_cfg["mgmt-interface"]["ip"], - 'port': tg_cfg["mgmt-interface"]["tg-config"]["tcl_port"], - 'chassis': tg_cfg["mgmt-interface"]["tg-config"]["ixchassis"], - 'cards': card, - 'ports': port, - 'output_dir': tg_cfg["mgmt-interface"]["tg-config"]["dut_result_dir"], - 'version': tg_cfg["mgmt-interface"]["tg-config"]["version"], - 'bidir': True, - } - - return cfg - - def __init__(self, ixnet=None): - self.ixnet = ixnet - self._objRefs = dict() - self._cfg = None - self._logger = logging.getLogger(__name__) - self._params = None - self._bidir = None - - def iter_over_get_lists(self, x1, x2, y2, offset=0): - for x in self.ixnet.getList(x1, x2): - y_list = self.ixnet.getList(x, y2) - for i, y in enumerate(y_list, offset): - yield x, y, i - - def set_random_ip_multi_attribute(self, ipv4, seed, fixed_bits, random_mask, l3_count): - self.ixnet.setMultiAttribute( - ipv4, - '-seed', str(seed), - '-fixedBits', str(fixed_bits), - '-randomMask', str(random_mask), - '-valueType', 'random', - '-countValue', str(l3_count)) - - def set_random_ip_multi_attributes(self, ip, version, seeds, l3): - try: - random_mask = self.RANDOM_MASK_MAP[version] - except KeyError: - raise ValueError('Unknown version %s' % version) - - l3_count = l3['count'] - if "srcIp" in ip: - fixed_bits = l3['srcip4'] - self.set_random_ip_multi_attribute(ip, seeds[0], fixed_bits, random_mask, l3_count) - if "dstIp" in ip: - fixed_bits = l3['dstip4'] - self.set_random_ip_multi_attribute(ip, seeds[1], fixed_bits, random_mask, l3_count) - - def add_ip_header(self, params, version): - for it, ep, i in self.iter_over_get_lists('/traffic', 'trafficItem', "configElement", 1): - iter1 = (v['outer_l3'] for v in params.values() if str(v['id']) == str(i)) - try: - l3 = next(iter1, {}) - seeds = self.MODE_SEEDS_MAP.get(i, self.MODE_SEEDS_DEFAULT)[1] - except (KeyError, IndexError): - continue - - for ip, ip_bits, _ in self.iter_over_get_lists(ep, 'stack', 'field'): - self.set_random_ip_multi_attributes(ip_bits, version, seeds, l3) - - self.ixnet.commit() - - def _connect(self, tg_cfg): - self._cfg = self.get_config(tg_cfg) - - sys.path.append(self._cfg["py_lib_path"]) - # Import IxNetwork after getting ixia lib path - try: - import IxNetwork - except ImportError: - raise - - self.ixnet = IxNetwork.IxNet() - - machine = self._cfg['machine'] - port = str(self._cfg['port']) - version = str(self._cfg['version']) - result = self.ixnet.connect(machine, '-port', port, '-version', version) - return result - - def clear_ixia_config(self): - self.ixnet.execute('newConfig') - - def load_ixia_profile(self, profile): - self.ixnet.execute('loadConfig', self.ixnet.readFrom(profile)) - - def ix_load_config(self, profile): - self.clear_ixia_config() - self.load_ixia_profile(profile) - - def ix_assign_ports(self): - vports = self.ixnet.getList(self.ixnet.getRoot(), 'vport') - ports = [] - - chassis = self._cfg['chassis'] - ports = [(chassis, card, port) for card, port in - zip(self._cfg['cards'], self._cfg['ports'])] - - vport_list = self.ixnet.getList("/", "vport") - self.ixnet.execute('assignPorts', ports, [], vport_list, True) - self.ixnet.commit() - - for vport in vports: - if self.ixnet.getAttribute(vport, '-state') != 'up': - log.error("Both thr ports are down...") - - def ix_update_frame(self, params): - streams = ["configElement"] - - for param in params.values(): - framesize_data = FramesizeHelper() - traffic_items = self.ixnet.getList('/traffic', 'trafficItem') - param_id = param['id'] - for traffic_item, stream in product(traffic_items, streams): - helper = TrafficStreamHelper(traffic_item, stream, param_id) - - self.ixnet.setMultiAttribute(helper.transmissionControl, - '-type', '{0}'.format(param.get('traffic_type', - 'continuous')), - '-duration', '{0}'.format(param.get('duration', - "30"))) - - stream_frame_rate_path = helper.frameRate - self.ixnet.setMultiAttribute(stream_frame_rate_path, '-rate', param['iload']) - if param['outer_l2']['framesPerSecond']: - self.ixnet.setMultiAttribute(stream_frame_rate_path, - '-type', 'framesPerSecond') - - framesize_data.populate_data(param['outer_l2']['framesize']) - - make_attr_args = framesize_data.make_args('-incrementFrom', '66', - '-randomMin', '66', - '-quadGaussian', [], - '-type', 'weightedPairs', - '-presetDistribution', 'cisco', - '-incrementTo', '1518') - - self.ixnet.setMultiAttribute(helper.frameSize, *make_attr_args) - - self.ixnet.commit() - - def update_ether_multi_attribute(self, ether, mac_addr): - self.ixnet.setMultiAttribute(ether, - '-singleValue', mac_addr, - '-fieldValue', mac_addr, - '-valueType', 'singleValue') - - def update_ether_multi_attributes(self, ether, l2): - if "ethernet.header.destinationAddress" in ether: - self.update_ether_multi_attribute(ether, str(l2.get('dstmac', "00:00:00:00:00:02"))) - - if "ethernet.header.sourceAddress" in ether: - self.update_ether_multi_attribute(ether, str(l2.get('srcmac', "00:00:00:00:00:01"))) - - def ix_update_ether(self, params): - for ti, ep, index in self.iter_over_get_lists('/traffic', 'trafficItem', - "configElement", 1): - iter1 = (v['outer_l2'] for v in params.values() if str(v['id']) == str(index)) - try: - l2 = next(iter1, {}) - except KeyError: - continue - - for ip, ether, _ in self.iter_over_get_lists(ep, 'stack', 'field'): - self.update_ether_multi_attributes(ether, l2) - - self.ixnet.commit() - - def ix_update_udp(self, params): - pass - - def ix_update_tcp(self, params): - pass - - def ix_start_traffic(self): - tis = self.ixnet.getList('/traffic', 'trafficItem') - for ti in tis: - self.ixnet.execute('generate', [ti]) - self.ixnet.execute('apply', '/traffic') - self.ixnet.execute('start', '/traffic') - - def ix_stop_traffic(self): - tis = self.ixnet.getList('/traffic', 'trafficItem') - for _ in tis: - self.ixnet.execute('stop', '/traffic') - - def build_stats_map(self, view_obj, name_map): - return {kl: self.execute_get_column_values(view_obj, kr) for kl, kr in name_map.items()} - - def execute_get_column_values(self, view_obj, name): - return self.ixnet.execute('getColumnValues', view_obj, name) - - def ix_get_statistics(self): - views = self.ixnet.getList('/statistics', 'view') - stats = {} - view_obj = self.find_view_obj("Traffic Item Statistics", views) - stats = self.build_stats_map(view_obj, self.STATS_NAME_MAP) - - view_obj = self.find_view_obj("Port Statistics", views) - ports_stats = self.build_stats_map(view_obj, self.PORT_STATS_NAME_MAP) - - view_obj = self.find_view_obj("Flow Statistics", views) - stats["latency"] = self.build_stats_map(view_obj, self.LATENCY_NAME_MAP) - - return stats, ports_stats diff --git a/tests/unit/network_services/traffic_profile/__init__.py b/yardstick/network_services/libs/ixia_libs/ixnet/__init__.py index e69de29bb..e69de29bb 100644 --- a/tests/unit/network_services/traffic_profile/__init__.py +++ b/yardstick/network_services/libs/ixia_libs/ixnet/__init__.py diff --git a/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py b/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py new file mode 100644 index 000000000..393f60f7c --- /dev/null +++ b/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py @@ -0,0 +1,470 @@ +# Copyright (c) 2016-2017 Intel 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 IxNetwork + +from yardstick.common import exceptions +from yardstick.common import utils + + +log = logging.getLogger(__name__) + +IP_VERSION_4 = 4 +IP_VERSION_6 = 6 + +PROTO_ETHERNET = 'ethernet' +PROTO_IPV4 = 'ipv4' +PROTO_IPV6 = 'ipv6' +PROTO_UDP = 'udp' +PROTO_TCP = 'tcp' +PROTO_VLAN = 'vlan' + +IP_VERSION_4_MASK = '0.0.0.255' +IP_VERSION_6_MASK = '0:0:0:0:0:0:0:ff' + +TRAFFIC_STATUS_STARTED = 'started' +TRAFFIC_STATUS_STOPPED = 'stopped' + + +# NOTE(ralonsoh): this pragma will be removed in the last patch of this series +class IxNextgen(object): # pragma: no cover + + PORT_STATS_NAME_MAP = { + "stat_name": 'Stat Name', + "Frames_Tx": 'Frames Tx.', + "Valid_Frames_Rx": 'Valid Frames Rx.', + "Frames_Tx_Rate": 'Frames Tx. Rate', + "Valid_Frames_Rx_Rate": 'Valid Frames Rx. Rate', + "Tx_Rate_Kbps": 'Tx. Rate (Kbps)', + "Rx_Rate_Kbps": 'Rx. Rate (Kbps)', + "Tx_Rate_Mbps": 'Tx. Rate (Mbps)', + "Rx_Rate_Mbps": 'Rx. Rate (Mbps)', + } + + LATENCY_NAME_MAP = { + "Store-Forward_Avg_latency_ns": 'Store-Forward Avg Latency (ns)', + "Store-Forward_Min_latency_ns": 'Store-Forward Min Latency (ns)', + "Store-Forward_Max_latency_ns": 'Store-Forward Max Latency (ns)', + } + + @staticmethod + def get_config(tg_cfg): + card = [] + port = [] + external_interface = tg_cfg["vdu"][0]["external-interface"] + for intf in external_interface: + card_port0 = intf["virtual-interface"]["vpci"] + card0, port0 = card_port0.split(':')[:2] + card.append(card0) + port.append(port0) + + cfg = { + 'machine': tg_cfg["mgmt-interface"]["ip"], + 'port': tg_cfg["mgmt-interface"]["tg-config"]["tcl_port"], + 'chassis': tg_cfg["mgmt-interface"]["tg-config"]["ixchassis"], + 'cards': card, + 'ports': port, + 'output_dir': tg_cfg["mgmt-interface"]["tg-config"]["dut_result_dir"], + 'version': tg_cfg["mgmt-interface"]["tg-config"]["version"], + 'bidir': True, + } + + return cfg + + def __init__(self): # pragma: no cover + self._ixnet = None + self._cfg = None + self._params = None + self._bidir = None + + @property + def ixnet(self): # pragma: no cover + if self._ixnet: + return self._ixnet + raise exceptions.IxNetworkClientNotConnected() + + def _get_config_element_by_flow_group_name(self, flow_group_name): + """Get a config element using the flow group name + + Each named flow group contains one config element (by configuration). + According to the documentation, "configElements" is a list and "each + item in this list is aligned to the sequential order of your endpoint + list". + + :param flow_group_name: (str) flow group name; this parameter is + always a number (converted to string) starting + from "1". + :return: (str) config element reference ID or None. + """ + traffic_item = self.ixnet.getList(self.ixnet.getRoot() + '/traffic', + 'trafficItem')[0] + flow_groups = self.ixnet.getList(traffic_item, 'endpointSet') + for flow_group in flow_groups: + if (str(self.ixnet.getAttribute(flow_group, '-name')) == + flow_group_name): + return traffic_item + '/configElement:' + flow_group_name + + def _get_stack_item(self, flow_group_name, protocol_name): + """Return the stack item given the flow group name and the proto name + + :param flow_group_name: (str) flow group name + :param protocol_name: (str) protocol name, referred to PROTO_* + constants + :return: list of stack item descriptors + """ + celement = self._get_config_element_by_flow_group_name(flow_group_name) + if not celement: + raise exceptions.IxNetworkFlowNotPresent( + flow_group=flow_group_name) + stack_items = self.ixnet.getList(celement, 'stack') + return [s_i for s_i in stack_items if protocol_name in s_i] + + def _get_field_in_stack_item(self, stack_item, field_name): + """Return the field in a stack item given the name + + :param stack_item: (str) stack item descriptor + :param field_name: (str) field name + :return: (str) field descriptor + """ + fields = self.ixnet.getList(stack_item, 'field') + for field in (field for field in fields if field_name in field): + return field + raise exceptions.IxNetworkFieldNotPresentInStackItem( + field_name=field_name, stack_item=stack_item) + + def _get_traffic_state(self): + """Get traffic state""" + return self.ixnet.getAttribute(self.ixnet.getRoot() + 'traffic', + '-state') + + def is_traffic_running(self): + """Returns true if traffic state == TRAFFIC_STATUS_STARTED""" + return self._get_traffic_state() == TRAFFIC_STATUS_STARTED + + def is_traffic_stopped(self): + """Returns true if traffic state == TRAFFIC_STATUS_STOPPED""" + return self._get_traffic_state() == TRAFFIC_STATUS_STOPPED + + @staticmethod + def _parse_framesize(framesize): + """Parse "framesize" config param. to return a list of weighted pairs + + :param framesize: dictionary of frame sizes and weights + :return: list of paired frame sizes and weights + """ + weighted_range_pairs = [] + for size, weight in framesize.items(): + weighted_range_pairs.append(int(size.upper().replace('B', ''))) + weighted_range_pairs.append(int(weight)) + return weighted_range_pairs + + def iter_over_get_lists(self, x1, x2, y2, offset=0): + for x in self.ixnet.getList(x1, x2): + y_list = self.ixnet.getList(x, y2) + for i, y in enumerate(y_list, offset): + yield x, y, i + + def connect(self, tg_cfg): + self._cfg = self.get_config(tg_cfg) + self._ixnet = IxNetwork.IxNet() + + machine = self._cfg['machine'] + port = str(self._cfg['port']) + version = str(self._cfg['version']) + return self.ixnet.connect(machine, '-port', port, + '-version', version) + + def clear_config(self): + """Wipe out any possible configuration present in the client""" + self.ixnet.execute('newConfig') + + def assign_ports(self): + """Create and assign vports for each physical port defined in config + + This configuration is present in the IXIA profile file. E.g.: + name: trafficgen_1 + role: IxNet + interfaces: + xe0: + vpci: "2:15" # Card:port + driver: "none" + dpdk_port_num: 0 + local_ip: "152.16.100.20" + netmask: "255.255.0.0" + local_mac: "00:98:10:64:14:00" + xe1: + ... + """ + chassis_ip = self._cfg['chassis'] + ports = [(chassis_ip, card, port) for card, port in + zip(self._cfg['cards'], self._cfg['ports'])] + + log.info('Create and assign vports: %s', ports) + for port in ports: + vport = self.ixnet.add(self.ixnet.getRoot(), 'vport') + self.ixnet.commit() + self.ixnet.execute('assignPorts', [port], [], [vport], True) + self.ixnet.commit() + if self.ixnet.getAttribute(vport, '-state') != 'up': + log.warning('Port %s is down', vport) + + def _create_traffic_item(self): + """Create the traffic item to hold the flow groups + + The traffic item tracking by "Traffic Item" is enabled to retrieve the + latency statistics. + """ + log.info('Create the traffic item "RFC2544"') + traffic_item = self.ixnet.add(self.ixnet.getRoot() + '/traffic', + 'trafficItem') + self.ixnet.setMultiAttribute(traffic_item, '-name', 'RFC2544', + '-trafficType', 'raw') + self.ixnet.commit() + + traffic_item_id = self.ixnet.remapIds(traffic_item)[0] + self.ixnet.setAttribute(traffic_item_id + '/tracking', + '-trackBy', 'trafficGroupId0') + self.ixnet.commit() + + def _create_flow_groups(self): + """Create the flow groups between the assigned ports""" + traffic_item_id = self.ixnet.getList(self.ixnet.getRoot() + 'traffic', + 'trafficItem')[0] + log.info('Create the flow groups') + vports = self.ixnet.getList(self.ixnet.getRoot(), 'vport') + uplink_ports = vports[::2] + downlink_ports = vports[1::2] + index = 0 + for up, down in zip(uplink_ports, downlink_ports): + log.info('FGs: %s <--> %s', up, down) + endpoint_set_1 = self.ixnet.add(traffic_item_id, 'endpointSet') + endpoint_set_2 = self.ixnet.add(traffic_item_id, 'endpointSet') + self.ixnet.setMultiAttribute( + endpoint_set_1, '-name', str(index + 1), + '-sources', [up + '/protocols'], + '-destinations', [down + '/protocols']) + self.ixnet.setMultiAttribute( + endpoint_set_2, '-name', str(index + 2), + '-sources', [down + '/protocols'], + '-destinations', [up + '/protocols']) + self.ixnet.commit() + index += 2 + + def _append_procotol_to_stack(self, protocol_name, previous_element): + """Append a new element in the packet definition stack""" + protocol = (self.ixnet.getRoot() + + '/traffic/protocolTemplate:"{}"'.format(protocol_name)) + self.ixnet.execute('append', previous_element, protocol) + + def _setup_config_elements(self): + """Setup the config elements + + The traffic item is configured to allow individual configurations per + config element. The default frame configuration is applied: + Ethernet II: added by default + IPv4: element to add + UDP: element to add + Payload: added by default + Ethernet II (Trailer): added by default + :return: + """ + traffic_item_id = self.ixnet.getList(self.ixnet.getRoot() + 'traffic', + 'trafficItem')[0] + log.info('Split the frame rate distribution per config element') + config_elements = self.ixnet.getList(traffic_item_id, 'configElement') + for config_element in config_elements: + self.ixnet.setAttribute(config_element + '/frameRateDistribution', + '-portDistribution', 'splitRateEvenly') + self.ixnet.setAttribute(config_element + '/frameRateDistribution', + '-streamDistribution', 'splitRateEvenly') + self.ixnet.commit() + self._append_procotol_to_stack( + PROTO_UDP, config_element + '/stack:"ethernet-1"') + self._append_procotol_to_stack( + PROTO_IPV4, config_element + '/stack:"ethernet-1"') + + def create_traffic_model(self): + """Create a traffic item and the needed flow groups + + Each flow group inside the traffic item (only one is present) + represents the traffic between two ports: + (uplink) (downlink) + FlowGroup1: port1 -> port2 + FlowGroup2: port1 <- port2 + FlowGroup3: port3 -> port4 + FlowGroup4: port3 <- port4 + """ + self._create_traffic_item() + self._create_flow_groups() + self._setup_config_elements() + + def _update_frame_mac(self, ethernet_descriptor, field, mac_address): + """Set the MAC address in a config element stack Ethernet field + + :param ethernet_descriptor: (str) ethernet descriptor, e.g.: + /traffic/trafficItem:1/configElement:1/stack:"ethernet-1" + :param field: (str) field name, e.g.: destinationAddress + :param mac_address: (str) MAC address + """ + field_descriptor = self._get_field_in_stack_item(ethernet_descriptor, + field) + self.ixnet.setMultiAttribute(field_descriptor, + '-singleValue', mac_address, + '-fieldValue', mac_address, + '-valueType', 'singleValue') + self.ixnet.commit() + + def update_frame(self, traffic): + """Update the L2 frame + + This function updates the L2 frame options: + - Traffic type: "continuous", "fixedDuration". + - Duration: in case of traffic_type="fixedDuration", amount of seconds + to inject traffic. + - Rate: in frames per seconds or percentage. + - Type of rate: "framesPerSecond" ("bitsPerSecond" and + "percentLineRate" no used) + - Frame size: custom IMIX [1] definition; a list of packet size in + bytes and the weight. E.g.: + [64, 10, 128, 15, 512, 5] + + [1] https://en.wikipedia.org/wiki/Internet_Mix + + :param traffic: list of traffic elements; each traffic element contains + the injection parameter for each flow group. + """ + for traffic_param in traffic.values(): + fg_id = str(traffic_param['id']) + config_element = self._get_config_element_by_flow_group_name(fg_id) + if not config_element: + raise exceptions.IxNetworkFlowNotPresent(flow_group=fg_id) + + type = traffic_param.get('traffic_type', 'fixedDuration') + duration = traffic_param.get('duration', 30) + rate = traffic_param['iload'] + weighted_range_pairs = self._parse_framesize( + traffic_param['outer_l2']['framesize']) + srcmac = str(traffic_param.get('srcmac', '00:00:00:00:00:01')) + dstmac = str(traffic_param.get('dstmac', '00:00:00:00:00:02')) + # NOTE(ralonsoh): add QinQ tagging when + # traffic_param['outer_l2']['QinQ'] exists. + # s_vlan = traffic_param['outer_l2']['QinQ']['S-VLAN'] + # c_vlan = traffic_param['outer_l2']['QinQ']['C-VLAN'] + + self.ixnet.setMultiAttribute( + config_element + '/transmissionControl', + '-type', type, '-duration', duration) + self.ixnet.setMultiAttribute( + config_element + '/frameRate', + '-rate', rate, '-type', 'framesPerSecond') + self.ixnet.setMultiAttribute( + config_element + '/frameSize', + '-type', 'weightedPairs', + '-weightedRangePairs', weighted_range_pairs) + self.ixnet.commit() + + self._update_frame_mac( + self._get_stack_item(fg_id, PROTO_ETHERNET)[0], + 'destinationAddress', dstmac) + self._update_frame_mac( + self._get_stack_item(fg_id, PROTO_ETHERNET)[0], + 'sourceAddress', srcmac) + + def _update_ipv4_address(self, ip_descriptor, field, ip_address, seed, + mask, count): + """Set the IPv4 address in a config element stack IP field + + :param ip_descriptor: (str) IP descriptor, e.g.: + /traffic/trafficItem:1/configElement:1/stack:"ipv4-2" + :param field: (str) field name, e.g.: scrIp, dstIp + :param ip_address: (str) IP address + :param seed: (int) seed length + :param mask: (str) IP address mask + :param count: (int) number of random IPs to generate + """ + field_descriptor = self._get_field_in_stack_item(ip_descriptor, + field) + self.ixnet.setMultiAttribute(field_descriptor, + '-seed', seed, + '-fixedBits', ip_address, + '-randomMask', mask, + '-valueType', 'random', + '-countValue', count) + self.ixnet.commit() + + def update_ip_packet(self, traffic): + """Update the IP packet + + NOTE: Only IPv4 is currently supported. + :param traffic: list of traffic elements; each traffic element contains + the injection parameter for each flow group. + """ + # NOTE(ralonsoh): L4 configuration is not set. + for traffic_param in traffic.values(): + fg_id = str(traffic_param['id']) + if not self._get_config_element_by_flow_group_name(fg_id): + raise exceptions.IxNetworkFlowNotPresent(flow_group=fg_id) + + count = traffic_param['outer_l3']['count'] + srcip4 = str(traffic_param['outer_l3']['srcip4']) + dstip4 = str(traffic_param['outer_l3']['dstip4']) + + self._update_ipv4_address( + self._get_stack_item(fg_id, PROTO_IPV4)[0], + 'srcIp', srcip4, 1, IP_VERSION_4_MASK, count) + self._update_ipv4_address( + self._get_stack_item(fg_id, PROTO_IPV4)[0], + 'dstIp', dstip4, 1, IP_VERSION_4_MASK, count) + + def _build_stats_map(self, view_obj, name_map): + return {data_yardstick: self.ixnet.execute( + 'getColumnValues', view_obj, data_ixia) + for data_yardstick, data_ixia in name_map.items()} + + def get_statistics(self): + """Retrieve port and flow statistics + + "Port Statistics" parameters are stored in self.PORT_STATS_NAME_MAP. + "Flow Statistics" parameters are stored in self.LATENCY_NAME_MAP. + + :return: dictionary with the statistics; the keys of this dictionary + are PORT_STATS_NAME_MAP and LATENCY_NAME_MAP keys. + """ + port_statistics = '::ixNet::OBJ-/statistics/view:"Port Statistics"' + flow_statistics = '::ixNet::OBJ-/statistics/view:"Flow Statistics"' + stats = self._build_stats_map(port_statistics, + self.PORT_STATS_NAME_MAP) + stats.update(self._build_stats_map(flow_statistics, + self.LATENCY_NAME_MAP)) + return stats + + def start_traffic(self): + """Start the traffic injection in the traffic item + + By configuration, there is only one traffic item. This function returns + when the traffic state is TRAFFIC_STATUS_STARTED. + """ + traffic_items = self.ixnet.getList('/traffic', 'trafficItem') + if self.is_traffic_running(): + self.ixnet.execute('stop', '/traffic') + # pylint: disable=unnecessary-lambda + utils.wait_until_true(lambda: self.is_traffic_stopped()) + + self.ixnet.execute('generate', traffic_items) + self.ixnet.execute('apply', '/traffic') + self.ixnet.execute('start', '/traffic') + # pylint: disable=unnecessary-lambda + utils.wait_until_true(lambda: self.is_traffic_running()) diff --git a/yardstick/network_services/nfvi/resource.py b/yardstick/network_services/nfvi/resource.py index dc5c46a86..0c0bf223a 100644 --- a/yardstick/network_services/nfvi/resource.py +++ b/yardstick/network_services/nfvi/resource.py @@ -27,6 +27,7 @@ from oslo_config import cfg from oslo_utils.encodeutils import safe_decode from yardstick import ssh +from yardstick.common.exceptions import ResourceCommandError from yardstick.common.task_template import finalize_for_yaml from yardstick.common.utils import validate_non_string_sequence from yardstick.network_services.nfvi.collectd import AmqpConsumer @@ -249,45 +250,46 @@ class ResourceProfile(object): if status != 0: LOG.error("cannot find OVS socket %s", socket_path) + def _start_rabbitmq(self, connection): + # Reset amqp queue + LOG.debug("reset and setup amqp to collect data from collectd") + # ensure collectd.conf.d exists to avoid error/warning + cmd_list = ["sudo mkdir -p /etc/collectd/collectd.conf.d", + "sudo service rabbitmq-server restart", + "sudo rabbitmqctl stop_app", + "sudo rabbitmqctl reset", + "sudo rabbitmqctl start_app", + "sudo rabbitmqctl add_user admin admin", + "sudo rabbitmqctl authenticate_user admin admin", + "sudo rabbitmqctl set_permissions -p / admin '.*' '.*' '.*'" + ] + for cmd in cmd_list: + exit_status, stdout, stderr = connection.execute(cmd) + if exit_status != 0: + raise ResourceCommandError(command=cmd, stderr=stderr) + + # check stdout for "sudo rabbitmqctl status" command + cmd = "sudo rabbitmqctl status" + _, stdout, stderr = connection.execute(cmd) + if not re.search("RabbitMQ", stdout): + LOG.error("rabbitmqctl status don't have RabbitMQ in running apps") + raise ResourceCommandError(command=cmd, stderr=stderr) + def _start_collectd(self, connection, bin_path): LOG.debug("Starting collectd to collect NFVi stats") - connection.execute('sudo pkill -x -9 collectd') collectd_path = os.path.join(bin_path, "collectd", "sbin", "collectd") config_file_path = os.path.join(bin_path, "collectd", "etc") + self._prepare_collectd_conf(config_file_path) + + connection.execute('sudo pkill -x -9 collectd') exit_status = connection.execute("which %s > /dev/null 2>&1" % collectd_path)[0] if exit_status != 0: LOG.warning("%s is not present disabling", collectd_path) - # disable auto-provisioning because it requires Internet access - # collectd_installer = os.path.join(bin_path, "collectd.sh") - # provision_tool(connection, collectd) - # http_proxy = os.environ.get('http_proxy', '') - # https_proxy = os.environ.get('https_proxy', '') - # connection.execute("sudo %s '%s' '%s'" % ( - # collectd_installer, http_proxy, https_proxy)) return if "ovs_stats" in self.plugins: self._setup_ovs_stats(connection) LOG.debug("Starting collectd to collect NFVi stats") - # ensure collectd.conf.d exists to avoid error/warning - connection.execute("sudo mkdir -p /etc/collectd/collectd.conf.d") - self._prepare_collectd_conf(config_file_path) - - # Reset amqp queue - LOG.debug("reset and setup amqp to collect data from collectd") - connection.execute("sudo rm -rf /var/lib/rabbitmq/mnesia/rabbit*") - connection.execute("sudo service rabbitmq-server start") - connection.execute("sudo rabbitmqctl stop_app") - connection.execute("sudo rabbitmqctl reset") - connection.execute("sudo rabbitmqctl start_app") - connection.execute("sudo service rabbitmq-server restart") - - LOG.debug("Creating admin user for rabbitmq in order to collect data from collectd") - connection.execute("sudo rabbitmqctl delete_user guest") - connection.execute("sudo rabbitmqctl add_user admin admin") - connection.execute("sudo rabbitmqctl authenticate_user admin admin") - connection.execute("sudo rabbitmqctl set_permissions -p / admin '.*' '.*' '.*'") - LOG.debug("Start collectd service..... %s second timeout", self.timeout) # intel_pmu plug requires large numbers of files open, so try to set # ulimit -n to a large value @@ -299,9 +301,10 @@ class ResourceProfile(object): """ Start system agent for NFVi collection on host """ if self.enable: try: + self._start_rabbitmq(self.connection) self._start_collectd(self.connection, bin_path) - except Exception: - LOG.exception("Exception during collectd start") + except ResourceCommandError as e: + LOG.exception("Exception during collectd and rabbitmq start: %s", str(e)) raise def start(self): diff --git a/yardstick/network_services/traffic_profile/base.py b/yardstick/network_services/traffic_profile/base.py index 162bab2bc..9eba550aa 100644 --- a/yardstick/network_services/traffic_profile/base.py +++ b/yardstick/network_services/traffic_profile/base.py @@ -44,7 +44,7 @@ class TrafficProfile(object): # IMIX = {"10K": 0.1, "100M": 0.5} self.params = tp_config - def execute_traffic(self, traffic_generator): + def execute_traffic(self, traffic_generator, **kawrgs): """ This methods defines the behavior of the traffic generator. It will be called in a loop until the traffic generator exits. diff --git a/yardstick/network_services/traffic_profile/http_ixload.py b/yardstick/network_services/traffic_profile/http_ixload.py index 348056551..6cbdb8ab2 100644 --- a/yardstick/network_services/traffic_profile/http_ixload.py +++ b/yardstick/network_services/traffic_profile/http_ixload.py @@ -12,9 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from __future__ import absolute_import -from __future__ import print_function - import sys import os import logging @@ -27,22 +24,14 @@ try: except ImportError: import json as jsonutils - -class ErrorClass(object): - - def __init__(self, *args, **kwargs): - if 'test' not in kwargs: - raise RuntimeError - - def __getattr__(self, item): - raise AttributeError - +from yardstick.common import exceptions try: from IxLoad import IxLoad, StatCollectorUtils except ImportError: - IxLoad = ErrorClass - StatCollectorUtils = ErrorClass + IxLoad = exceptions.ErrorClass + StatCollectorUtils = exceptions.ErrorClass + LOG = logging.getLogger(__name__) CSV_FILEPATH_NAME = 'IxL_statResults.csv' @@ -93,7 +82,7 @@ def validate_non_string_sequence(value, default=None, raise_exc=None): if isinstance(value, collections.Sequence) and not isinstance(value, str): return value if raise_exc: - raise raise_exc + raise raise_exc # pylint: disable=raising-bad-type return default @@ -218,7 +207,7 @@ class IXLOADHttpTest(object): # ---- Remap ports ---- try: self.reassign_ports(test, repository, self.ports_to_reassign) - except Exception: + except Exception: # pylint: disable=broad-except LOG.exception("Exception occurred during reassign_ports") # ----------------------------------------------------------------------- diff --git a/yardstick/network_services/traffic_profile/ixia_rfc2544.py b/yardstick/network_services/traffic_profile/ixia_rfc2544.py index 7f047226b..73806f958 100644 --- a/yardstick/network_services/traffic_profile/ixia_rfc2544.py +++ b/yardstick/network_services/traffic_profile/ixia_rfc2544.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from __future__ import absolute_import import logging from yardstick.network_services.traffic_profile.trex_traffic_profile import \ @@ -82,13 +81,10 @@ class IXIARFC2544Profile(TrexProfile): def _ixia_traffic_generate(self, traffic, ixia_obj): for key, value in traffic.items(): if key.startswith((self.UPLINK, self.DOWNLINK)): - value["iload"] = str(self.rate) - ixia_obj.ix_update_frame(traffic) - ixia_obj.ix_update_ether(traffic) - ixia_obj.add_ip_header(traffic, 4) - ixia_obj.ix_start_traffic() - self.tmp_drop = 0 - self.tmp_throughput = 0 + value['iload'] = str(self.rate) + ixia_obj.update_frame(traffic) + ixia_obj.update_ip_packet(traffic) + ixia_obj.start_traffic() def update_traffic_profile(self, traffic_generator): def port_generator(): @@ -106,78 +102,59 @@ class IXIARFC2544Profile(TrexProfile): self.ports = [port for port in port_generator()] - def execute_traffic(self, traffic_generator, ixia_obj, mac=None): - if mac is None: - mac = {} + def execute_traffic(self, traffic_generator, ixia_obj=None, mac=None): + mac = {} if mac is None else mac + first_run = self.first_run if self.first_run: + self.first_run = False self.full_profile = {} self.pg_id = 0 self.update_traffic_profile(traffic_generator) - traffic = \ - self._get_ixia_traffic_profile(self.full_profile, mac) self.max_rate = self.rate self.min_rate = 0 - self.get_multiplier() - self._ixia_traffic_generate(traffic, ixia_obj) - - def get_multiplier(self): - self.rate = round((self.max_rate + self.min_rate) / 2.0, 2) - multiplier = round(self.rate / self.pps, 2) - return str(multiplier) + else: + self.rate = round(float(self.max_rate + self.min_rate) / 2.0, 2) - def start_ixia_latency(self, traffic_generator, ixia_obj, mac=None): - if mac is None: - mac = {} - self.update_traffic_profile(traffic_generator) - traffic = \ - self._get_ixia_traffic_profile(self.full_profile, mac) + traffic = self._get_ixia_traffic_profile(self.full_profile, mac) self._ixia_traffic_generate(traffic, ixia_obj) + return first_run - def get_drop_percentage(self, samples, tol_min, tolerance, ixia_obj, - mac=None): - if mac is None: - mac = {} - status = 'Running' + def get_drop_percentage(self, samples, tol_min, tolerance, duration=30.0, + first_run=False): + completed = False drop_percent = 100 - in_packets = sum([samples[iface]['in_packets'] for iface in samples]) - out_packets = sum([samples[iface]['out_packets'] for iface in samples]) - rx_throughput = \ - sum([samples[iface]['RxThroughput'] for iface in samples]) - tx_throughput = \ - sum([samples[iface]['TxThroughput'] for iface in samples]) - packet_drop = abs(out_packets - in_packets) + num_ifaces = len(samples) + in_packets_sum = sum( + [samples[iface]['in_packets'] for iface in samples]) + out_packets_sum = sum( + [samples[iface]['out_packets'] for iface in samples]) + rx_throughput = sum( + [samples[iface]['RxThroughput'] for iface in samples]) + rx_throughput = round(float(rx_throughput), 2) + tx_throughput = sum( + [samples[iface]['TxThroughput'] for iface in samples]) + tx_throughput = round(float(tx_throughput), 2) + packet_drop = abs(out_packets_sum - in_packets_sum) + try: - drop_percent = round((packet_drop / float(out_packets)) * 100, 2) + drop_percent = round( + (packet_drop / float(out_packets_sum)) * 100, 2) except ZeroDivisionError: LOG.info('No traffic is flowing') - samples['TxThroughput'] = round(tx_throughput / 1.0, 2) - samples['RxThroughput'] = round(rx_throughput / 1.0, 2) - samples['CurrentDropPercentage'] = drop_percent - samples['Throughput'] = self.tmp_throughput - samples['DropPercentage'] = self.tmp_drop - if drop_percent > tolerance and self.tmp_throughput == 0: - samples['Throughput'] = round(rx_throughput / 1.0, 2) - samples['DropPercentage'] = drop_percent - if self.first_run: - max_supported_rate = out_packets / 30.0 - self.rate = max_supported_rate - self.first_run = False - if drop_percent <= tolerance: - status = 'Completed' + + samples['TxThroughput'] = tx_throughput + samples['RxThroughput'] = rx_throughput + samples['DropPercentage'] = drop_percent + + if first_run: + self.rate = out_packets_sum / duration / num_ifaces + completed = True if drop_percent <= tolerance else False + if drop_percent > tolerance: self.max_rate = self.rate elif drop_percent < tol_min: self.min_rate = self.rate - if drop_percent >= self.tmp_drop: - self.tmp_drop = drop_percent - self.tmp_throughput = round((rx_throughput / 1.0), 2) - samples['Throughput'] = round(rx_throughput / 1.0, 2) - samples['DropPercentage'] = drop_percent else: - samples['Throughput'] = round(rx_throughput / 1.0, 2) - samples['DropPercentage'] = drop_percent - return status, samples - self.get_multiplier() - traffic = self._get_ixia_traffic_profile(self.full_profile, mac) - self._ixia_traffic_generate(traffic, ixia_obj) - return status, samples + completed = True + + return completed, samples diff --git a/yardstick/network_services/vnf_generic/vnf/acl_vnf.py b/yardstick/network_services/vnf_generic/vnf/acl_vnf.py index f3cafef7a..1357f6b26 100644 --- a/yardstick/network_services/vnf_generic/vnf/acl_vnf.py +++ b/yardstick/network_services/vnf_generic/vnf/acl_vnf.py @@ -13,16 +13,20 @@ # limitations under the License. import logging - +import ipaddress +import six from yardstick.common import utils +from yardstick.common import exceptions + from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNF, DpdkVnfSetupEnvHelper -from yardstick.network_services.yang_model import YangModel +from yardstick.network_services.helpers.samplevnf_helper import PortPairs +from itertools import chain LOG = logging.getLogger(__name__) # ACL should work the same on all systems, we can provide the binary ACL_PIPELINE_COMMAND = \ - 'sudo {tool_path} -p {port_mask_hex} -f {cfg_file} -s {script}' + 'sudo {tool_path} -p {port_mask_hex} -f {cfg_file} -s {script} {hwlb}' ACL_COLLECT_KPI = r"""\ ACL TOTAL:[^p]+pkts_processed"?:\s(\d+),[^p]+pkts_drop"?:\s(\d+),[^p]+pkts_received"?:\s(\d+),""" @@ -38,6 +42,196 @@ class AclApproxSetupEnvSetupEnvHelper(DpdkVnfSetupEnvHelper): SW_DEFAULT_CORE = 5 DEFAULT_CONFIG_TPL_CFG = "acl.cfg" VNF_TYPE = "ACL" + RULE_CMD = "acl" + + DEFAULT_PRIORITY = 1 + DEFAULT_PROTOCOL = 0 + DEFAULT_PROTOCOL_MASK = 0 + # Default actions to be applied to SampleVNF. Please note, + # that this list is extended with `fwd` action when default + # actions are generated. + DEFAULT_FWD_ACTIONS = ["accept", "count"] + + def __init__(self, vnfd_helper, ssh_helper, scenario_helper): + super(AclApproxSetupEnvSetupEnvHelper, self).__init__(vnfd_helper, + ssh_helper, + scenario_helper) + self._action_id = 0 + + def get_ip_from_port(self, port): + # we can't use gateway because in OpenStack gateways interfere with floating ip routing + # return self.make_ip_addr(self.get_ports_gateway(port), self.get_netmask_gateway(port)) + vintf = self.vnfd_helper.find_interface(name=port)["virtual-interface"] + return utils.make_ip_addr(vintf["local_ip"], vintf["netmask"]) + + def get_network_and_prefixlen_from_ip_of_port(self, port): + ip_addr = self.get_ip_from_port(port) + # handle cases with no gateway + if ip_addr: + return ip_addr.network.network_address.exploded, ip_addr.network.prefixlen + else: + return None, None + + @property + def new_action_id(self): + """Get new action id""" + self._action_id += 1 + return self._action_id + + def get_default_flows(self): + """Get default actions/rules + Returns: (<actions>, <rules>) + <actions>: + { <action_id>: [ <list of actions> ]} + Example: + { 0 : [ "accept", "count", {"fwd" : "port": 0} ], ... } + <rules>: + [ {"src_ip": "x.x.x.x", "src_ip_mask", 24, ...}, ... ] + Note: + See `generate_rule_cmds()` to get list of possible map keys. + """ + actions, rules = {}, [] + _port_pairs = PortPairs(self.vnfd_helper.interfaces) + port_pair_list = _port_pairs.port_pair_list + for src_intf, dst_intf in port_pair_list: + # get port numbers of the interfaces + src_port = self.vnfd_helper.port_num(src_intf) + dst_port = self.vnfd_helper.port_num(dst_intf) + # get interface addresses and prefixes + src_net, src_prefix_len = self.get_network_and_prefixlen_from_ip_of_port(src_intf) + dst_net, dst_prefix_len = self.get_network_and_prefixlen_from_ip_of_port(dst_intf) + # ignore entries with empty values + if all((src_net, src_prefix_len, dst_net, dst_prefix_len)): + # flow: src_net:dst_net -> dst_port + action_id = self.new_action_id + actions[action_id] = self.DEFAULT_FWD_ACTIONS[:] + actions[action_id].append({"fwd": {"port": dst_port}}) + rules.append({"priority": 1, 'cmd': self.RULE_CMD, + "src_ip": src_net, "src_ip_mask": src_prefix_len, + "dst_ip": dst_net, "dst_ip_mask": dst_prefix_len, + "src_port_from": 0, "src_port_to": 65535, + "dst_port_from": 0, "dst_port_to": 65535, + "protocol": 0, "protocol_mask": 0, + "action_id": action_id}) + # flow: dst_net:src_net -> src_port + action_id = self.new_action_id + actions[action_id] = self.DEFAULT_FWD_ACTIONS[:] + actions[action_id].append({"fwd": {"port": src_port}}) + rules.append({"cmd":self.RULE_CMD, "priority": 1, + "src_ip": dst_net, "src_ip_mask": dst_prefix_len, + "dst_ip": src_net, "dst_ip_mask": src_prefix_len, + "src_port_from": 0, "src_port_to": 65535, + "dst_port_from": 0, "dst_port_to": 65535, + "protocol": 0, "protocol_mask": 0, + "action_id": action_id}) + return actions, rules + + def get_flows(self, options): + """Get actions/rules based on provided options. + The `options` is a dict representing the ACL rules configuration + file. Result is the same as described in `get_default_flows()`. + """ + actions, rules = {}, [] + for ace in options['access-list-entries']: + # Generate list of actions + action_id = self.new_action_id + actions[action_id] = ace['actions'] + # Destination nestwork + matches = ace['matches'] + dst_ipv4_net = matches['destination-ipv4-network'] + dst_ipv4_net_ip = ipaddress.ip_interface(six.text_type(dst_ipv4_net)) + # Source network + src_ipv4_net = matches['source-ipv4-network'] + src_ipv4_net_ip = ipaddress.ip_interface(six.text_type(src_ipv4_net)) + # Append the rule + rules.append({'action_id': action_id, 'cmd': self.RULE_CMD, + 'dst_ip': dst_ipv4_net_ip.network.network_address.exploded, + 'dst_ip_mask': dst_ipv4_net_ip.network.prefixlen, + 'src_ip': src_ipv4_net_ip.network.network_address.exploded, + 'src_ip_mask': src_ipv4_net_ip.network.prefixlen, + 'dst_port_from': matches['destination-port-range']['lower-port'], + 'dst_port_to': matches['destination-port-range']['upper-port'], + 'src_port_from': matches['source-port-range']['lower-port'], + 'src_port_to': matches['source-port-range']['upper-port'], + 'priority': matches.get('priority', self.DEFAULT_PRIORITY), + 'protocol': matches.get('protocol', self.DEFAULT_PROTOCOL), + 'protocol_mask': matches.get('protocol_mask', + self.DEFAULT_PROTOCOL_MASK) + }) + return actions, rules + + def generate_rule_cmds(self, rules, apply_rules=False): + """Convert rules into list of SampleVNF CLI commands""" + rule_template = ("p {cmd} add {priority} {src_ip} {src_ip_mask} " + "{dst_ip} {dst_ip_mask} {src_port_from} {src_port_to} " + "{dst_port_from} {dst_port_to} {protocol} " + "{protocol_mask} {action_id}") + rule_cmd_list = [] + for rule in rules: + rule_cmd_list.append(rule_template.format(**rule)) + if apply_rules: + # add command to apply all rules at the end + rule_cmd_list.append("p {cmd} applyruleset".format(cmd=self.RULE_CMD)) + return rule_cmd_list + + def generate_action_cmds(self, actions): + """Convert actions into list of SampleVNF CLI commands. + These method doesn't validate the provided list of actions. Supported + list of actions are limited by SampleVNF. Thus, the user should be + responsible to specify correct action name(s). Yardstick should take + the provided action by user and apply it to SampleVNF. + Anyway, some of the actions require addition parameters to be + specified. In case of `fwd` & `nat` action used have to specify + the port attribute. + """ + _action_template_map = { + "fwd": "p action add {action_id} fwd {port}", + "nat": "p action add {action_id} nat {port}" + } + action_cmd_list = [] + for action_id, actions in actions.items(): + for action in actions: + if isinstance(action, dict): + for action_name in action.keys(): + # user provided an action name with addition options + # e.g.: {"fwd": {"port": 0}} + # format action CLI command and add it to the list + if action_name not in _action_template_map.keys(): + raise exceptions.AclUknownActionTemplate( + action_name=action_name) + template = _action_template_map[action_name] + try: + action_cmd_list.append(template.format( + action_id=action_id, **action[action_name])) + except KeyError as exp: + raise exceptions.AclMissingActionArguments( + action_name=action_name, + action_param=exp.args[0]) + else: + # user provided an action name w/o addition options + # e.g.: "accept", "count" + action_cmd_list.append( + "p action add {action_id} {action}".format( + action_id=action_id, action=action)) + return action_cmd_list + + def get_flows_config(self, options=None): + """Get action/rules configuration commands (string) to be + applied to SampleVNF to configure ACL rules (flows). + """ + action_cmd_list, rule_cmd_list = [], [] + if options: + # if file name is set, read actions/rules from the file + actions, rules = self.get_flows(options) + action_cmd_list = self.generate_action_cmds(actions) + rule_cmd_list = self.generate_rule_cmds(rules) + # default actions/rules + dft_actions, dft_rules = self.get_default_flows() + dft_action_cmd_list = self.generate_action_cmds(dft_actions) + dft_rule_cmd_list = self.generate_rule_cmds(dft_rules, apply_rules=True) + # generate multi-line commands to add actions/rules + return '\n'.join(chain(action_cmd_list, dft_action_cmd_list, + rule_cmd_list, dft_rule_cmd_list)) class AclApproxVnf(SampleVNF): @@ -57,12 +251,3 @@ class AclApproxVnf(SampleVNF): setup_env_helper_type = AclApproxSetupEnvSetupEnvHelper super(AclApproxVnf, self).__init__(name, vnfd, setup_env_helper_type, resource_helper_type) - self.acl_rules = None - - def _start_vnf(self): - yang_model_path = utils.find_relative_file( - self.scenario_helper.options['rules'], - self.scenario_helper.task_path) - yang_model = YangModel(yang_model_path) - self.acl_rules = yang_model.get_rules() - super(AclApproxVnf, self)._start_vnf() diff --git a/yardstick/network_services/vnf_generic/vnf/base.py b/yardstick/network_services/vnf_generic/vnf/base.py index a776b0989..9ceac3167 100644 --- a/yardstick/network_services/vnf_generic/vnf/base.py +++ b/yardstick/network_services/vnf_generic/vnf/base.py @@ -195,6 +195,18 @@ class GenericVNF(object): :return: {"kpi": value, "kpi2": value} """ + @abc.abstractmethod + def start_collect(self): + """Start KPI collection + :return: None + """ + + @abc.abstractmethod + def stop_collect(self): + """Stop KPI collection + :return: None + """ + @six.add_metaclass(abc.ABCMeta) class GenericTrafficGen(GenericVNF): @@ -254,3 +266,23 @@ class GenericTrafficGen(GenericVNF): :return: True/False """ pass + + def start_collect(self): + """Start KPI collection. + + Traffic measurements are always collected during injection. + + Optional. + + :return: True/False + """ + pass + + def stop_collect(self): + """Stop KPI collection. + + Optional. + + :return: True/False + """ + pass diff --git a/yardstick/network_services/vnf_generic/vnf/cgnapt_vnf.py b/yardstick/network_services/vnf_generic/vnf/cgnapt_vnf.py index 53f73b4d7..bfe628f09 100644 --- a/yardstick/network_services/vnf_generic/vnf/cgnapt_vnf.py +++ b/yardstick/network_services/vnf_generic/vnf/cgnapt_vnf.py @@ -21,10 +21,10 @@ from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNF, Dpd LOG = logging.getLogger(__name__) # CGNAPT should work the same on all systems, we can provide the binary -CGNAPT_PIPELINE_COMMAND = 'sudo {tool_path} -p {port_mask_hex} -f {cfg_file} -s {script}' +CGNAPT_PIPELINE_COMMAND = 'sudo {tool_path} -p {port_mask_hex} -f {cfg_file} -s {script} {hwlb}' WAIT_FOR_STATIC_NAPT = 4 -CGNAPT_COLLECT_KPI = """\ +CGNAPT_COLLECT_KPI = r"""\ CG-NAPT(.*\n)*\ Received\s(\d+),\ Missed\s(\d+),\ diff --git a/yardstick/network_services/vnf_generic/vnf/sample_vnf.py b/yardstick/network_services/vnf_generic/vnf/sample_vnf.py index d8b9625fb..02927295c 100644 --- a/yardstick/network_services/vnf_generic/vnf/sample_vnf.py +++ b/yardstick/network_services/vnf_generic/vnf/sample_vnf.py @@ -32,6 +32,7 @@ from yardstick.benchmark.contexts.base import Context from yardstick.common import exceptions as y_exceptions from yardstick.common.process import check_if_process_failed from yardstick.common import utils +from yardstick.common import yaml_loader from yardstick.network_services import constants from yardstick.network_services.helpers.dpdkbindnic_helper import DpdkBindHelper, DpdkNode from yardstick.network_services.helpers.samplevnf_helper import MultiPortConfig @@ -59,6 +60,7 @@ class SetupEnvHelper(object): self.vnfd_helper = vnfd_helper self.ssh_helper = ssh_helper self.scenario_helper = scenario_helper + self.collectd_options = {} def build_config(self): raise NotImplementedError @@ -143,6 +145,13 @@ class DpdkVnfSetupEnvHelper(SetupEnvHelper): 'vnf_type': self.VNF_TYPE, } + # read actions/rules from file + acl_options = None + acl_file_name = self.scenario_helper.options.get('rules') + if acl_file_name: + with utils.open_relative_file(acl_file_name, task_path) as infile: + acl_options = yaml_loader.yaml_load(infile) + config_tpl_cfg = utils.find_relative_file(self.DEFAULT_CONFIG_TPL_CFG, task_path) config_basename = posixpath.basename(self.CFG_CONFIG) @@ -175,12 +184,17 @@ class DpdkVnfSetupEnvHelper(SetupEnvHelper): new_config = self._update_packet_type(new_config, traffic_options) self.ssh_helper.upload_config_file(config_basename, new_config) self.ssh_helper.upload_config_file(script_basename, - multiport.generate_script(self.vnfd_helper)) + multiport.generate_script(self.vnfd_helper, + self.get_flows_config(acl_options))) LOG.info("Provision and start the %s", self.APP_NAME) self._build_pipeline_kwargs() return self.PIPELINE_COMMAND.format(**self.pipeline_kwargs) + def get_flows_config(self, options=None): # pylint: disable=unused-argument + """No actions/rules (flows) by default""" + return None + def _build_pipeline_kwargs(self): tool_path = self.ssh_helper.provision_tool(tool_file=self.APP_NAME) # count the number of actual ports in the list of pairs @@ -192,11 +206,20 @@ class DpdkVnfSetupEnvHelper(SetupEnvHelper): port_nums = self.vnfd_helper.port_nums(ports) # create mask from all the dpdk port numbers ports_mask_hex = hex(sum(2 ** num for num in port_nums)) + + vnf_cfg = self.scenario_helper.vnf_cfg + lb_config = vnf_cfg.get('lb_config', 'SW') + worker_threads = vnf_cfg.get('worker_threads', 3) + hwlb = '' + if lb_config == 'HW': + hwlb = ' --hwlb %s' % worker_threads + self.pipeline_kwargs = { 'cfg_file': self.CFG_CONFIG, 'script': self.CFG_SCRIPT, 'port_mask_hex': ports_mask_hex, 'tool_path': tool_path, + 'hwlb': hwlb, } def setup_vnf_environment(self): @@ -224,12 +247,6 @@ class DpdkVnfSetupEnvHelper(SetupEnvHelper): if exit_status == 0: return - def get_collectd_options(self): - options = self.scenario_helper.all_options.get("collectd", {}) - # override with specific node settings - options.update(self.scenario_helper.options.get("collectd", {})) - return options - def _setup_resources(self): # what is this magic? how do we know which socket is for which port? # what about quad-socket? @@ -242,11 +259,11 @@ class DpdkVnfSetupEnvHelper(SetupEnvHelper): # this won't work because we don't have DPDK port numbers yet ports = sorted(self.vnfd_helper.interfaces, key=self.vnfd_helper.port_num) port_names = (intf["name"] for intf in ports) - collectd_options = self.get_collectd_options() - plugins = collectd_options.get("plugins", {}) + plugins = self.collectd_options.get("plugins", {}) + interval = self.collectd_options.get("interval") # we must set timeout to be the same as the VNF otherwise KPIs will die before VNF return ResourceProfile(self.vnfd_helper.mgmt_interface, port_names=port_names, - plugins=plugins, interval=collectd_options.get("interval"), + plugins=plugins, interval=interval, timeout=self.scenario_helper.timeout) def _check_interface_fields(self): @@ -666,6 +683,7 @@ class SampleVNF(GenericVNF): pass 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 self.nfvi_context = Context.get_context_from_server(self.scenario_helper.nodes[self.name]) @@ -677,6 +695,54 @@ class SampleVNF(GenericVNF): self.resource_helper.setup() self._start_vnf() + def _update_collectd_options(self, scenario_cfg, context_cfg): + """Update collectd configuration options + This function retrieves all collectd options contained in the test case + + definition builds a single dictionary combining them. The following fragment + represents a test case with the collectd options and priorities (1 highest, 3 lowest): + --- + schema: yardstick:task:0.1 + scenarios: + - type: NSPerf + nodes: + tg__0: trafficgen_1.yardstick + vnf__0: vnf.yardstick + options: + collectd: + <options> # COLLECTD priority 3 + vnf__0: + collectd: + plugins: + load + <options> # COLLECTD priority 2 + context: + type: Node + name: yardstick + nfvi_type: baremetal + file: /etc/yardstick/nodes/pod_ixia.yaml # COLLECTD priority 1 + """ + 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): + """Update collectd options and plugins dictionary""" + 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): buf = [] time.sleep(self.WAIT_TIME) # Give some time for config to load @@ -692,7 +758,6 @@ class SampleVNF(GenericVNF): LOG.info("%s VNF is up and running.", self.APP_NAME) self._vnf_up_post() self.queue_wrapper.clear() - self.resource_helper.start_collect() return self._vnf_process.exitcode if "PANIC" in message: @@ -705,6 +770,12 @@ class SampleVNF(GenericVNF): # by other VNF output self.q_in.put('\r\n') + def start_collect(self): + self.resource_helper.start_collect() + + def stop_collect(self): + self.resource_helper.stop_collect() + def _build_run_kwargs(self): self.run_kwargs = { 'stdin': self.queue_wrapper, diff --git a/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py b/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py index 265d0b7a9..2010546e7 100644 --- a/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py +++ b/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py @@ -12,19 +12,17 @@ # See the License for the specific language governing permissions and # limitations under the License. -from __future__ import absolute_import - -import time import os import logging import sys +from yardstick.common import exceptions from yardstick.common import utils -from yardstick import error from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNFTrafficGen from yardstick.network_services.vnf_generic.vnf.sample_vnf import ClientResourceHelper from yardstick.network_services.vnf_generic.vnf.sample_vnf import Rfc2544ResourceHelper + LOG = logging.getLogger(__name__) WAIT_AFTER_CFG_LOAD = 10 @@ -36,7 +34,7 @@ sys.path.append(IXNET_LIB) try: from IxNet import IxNextgen except ImportError: - IxNextgen = error.ErrorClass + IxNextgen = exceptions.ErrorClass class IxiaRfc2544Helper(Rfc2544ResourceHelper): @@ -64,10 +62,10 @@ class IxiaResourceHelper(ClientResourceHelper): self._connect() def _connect(self, client=None): - self.client._connect(self.vnfd_helper) + self.client.connect(self.vnfd_helper) def get_stats(self, *args, **kwargs): - return self.client.ix_get_statistics() + return self.client.get_statistics() def stop_collect(self): self._terminated.value = 1 @@ -76,8 +74,6 @@ class IxiaResourceHelper(ClientResourceHelper): def generate_samples(self, ports, key=None, default=None): stats = self.get_stats() - last_result = stats[1] - latency = stats[0] samples = {} # this is not DPDK port num, but this is whatever number we gave @@ -88,19 +84,21 @@ class IxiaResourceHelper(ClientResourceHelper): intf = self.vnfd_helper.find_interface_by_port(port_num) port_name = intf["name"] samples[port_name] = { - "rx_throughput_kps": float(last_result["Rx_Rate_Kbps"][port_num]), - "tx_throughput_kps": float(last_result["Tx_Rate_Kbps"][port_num]), - "rx_throughput_mbps": float(last_result["Rx_Rate_Mbps"][port_num]), - "tx_throughput_mbps": float(last_result["Tx_Rate_Mbps"][port_num]), - "in_packets": int(last_result["Valid_Frames_Rx"][port_num]), - "out_packets": int(last_result["Frames_Tx"][port_num]), - "RxThroughput": int(last_result["Valid_Frames_Rx"][port_num]) / 30, - "TxThroughput": int(last_result["Frames_Tx"][port_num]) / 30, + "rx_throughput_kps": float(stats["Rx_Rate_Kbps"][port_num]), + "tx_throughput_kps": float(stats["Tx_Rate_Kbps"][port_num]), + "rx_throughput_mbps": float(stats["Rx_Rate_Mbps"][port_num]), + "tx_throughput_mbps": float(stats["Tx_Rate_Mbps"][port_num]), + "in_packets": int(stats["Valid_Frames_Rx"][port_num]), + "out_packets": int(stats["Frames_Tx"][port_num]), + # NOTE(ralonsoh): we need to make the traffic injection + # time variable. + "RxThroughput": int(stats["Valid_Frames_Rx"][port_num]) / 30, + "TxThroughput": int(stats["Frames_Tx"][port_num]) / 30, } if key: - avg_latency = latency["Store-Forward_Avg_latency_ns"][port_num] - min_latency = latency["Store-Forward_Min_latency_ns"][port_num] - max_latency = latency["Store-Forward_Max_latency_ns"][port_num] + avg_latency = stats["Store-Forward_Avg_latency_ns"][port_num] + min_latency = stats["Store-Forward_Min_latency_ns"][port_num] + max_latency = stats["Store-Forward_Max_latency_ns"][port_num] samples[port_name][key] = \ {"Store-Forward_Avg_latency_ns": avg_latency, "Store-Forward_Min_latency_ns": min_latency, @@ -110,6 +108,12 @@ class IxiaResourceHelper(ClientResourceHelper): return samples + def _initialize_client(self): + """Initialize the IXIA IxNetwork client and configure the server""" + self.client.clear_config() + self.client.assign_ports() + self.client.create_traffic_model() + def run_traffic(self, traffic_profile): if self._terminated.value: return @@ -119,16 +123,7 @@ class IxiaResourceHelper(ClientResourceHelper): default = "00:00:00:00:00:00" self._build_ports() - - # we don't know client_file_name until runtime as instantiate - client_file_name = \ - utils.find_relative_file( - self.scenario_helper.scenario_cfg['ixia_profile'], - self.scenario_helper.scenario_cfg["task_path"]) - self.client.ix_load_config(client_file_name) - time.sleep(WAIT_AFTER_CFG_LOAD) - - self.client.ix_assign_ports() + self._initialize_client() mac = {} for port_name in self.vnfd_helper.port_pairs.all_ports: @@ -140,43 +135,28 @@ class IxiaResourceHelper(ClientResourceHelper): mac["src_mac_{}".format(port_num)] = virt_intf.get("local_mac", default) mac["dst_mac_{}".format(port_num)] = virt_intf.get("dst_mac", default) - samples = {} - # Generate ixia traffic config... try: while not self._terminated.value: - traffic_profile.execute_traffic(self, self.client, mac) + first_run = traffic_profile.execute_traffic( + self, self.client, mac) self.client_started.value = 1 - time.sleep(WAIT_FOR_TRAFFIC) - self.client.ix_stop_traffic() + # pylint: disable=unnecessary-lambda + utils.wait_until_true(lambda: self.client.is_traffic_stopped()) samples = self.generate_samples(traffic_profile.ports) + + # NOTE(ralonsoh): the traffic injection duration is fixed to 30 + # seconds. This parameter is configurable and must be retrieved + # from the traffic_profile.full_profile information. + # Every flow must have the same duration. + completed, samples = traffic_profile.get_drop_percentage( + samples, min_tol, max_tol, first_run=first_run) self._queue.put(samples) - status, samples = traffic_profile.get_drop_percentage(samples, min_tol, - max_tol, self.client, mac) - current = samples['CurrentDropPercentage'] - if min_tol <= current <= max_tol or status == 'Completed': + if completed: self._terminated.value = 1 - self.client.ix_stop_traffic() - self._queue.put(samples) - - if not self.rfc_helper.is_done(): - self._terminated.value = 1 - return - - traffic_profile.execute_traffic(self, self.client, mac) - for _ in range(5): - time.sleep(self.LATENCY_TIME_SLEEP) - self.client.ix_stop_traffic() - samples = self.generate_samples(traffic_profile.ports, 'latency', {}) - self._queue.put(samples) - traffic_profile.start_ixia_latency(self, self.client, mac) - if self._terminated.value: - break - - self.client.ix_stop_traffic() except Exception: # pylint: disable=broad-except - LOG.exception("Run Traffic terminated") + LOG.exception('Run Traffic terminated') self._terminated.value = 1 diff --git a/yardstick/network_services/vnf_generic/vnf/vfw_vnf.py b/yardstick/network_services/vnf_generic/vnf/vfw_vnf.py index 61e99855f..432f30a0c 100644 --- a/yardstick/network_services/vnf_generic/vnf/vfw_vnf.py +++ b/yardstick/network_services/vnf_generic/vnf/vfw_vnf.py @@ -14,20 +14,19 @@ import logging -from yardstick.common import utils -from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNF, DpdkVnfSetupEnvHelper -from yardstick.network_services.yang_model import YangModel +from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNF +from yardstick.network_services.vnf_generic.vnf.acl_vnf import AclApproxSetupEnvSetupEnvHelper LOG = logging.getLogger(__name__) # vFW should work the same on all systems, we can provide the binary -FW_PIPELINE_COMMAND = """sudo {tool_path} -p {port_mask_hex} -f {cfg_file} -s {script}""" +FW_PIPELINE_COMMAND = "sudo {tool_path} -p {port_mask_hex} -f {cfg_file} -s {script} {hwlb}" FW_COLLECT_KPI = (r"""VFW TOTAL:[^p]+pkts_received"?:\s(\d+),[^p]+pkts_fw_forwarded"?:\s(\d+),""" r"""[^p]+pkts_drop_fw"?:\s(\d+),\s""") -class FWApproxSetupEnvHelper(DpdkVnfSetupEnvHelper): +class FWApproxSetupEnvHelper(AclApproxSetupEnvSetupEnvHelper): APP_NAME = "vFW" CFG_CONFIG = "/tmp/vfw_config" @@ -37,6 +36,8 @@ class FWApproxSetupEnvHelper(DpdkVnfSetupEnvHelper): SW_DEFAULT_CORE = 5 HW_DEFAULT_CORE = 2 VNF_TYPE = "VFW" + RULE_CMD = "vfw" + DEFAULT_FWD_ACTIONS = ["accept", "count", "conntrack"] class FWApproxVnf(SampleVNF): @@ -56,12 +57,3 @@ class FWApproxVnf(SampleVNF): setup_env_helper_type = FWApproxSetupEnvHelper super(FWApproxVnf, self).__init__(name, vnfd, setup_env_helper_type, resource_helper_type) - self.vfw_rules = None - - def _start_vnf(self): - yang_model_path = utils.find_relative_file( - self.scenario_helper.options['rules'], - self.scenario_helper.task_path) - yang_model = YangModel(yang_model_path) - self.vfw_rules = yang_model.get_rules() - super(FWApproxVnf, self)._start_vnf() diff --git a/yardstick/network_services/vnf_generic/vnf/vnf_ssh_helper.py b/yardstick/network_services/vnf_generic/vnf/vnf_ssh_helper.py index de6fd9329..6c5c6c833 100644 --- a/yardstick/network_services/vnf_generic/vnf/vnf_ssh_helper.py +++ b/yardstick/network_services/vnf_generic/vnf/vnf_ssh_helper.py @@ -47,6 +47,7 @@ class VnfSshHelper(AutoConnectSSH): def upload_config_file(self, prefix, content): cfg_file = os.path.join(constants.REMOTE_TMP, prefix) + LOG.debug('Config file name: %s', cfg_file) LOG.debug(content) file_obj = StringIO(content) self.put_file_obj(file_obj, cfg_file) diff --git a/yardstick/network_services/vnf_generic/vnf/vpe_vnf.py b/yardstick/network_services/vnf_generic/vnf/vpe_vnf.py index 077ce2385..0067f6bf9 100644 --- a/yardstick/network_services/vnf_generic/vnf/vpe_vnf.py +++ b/yardstick/network_services/vnf_generic/vnf/vpe_vnf.py @@ -31,7 +31,7 @@ from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNF, Dpd LOG = logging.getLogger(__name__) -VPE_PIPELINE_COMMAND = """sudo {tool_path} -p {port_mask_hex} -f {cfg_file} -s {script}""" +VPE_PIPELINE_COMMAND = "sudo {tool_path} -p {port_mask_hex} -f {cfg_file} -s {script} {hwlb}" VPE_COLLECT_KPI = """\ Pkts in:\\s(\\d+)\r\n\ diff --git a/yardstick/network_services/yang_model.py b/yardstick/network_services/yang_model.py deleted file mode 100644 index ec00c4513..000000000 --- a/yardstick/network_services/yang_model.py +++ /dev/null @@ -1,108 +0,0 @@ -# Copyright (c) 2017 Intel 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. - -from __future__ import absolute_import -from __future__ import print_function -import logging -import ipaddress -import six - -from yardstick.common.yaml_loader import yaml_load - -LOG = logging.getLogger(__name__) - - -class YangModel(object): - - RULE_TEMPLATE = "p acl add 1 {0} {1} {2} {3} {4} {5} {6} {7} 0 0 {8}" - - def __init__(self, config_file): - super(YangModel, self).__init__() - self._config_file = config_file - self._options = {} - self._rules = '' - - @property - def config_file(self): - return self._config_file - - @config_file.setter - def config_file(self, value): - self._config_file = value - self._options = {} - self._rules = '' - - def _read_config(self): - # TODO: add some error handling in case of empty or non-existing file - try: - with open(self._config_file) as f: - self._options = yaml_load(f) - except Exception as e: - LOG.exception("Failed to load the yaml %s", e) - raise - - def _get_entries(self): - if not self._options: - return '' - - rule_list = [] - for ace in self._options['access-list1']['acl']['access-list-entries']: - # TODO: resolve ports using topology file and nodes' - # ids: public or private. - matches = ace['ace']['matches'] - dst_ipv4_net = matches['destination-ipv4-network'] - dst_ipv4_net_ip = ipaddress.ip_interface(six.text_type(dst_ipv4_net)) - port0_local_network = dst_ipv4_net_ip.network.network_address.exploded - port0_prefix = dst_ipv4_net_ip.network.prefixlen - - src_ipv4_net = matches['source-ipv4-network'] - src_ipv4_net_ip = ipaddress.ip_interface(six.text_type(src_ipv4_net)) - port1_local_network = src_ipv4_net_ip.network.network_address.exploded - port1_prefix = src_ipv4_net_ip.network.prefixlen - - lower_dport = matches['destination-port-range']['lower-port'] - upper_dport = matches['destination-port-range']['upper-port'] - - lower_sport = matches['source-port-range']['lower-port'] - upper_sport = matches['source-port-range']['upper-port'] - - # TODO: proto should be read from file also. - # Now all rules in sample ACL file are TCP. - rule_list.append('') # get an extra new line - rule_list.append(self.RULE_TEMPLATE.format(port0_local_network, - port0_prefix, - port1_local_network, - port1_prefix, - lower_dport, - upper_dport, - lower_sport, - upper_sport, - 0)) - rule_list.append(self.RULE_TEMPLATE.format(port1_local_network, - port1_prefix, - port0_local_network, - port0_prefix, - lower_sport, - upper_sport, - lower_dport, - upper_dport, - 1)) - - self._rules = '\n'.join(rule_list) - - def get_rules(self): - if not self._rules: - self._read_config() - self._get_entries() - return self._rules diff --git a/yardstick/ssh.py b/yardstick/ssh.py index d7adc0d05..6b5e6faf4 100644 --- a/yardstick/ssh.py +++ b/yardstick/ssh.py @@ -62,15 +62,13 @@ Eventlet: sshclient = eventlet.import_patched("yardstick.ssh") """ -from __future__ import absolute_import -import os import io +import logging +import os +import re import select import socket import time -import re - -import logging import paramiko from chainmap import ChainMap @@ -78,6 +76,7 @@ from oslo_utils import encodeutils from scp import SCPClient import six +from yardstick.common import exceptions from yardstick.common.utils import try_int, NON_NONE_DEFAULT, make_dict_from_map from yardstick.network_services.utils import provision_tool @@ -90,12 +89,12 @@ def convert_key_to_str(key): return k.getvalue() -class SSHError(Exception): - pass - - -class SSHTimeout(SSHError): - pass +# class SSHError(Exception): +# pass +# +# +# class SSHTimeout(SSHError): +# pass class SSH(object): @@ -193,7 +192,7 @@ class SSH(object): return key_class.from_private_key(key) except paramiko.SSHException as e: errors.append(e) - raise SSHError("Invalid pkey: %s" % errors) + raise exceptions.SSHError(error_msg='Invalid pkey: %s' % errors) @property def is_connected(self): @@ -214,10 +213,10 @@ class SSH(object): return self._client except Exception as e: message = ("Exception %(exception_type)s was raised " - "during connect. Exception value is: %(exception)r") + "during connect. Exception value is: %(exception)r" % + {"exception": e, "exception_type": type(e)}) self._client = False - raise SSHError(message % {"exception": e, - "exception_type": type(e)}) + raise exceptions.SSHError(error_msg=message) def _make_dict(self): return { @@ -334,11 +333,11 @@ class SSH(object): break if timeout and (time.time() - timeout) > start_time: - args = {"cmd": cmd, "host": self.host} - raise SSHTimeout("Timeout executing command " - "'%(cmd)s' on host %(host)s" % args) + message = ('Timeout executing command %(cmd)s on host %(host)s' + % {"cmd": cmd, "host": self.host}) + raise exceptions.SSHTimeout(error_msg=message) if e: - raise SSHError("Socket error.") + raise exceptions.SSHError(error_msg='Socket error') exit_status = session.recv_exit_status() if exit_status != 0 and raise_on_error: @@ -346,7 +345,7 @@ class SSH(object): details = fmt % {"cmd": cmd, "status": exit_status} if stderr_data: details += " Last stderr data: '%s'." % stderr_data - raise SSHError(details) + raise exceptions.SSHError(error_msg=details) return exit_status def execute(self, cmd, stdin=None, timeout=3600): @@ -377,11 +376,12 @@ class SSH(object): while True: try: return self.execute("uname") - except (socket.error, SSHError) as e: + except (socket.error, exceptions.SSHError) as e: self.log.debug("Ssh is still unavailable: %r", e) time.sleep(interval) if time.time() > end_time: - raise SSHTimeout("Timeout waiting for '%s'" % self.host) + raise exceptions.SSHTimeout( + error_msg='Timeout waiting for "%s"' % self.host) def put(self, files, remote_path=b'.', recursive=False): client = self._get_client() @@ -486,11 +486,12 @@ class AutoConnectSSH(SSH): while True: try: return self._get_client() - except (socket.error, SSHError) as e: + except (socket.error, exceptions.SSHError) as e: self.log.debug("Ssh is still unavailable: %r", e) time.sleep(interval) if time.time() > end_time: - raise SSHTimeout("Timeout waiting for '%s'" % self.host) + raise exceptions.SSHTimeout( + error_msg='Timeout waiting for "%s"' % self.host) def drop_connection(self): """ Don't close anything, just force creation of a new client """ diff --git a/yardstick/tests/unit/benchmark/contexts/standalone/test_model.py b/yardstick/tests/unit/benchmark/contexts/standalone/test_model.py index b1dcee209..3d6fe183c 100644 --- a/yardstick/tests/unit/benchmark/contexts/standalone/test_model.py +++ b/yardstick/tests/unit/benchmark/contexts/standalone/test_model.py @@ -493,5 +493,5 @@ class OvsDeployTestCase(unittest.TestCase): 'ovs_version': ovs_version, 'dpdk_version': dpdk_version, 'proxy': 'test_proxy'}) - mock_execute.assert_called_once_with(cmd) - mock_env_get.assert_called_once_with('http_proxy', '') + mock_execute.assert_called_with(cmd) + mock_env_get.assert_called_with('http_proxy', '') diff --git a/yardstick/tests/unit/benchmark/contexts/standalone/test_sriov.py b/yardstick/tests/unit/benchmark/contexts/standalone/test_sriov.py index e70ab0ae8..b426985be 100644 --- a/yardstick/tests/unit/benchmark/contexts/standalone/test_sriov.py +++ b/yardstick/tests/unit/benchmark/contexts/standalone/test_sriov.py @@ -242,18 +242,19 @@ class SriovContextTestCase(unittest.TestCase): self.assertIsNone(self.sriov.configure_nics_for_sriov()) @mock.patch.object(ssh, 'SSH', return_value=(0, "a", "")) - @mock.patch.object(model, 'Libvirt') - def test__enable_interfaces(self, mock_libvirt, mock_ssh): - # pylint: disable=unused-argument - # NOTE(ralonsoh): the pylint exception should be removed. + @mock.patch.object(model.Libvirt, 'add_sriov_interfaces', + return_value='out_xml') + def test__enable_interfaces(self, mock_add_sriov, mock_ssh): self.sriov.vm_deploy = True self.sriov.connection = mock_ssh self.sriov.vm_names = ['vm_0', 'vm_1'] self.sriov.drivers = [] self.sriov.networks = self.NETWORKS - self.sriov.get_vf_data = mock.Mock(return_value="") - self.assertIsNone(self.sriov._enable_interfaces( - 0, 0, ["private_0"], 'test')) + self.assertEqual( + 'out_xml', + self.sriov._enable_interfaces(0, 0, ['private_0'], 'test')) + mock_add_sriov.assert_called_once_with( + '0000:00:0a.0', 0, self.NETWORKS['private_0']['mac'], 'test') @mock.patch.object(model.Libvirt, 'build_vm_xml') @mock.patch.object(model.Libvirt, 'check_if_vm_exists_and_delete') @@ -282,7 +283,9 @@ class SriovContextTestCase(unittest.TestCase): mock_build_vm_xml.return_value = (xml_out, '00:00:00:00:00:01') with mock.patch.object(self.sriov, 'vnf_node') as mock_vnf_node, \ - mock.patch.object(self.sriov, '_enable_interfaces'): + mock.patch.object(self.sriov, '_enable_interfaces') as \ + mock_enable_interfaces: + mock_enable_interfaces.return_value = 'out_xml' mock_vnf_node.generate_vnf_instance = mock.Mock( return_value='node') nodes_out = self.sriov.setup_sriov_context() @@ -294,7 +297,10 @@ class SriovContextTestCase(unittest.TestCase): connection, 'flavor', vm_name, 0) mock_create_vm.assert_called_once_with(connection, cfg) mock_check.assert_called_once_with(vm_name, connection) - mock_write_file.assert_called_once_with(cfg, xml_out) + mock_write_file.assert_called_once_with(cfg, 'out_xml') + mock_enable_interfaces.assert_has_calls([ + mock.call(0, mock.ANY, ['private_0'], mock.ANY), + mock.call(0, mock.ANY, ['public_0'], mock.ANY)], any_order=True) def test__get_vf_data(self): with mock.patch("yardstick.ssh.SSH") as ssh: diff --git a/yardstick/tests/unit/benchmark/contexts/test_base.py b/yardstick/tests/unit/benchmark/contexts/test_base.py index 153c6a527..1311317ac 100644 --- a/yardstick/tests/unit/benchmark/contexts/test_base.py +++ b/yardstick/tests/unit/benchmark/contexts/test_base.py @@ -12,12 +12,26 @@ # See the License for the specific language governing permissions and # limitations under the License. -import unittest - from yardstick.benchmark.contexts import base +from yardstick.tests.unit import base as ut_base + + +class DummyContextClass(base.Context): + + def _get_network(self, *args): + pass + + def _get_server(self, *args): + pass + + def deploy(self): + pass + + def undeploy(self): + pass -class FlagsTestCase(unittest.TestCase): +class FlagsTestCase(ut_base.BaseUnitTestCase): def setUp(self): self.flags = base.Flags() @@ -41,3 +55,32 @@ class FlagsTestCase(unittest.TestCase): self.flags.parse(foo=42) with self.assertRaises(AttributeError): _ = self.flags.foo + + +class ContextTestCase(ut_base.BaseUnitTestCase): + + @staticmethod + def _remove_ctx(ctx_obj): + if ctx_obj in base.Context.list: + base.Context.list.remove(ctx_obj) + + def test_split_host_name(self): + ctx_obj = DummyContextClass() + self.addCleanup(self._remove_ctx, ctx_obj) + config_name = 'host_name.ctx_name' + self.assertEqual(('host_name', 'ctx_name'), + ctx_obj.split_host_name(config_name)) + + def test_split_host_name_wrong_separator(self): + ctx_obj = DummyContextClass() + self.addCleanup(self._remove_ctx, ctx_obj) + config_name = 'host_name-ctx_name' + self.assertEqual((None, None), + ctx_obj.split_host_name(config_name)) + + def test_split_host_name_other_separator(self): + ctx_obj = DummyContextClass(host_name_separator='-') + self.addCleanup(self._remove_ctx, ctx_obj) + config_name = 'host_name-ctx_name' + self.assertEqual(('host_name', 'ctx_name'), + ctx_obj.split_host_name(config_name)) diff --git a/yardstick/tests/unit/benchmark/contexts/test_heat.py b/yardstick/tests/unit/benchmark/contexts/test_heat.py index 625f97bf4..8febfc9de 100644 --- a/yardstick/tests/unit/benchmark/contexts/test_heat.py +++ b/yardstick/tests/unit/benchmark/contexts/test_heat.py @@ -13,29 +13,26 @@ import logging import os import mock -import unittest +from yardstick import ssh from yardstick.benchmark.contexts import base from yardstick.benchmark.contexts import heat from yardstick.benchmark.contexts import model from yardstick.common import constants as consts from yardstick.common import exceptions as y_exc -from yardstick import ssh +from yardstick.tests.unit import base as ut_base LOG = logging.getLogger(__name__) -class HeatContextTestCase(unittest.TestCase): - - def __init__(self, *args, **kwargs): - super(HeatContextTestCase, self).__init__(*args, **kwargs) - self.name_iter = ('vnf{:03}'.format(x) for x in count(0, step=3)) +class HeatContextTestCase(ut_base.BaseUnitTestCase): def setUp(self): self.test_context = heat.HeatContext() self.addCleanup(self._remove_contexts) self.mock_context = mock.Mock(spec=heat.HeatContext()) + self.name_iter = ('vnf{:03}'.format(x) for x in count(0, step=3)) def _remove_contexts(self): if self.test_context in self.test_context.list: diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_basemonitor.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_basemonitor.py index ce972779d..8d042c406 100644 --- a/yardstick/tests/unit/benchmark/scenarios/availability/test_basemonitor.py +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_basemonitor.py @@ -7,6 +7,8 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## +import time + import mock import unittest @@ -86,13 +88,19 @@ class BaseMonitorTestCase(unittest.TestCase): 'sla': {'max_outage_time': 5} } + def _close_queue(self, instace): + time.sleep(0.1) + instace._queue.close() + def test__basemonitor_start_wait_successful(self): ins = basemonitor.BaseMonitor(self.monitor_cfg, None, {"nova-api": 10}) + self.addCleanup(self._close_queue, ins) ins.start_monitor() ins.wait_monitor() def test__basemonitor_all_successful(self): ins = self.MonitorSimple(self.monitor_cfg, None, {"nova-api": 10}) + self.addCleanup(self._close_queue, ins) ins.setup() ins.run() ins.verify_SLA() @@ -100,16 +108,12 @@ class BaseMonitorTestCase(unittest.TestCase): @mock.patch.object(basemonitor, 'multiprocessing') def test__basemonitor_func_false(self, mock_multiprocess): ins = self.MonitorSimple(self.monitor_cfg, None, {"nova-api": 10}) + self.addCleanup(self._close_queue, ins) ins.setup() mock_multiprocess.Event().is_set.return_value = False ins.run() ins.verify_SLA() - # TODO(elfoley): fix this test to not throw an error def test__basemonitor_getmonitorcls_successfule(self): - cls = None - try: - cls = basemonitor.BaseMonitor.get_monitor_cls(self.monitor_cfg) - except Exception: # pylint: disable=broad-except - pass - self.assertIsNone(cls) + with self.assertRaises(RuntimeError): + basemonitor.BaseMonitor.get_monitor_cls(self.monitor_cfg) diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py index 9bfbf0752..2885dc6fb 100644 --- a/yardstick/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py @@ -20,11 +20,11 @@ import mock import unittest from yardstick import tests +from yardstick.common import exceptions from yardstick.common import utils from yardstick.network_services.collector.subscriber import Collector from yardstick.network_services.traffic_profile import base from yardstick.network_services.vnf_generic import vnfdgen -from yardstick.error import IncorrectConfig from yardstick.network_services.vnf_generic.vnf.base import GenericTrafficGen from yardstick.network_services.vnf_generic.vnf.base import GenericVNF @@ -423,7 +423,7 @@ class TestNetworkServiceTestCase(unittest.TestCase): with mock.patch.dict(sys.modules, tests.STL_MOCKS): self.assertIsNotNone(self.s.get_vnf_impl(vnfd)) - with self.assertRaises(vnf_generic.IncorrectConfig) as raised: + with self.assertRaises(exceptions.IncorrectConfig) as raised: self.s.get_vnf_impl('NonExistentClass') exc_str = str(raised.exception) @@ -465,7 +465,7 @@ class TestNetworkServiceTestCase(unittest.TestCase): cfg_patch = mock.patch.object(self.s, 'context_cfg', cfg) with cfg_patch: - with self.assertRaises(IncorrectConfig): + with self.assertRaises(exceptions.IncorrectConfig): self.s.map_topology_to_infrastructure() def test_map_topology_to_infrastructure_config_invalid(self): @@ -482,7 +482,7 @@ class TestNetworkServiceTestCase(unittest.TestCase): config_patch = mock.patch.object(self.s, 'context_cfg', cfg) with config_patch: - with self.assertRaises(IncorrectConfig): + with self.assertRaises(exceptions.IncorrectConfig): self.s.map_topology_to_infrastructure() def test__resolve_topology_invalid_config(self): @@ -496,7 +496,7 @@ class TestNetworkServiceTestCase(unittest.TestCase): for interface in self.tg__1['interfaces'].values(): del interface['local_mac'] - with self.assertRaises(vnf_generic.IncorrectConfig) as raised: + with self.assertRaises(exceptions.IncorrectConfig) as raised: self.s._resolve_topology() self.assertIn('not found', str(raised.exception)) @@ -509,7 +509,7 @@ class TestNetworkServiceTestCase(unittest.TestCase): self.s.topology["vld"][0]['vnfd-connection-point-ref'].append( self.s.topology["vld"][0]['vnfd-connection-point-ref'][0]) - with self.assertRaises(vnf_generic.IncorrectConfig) as raised: + with self.assertRaises(exceptions.IncorrectConfig) as raised: self.s._resolve_topology() self.assertIn('wrong endpoint count', str(raised.exception)) @@ -518,7 +518,7 @@ class TestNetworkServiceTestCase(unittest.TestCase): self.s.topology["vld"][0]['vnfd-connection-point-ref'] = \ self.s.topology["vld"][0]['vnfd-connection-point-ref'][:1] - with self.assertRaises(vnf_generic.IncorrectConfig) as raised: + with self.assertRaises(exceptions.IncorrectConfig) as raised: self.s._resolve_topology() self.assertIn('wrong endpoint count', str(raised.exception)) diff --git a/yardstick/tests/unit/common/test_exceptions.py b/yardstick/tests/unit/common/test_exceptions.py new file mode 100644 index 000000000..884015536 --- /dev/null +++ b/yardstick/tests/unit/common/test_exceptions.py @@ -0,0 +1,28 @@ +# Copyright 2018 Intel 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. + +from yardstick.common import exceptions +from yardstick.tests.unit import base as ut_base + + +class ErrorClassTestCase(ut_base.BaseUnitTestCase): + + def test_init(self): + with self.assertRaises(RuntimeError): + exceptions.ErrorClass() + + def test_getattr(self): + error_instance = exceptions.ErrorClass(test='') + with self.assertRaises(AttributeError): + error_instance.get_name() diff --git a/yardstick/tests/unit/common/test_utils.py b/yardstick/tests/unit/common/test_utils.py index 9540a39e8..87995ac49 100644 --- a/yardstick/tests/unit/common/test_utils.py +++ b/yardstick/tests/unit/common/test_utils.py @@ -16,13 +16,14 @@ import mock import os import six from six.moves import configparser +import time import unittest import yardstick from yardstick import ssh -import yardstick.error -from yardstick.common import utils from yardstick.common import constants +from yardstick.common import utils +from yardstick.common import exceptions class IterSubclassesTestCase(unittest.TestCase): @@ -987,14 +988,6 @@ class TestUtils(unittest.TestCase): with self.assertRaises(RuntimeError): utils.validate_non_string_sequence(1, raise_exc=RuntimeError) - def test_error_class(self): - with self.assertRaises(RuntimeError): - yardstick.error.ErrorClass() - - error_instance = yardstick.error.ErrorClass(test='') - with self.assertRaises(AttributeError): - error_instance.get_name() - class TestUtilsIpAddrMethods(unittest.TestCase): @@ -1158,3 +1151,43 @@ class ReadMeminfoTestCase(unittest.TestCase): output = utils.read_meminfo(ssh_client) mock_get_client.assert_called_once_with('/proc/meminfo', mock.ANY) self.assertEqual(self.MEMINFO_DICT, output) + + +class TimerTestCase(unittest.TestCase): + + def test__getattr(self): + with utils.Timer() as timer: + time.sleep(1) + self.assertEqual(1, round(timer.total_seconds(), 0)) + self.assertEqual(1, timer.delta.seconds) + + def test__enter_with_timeout(self): + with utils.Timer(timeout=10) as timer: + time.sleep(1) + self.assertEqual(1, round(timer.total_seconds(), 0)) + + def test__enter_with_timeout_exception(self): + with self.assertRaises(exceptions.TimerTimeout): + with utils.Timer(timeout=1): + time.sleep(2) + + +class WaitUntilTrueTestCase(unittest.TestCase): + + def test_no_timeout(self): + self.assertIsNone(utils.wait_until_true(lambda: True, + timeout=1, sleep=1)) + + def test_timeout_generic_exception(self): + with self.assertRaises(exceptions.WaitTimeout): + self.assertIsNone(utils.wait_until_true(lambda: False, + timeout=1, sleep=1)) + + def test_timeout_given_exception(self): + class MyTimeoutException(exceptions.YardstickException): + message = 'My timeout exception' + + with self.assertRaises(MyTimeoutException): + self.assertIsNone( + utils.wait_until_true(lambda: False, timeout=1, sleep=1, + exception=MyTimeoutException)) diff --git a/yardstick/tests/unit/network_services/collector/test_subscriber.py b/yardstick/tests/unit/network_services/collector/test_subscriber.py index 9b9649979..4ec2ea7a2 100644 --- a/yardstick/tests/unit/network_services/collector/test_subscriber.py +++ b/yardstick/tests/unit/network_services/collector/test_subscriber.py @@ -17,6 +17,7 @@ import unittest import mock from yardstick.network_services.collector import subscriber +from yardstick import ssh class MockVnfAprrox(object): @@ -37,57 +38,41 @@ class MockVnfAprrox(object): class CollectorTestCase(unittest.TestCase): - NODES = { - 'node1': {}, - 'node2': { - 'ip': '1.2.3.4', - 'collectd': { - 'plugins': {'abc': 12, 'def': 34}, - 'interval': 987, - }, - }, - } - TRAFFIC_PROFILE = { - 'key1': 'value1', - } - def setUp(self): vnf = MockVnfAprrox() - self.ssh_patch = mock.patch('yardstick.network_services.nfvi.resource.ssh', autospec=True) + vnf.start_collect = mock.Mock() + vnf.stop_collect = mock.Mock() + self.ssh_patch = mock.patch.object(ssh, 'AutoConnectSSH') mock_ssh = self.ssh_patch.start() mock_instance = mock.Mock() mock_instance.execute.return_value = 0, '', '' - mock_ssh.AutoConnectSSH.from_node.return_value = mock_instance - self.collector = subscriber.Collector([vnf], self.NODES, self.TRAFFIC_PROFILE, 1800) + mock_ssh.from_node.return_value = mock_instance + self.collector = subscriber.Collector([vnf]) def tearDown(self): self.ssh_patch.stop() def test___init__(self, *_): vnf = MockVnfAprrox() - collector = subscriber.Collector([vnf], {}, {}) + collector = subscriber.Collector([vnf]) self.assertEqual(len(collector.vnfs), 1) - self.assertEqual(collector.traffic_profile, {}) - - def test___init___with_data(self, *_): - self.assertEqual(len(self.collector.vnfs), 1) - self.assertDictEqual(self.collector.traffic_profile, self.TRAFFIC_PROFILE) - self.assertEqual(len(self.collector.resource_profiles), 1) - - def test___init___negative(self, *_): - pass def test_start(self, *_): - self.assertIsNone(self.collector.start()) + self.collector.start() + for vnf in self.collector.vnfs: + vnf.start_collect.assert_called_once() def test_stop(self, *_): self.assertIsNone(self.collector.stop()) + for vnf in self.collector.vnfs: + vnf.stop_collect.assert_called_once() def test_get_kpi(self, *_): result = self.collector.get_kpi() + self.assertEqual(1, len(result)) + self.assertEqual(4, len(result["vnf__1"])) self.assertEqual(result["vnf__1"]["pkt_in_up_stream"], 100) self.assertEqual(result["vnf__1"]["pkt_drop_up_stream"], 5) self.assertEqual(result["vnf__1"]["pkt_in_down_stream"], 50) self.assertEqual(result["vnf__1"]["pkt_drop_down_stream"], 40) - self.assertIn('node2', result) diff --git a/yardstick/tests/unit/network_services/libs/ixia_libs/test_IxNet.py b/yardstick/tests/unit/network_services/libs/ixia_libs/test_IxNet.py deleted file mode 100644 index fe750e5ba..000000000 --- a/yardstick/tests/unit/network_services/libs/ixia_libs/test_IxNet.py +++ /dev/null @@ -1,870 +0,0 @@ -# Copyright (c) 2016-2017 Intel 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.libs.ixia_libs.IxNet.IxNet import IxNextgen -from yardstick.network_services.libs.ixia_libs.IxNet.IxNet import IP_VERSION_4 -from yardstick.network_services.libs.ixia_libs.IxNet.IxNet import IP_VERSION_6 - -UPLINK = "uplink" -DOWNLINK = "downlink" - - -class TestIxNextgen(unittest.TestCase): - - def test___init__(self): - ixnet_gen = IxNextgen() - self.assertIsNone(ixnet_gen._bidir) - - @mock.patch("yardstick.network_services.libs.ixia_libs.IxNet.IxNet.sys") - def test_connect(self, *args): - - ixnet_gen = IxNextgen() - ixnet_gen.get_config = mock.MagicMock() - ixnet_gen.get_ixnet = mock.MagicMock() - - self.assertRaises(ImportError, ixnet_gen._connect, - {"py_lib_path": "/tmp"}) - - def test_clear_ixia_config(self): - ixnet = mock.MagicMock() - ixnet.execute = mock.Mock() - - ixnet_gen = IxNextgen(ixnet) - - result = ixnet_gen.clear_ixia_config() - self.assertIsNone(result) - self.assertEqual(ixnet.execute.call_count, 1) - - def test_load_ixia_profile(self): - ixnet = mock.MagicMock() - ixnet.execute = mock.Mock() - - ixnet_gen = IxNextgen(ixnet) - - result = ixnet_gen.load_ixia_profile({}) - self.assertIsNone(result) - self.assertEqual(ixnet.execute.call_count, 1) - - def test_load_ixia_config(self): - ixnet = mock.MagicMock() - ixnet.execute = mock.Mock() - - ixnet_gen = IxNextgen(ixnet) - - result = ixnet_gen.ix_load_config({}) - self.assertIsNone(result) - self.assertEqual(ixnet.execute.call_count, 2) - - @mock.patch('yardstick.network_services.libs.ixia_libs.IxNet.IxNet.log') - def test_ix_assign_ports(self, mock_logger): - ixnet = mock.MagicMock() - ixnet.getList.return_value = [0, 1] - ixnet.getAttribute.side_effect = ['up', 'down'] - - config = { - 'chassis': '1.1.1.1', - 'cards': ['1', '2'], - 'ports': ['2', '2'], - } - - ixnet_gen = IxNextgen(ixnet) - ixnet_gen._cfg = config - - result = ixnet_gen.ix_assign_ports() - self.assertIsNone(result) - self.assertEqual(ixnet.execute.call_count, 1) - self.assertEqual(ixnet.commit.call_count, 1) - self.assertEqual(ixnet.getAttribute.call_count, 2) - self.assertEqual(mock_logger.error.call_count, 1) - - def test_ix_update_frame(self): - static_traffic_params = { - UPLINK: { - "id": 1, - "bidir": "False", - "duration": 60, - "iload": "100", - "outer_l2": { - "dstmac": "00:00:00:00:00:03", - "framesPerSecond": True, - "framesize": { - "64B": "100", - "1KB": "0", - }, - "srcmac": "00:00:00:00:00:01" - }, - "outer_l3": { - "dscp": 0, - "dstip4": "152.16.40.20", - "proto": "udp", - "srcip4": "152.16.100.20", - "ttl": 32 - }, - "outer_l3v4": { - "dscp": 0, - "dstip4": "152.16.40.20", - "proto": "udp", - "srcip4": "152.16.100.20", - "ttl": 32 - }, - "outer_l3v6": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l4": { - "dstport": "2001", - "srcport": "1234" - }, - "traffic_type": "continuous" - }, - DOWNLINK: { - "id": 2, - "bidir": "False", - "duration": 60, - "iload": "100", - "outer_l2": { - "dstmac": "00:00:00:00:00:04", - "framesPerSecond": False, - "framesize": {"64B": "100"}, - "srcmac": "00:00:00:00:00:01" - }, - "outer_l3": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l3v4": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l3v6": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l4": { - "dstport": "1234", - "srcport": "2001" - }, - "traffic_type": "continuous" - } - } - - ixnet = mock.MagicMock() - ixnet.remapIds.return_value = ["0"] - ixnet.setMultiAttribute.return_value = [1] - ixnet.commit.return_value = [1] - ixnet.getList.side_effect = [ - [1], - [1], - [1], - [ - "ethernet.header.destinationAddress", - "ethernet.header.sourceAddress", - ], - ] - - ixnet_gen = IxNextgen(ixnet) - - result = ixnet_gen.ix_update_frame(static_traffic_params) - self.assertIsNone(result) - self.assertEqual(ixnet.setMultiAttribute.call_count, 7) - self.assertEqual(ixnet.commit.call_count, 2) - - def test_ix_update_udp(self): - ixnet = mock.MagicMock() - - ixnet_gen = IxNextgen(ixnet) - - result = ixnet_gen.ix_update_udp({}) - self.assertIsNone(result) - - def test_ix_update_tcp(self): - ixnet = mock.MagicMock() - ixnet_gen = IxNextgen(ixnet) - - result = ixnet_gen.ix_update_tcp({}) - self.assertIsNone(result) - - def test_ix_start_traffic(self): - ixnet = mock.MagicMock() - ixnet.getList.return_value = [0] - ixnet.getAttribute.return_value = 'down' - - ixnet_gen = IxNextgen(ixnet) - - result = ixnet_gen.ix_start_traffic() - self.assertIsNone(result) - self.assertEqual(ixnet.getList.call_count, 1) - self.assertEqual(ixnet.execute.call_count, 3) - - def test_ix_stop_traffic(self): - ixnet = mock.MagicMock() - ixnet.getList.return_value = [0] - - ixnet_gen = IxNextgen(ixnet) - - result = ixnet_gen.ix_stop_traffic() - self.assertIsNone(result) - self.assertEqual(ixnet.getList.call_count, 1) - self.assertEqual(ixnet.execute.call_count, 1) - - def test_ix_get_statistics(self): - ixnet = mock.MagicMock() - ixnet.execute.return_value = "" - ixnet.getList.side_effect = [ - [ - '::ixNet::OBJ-/statistics/view:"Traffic Item Statistics"', - '::ixNet::OBJ-/statistics/view:"Port Statistics"', - ], - [ - '::ixNet::OBJ-/statistics/view:"Flow Statistics"', - ], - ] - - ixnet_gen = IxNextgen(ixnet) - - result = ixnet_gen.ix_get_statistics() - self.assertIsNotNone(result) - self.assertEqual(ixnet.getList.call_count, 1) - self.assertEqual(ixnet.execute.call_count, 20) - - def test_find_view_obj_no_where(self): - views = ['here', 'there', 'everywhere'] - result = IxNextgen.find_view_obj('no_where', views) - self.assertEqual(result, '') - - def test_add_ip_header_v4(self): - static_traffic_params = { - "uplink_0": { - "id": 1, - "bidir": "False", - "duration": 60, - "iload": "100", - "outer_l2": { - "dstmac": "00:00:00:00:00:03", - "framesPerSecond": True, - "framesize": {"64B": "100"}, - "srcmac": "00:00:00:00:00:01" - }, - "outer_l3": { - "dscp": 0, - "dstip4": "152.16.40.20", - "proto": "udp", - "srcip4": "152.16.100.20", - "count": 1024, - "ttl": 32 - }, - "outer_l3v4": { - "dscp": 0, - "dstip4": "152.16.40.20", - "proto": "udp", - "srcip4": "152.16.100.20", - "ttl": 32 - }, - "outer_l3v6": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l4": { - "dstport": "2001", - "srcport": "1234" - }, - "traffic_type": "continuous" - }, - "downlink_0": { - "id": 2, - "bidir": "False", - "duration": 60, - "iload": "100", - "outer_l2": { - "dstmac": "00:00:00:00:00:04", - "framesPerSecond": True, - "framesize": {"64B": "100"}, - "srcmac": "00:00:00:00:00:01" - }, - "outer_l3": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l3v4": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l3v6": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l4": { - "dstport": "1234", - "srcport": "2001" - }, - "traffic_type": "continuous" - } - } - - ixnet = mock.MagicMock() - ixnet.remapIds.return_value = ["0"] - ixnet.setMultiAttribute.return_value = [1] - ixnet.commit.return_value = [1] - ixnet.getList.side_effect = [[1], [0], [0], ["srcIp", "dstIp"]] - - ixnet_gen = IxNextgen(ixnet) - - result = ixnet_gen.add_ip_header(static_traffic_params, IP_VERSION_4) - self.assertIsNone(result) - self.assertGreater(ixnet.setMultiAttribute.call_count, 0) - self.assertEqual(ixnet.commit.call_count, 1) - - def test_add_ip_header_v4_nothing_to_do(self): - static_traffic_params = { - "uplink_0": { - "id": 1, - "bidir": "False", - "duration": 60, - "iload": "100", - "outer_l2": { - "dstmac": "00:00:00:00:00:03", - "framesPerSecond": True, - "framesize": {"64B": "100"}, - "srcmac": "00:00:00:00:00:01" - }, - "outer_l3": { - "dscp": 0, - "dstip4": "152.16.40.20", - "proto": "udp", - "srcip4": "152.16.100.20", - "count": 1024, - "ttl": 32 - }, - "outer_l3v4": { - "dscp": 0, - "dstip4": "152.16.40.20", - "proto": "udp", - "srcip4": "152.16.100.20", - "ttl": 32 - }, - "outer_l3v6": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l4": { - "dstport": "2001", - "srcport": "1234" - }, - "traffic_type": "continuous" - }, - "downlink_0": { - "id": 2, - "bidir": "False", - "duration": 60, - "iload": "100", - "outer_l2": { - "dstmac": "00:00:00:00:00:04", - "framesPerSecond": True, - "framesize": {"64B": "100"}, - "srcmac": "00:00:00:00:00:01" - }, - "outer_l3": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l3v4": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l3v6": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l4": { - "dstport": "1234", - "srcport": "2001" - }, - "traffic_type": "continuous" - } - } - - ixnet = mock.MagicMock() - ixnet.remapIds.return_value = ["0"] - ixnet.setMultiAttribute.return_value = [1] - ixnet.commit.return_value = [1] - ixnet.getList.side_effect = [[1], [0, 1], [0], ["srcIp", "dstIp"]] - - ixnet_gen = IxNextgen(ixnet) - - result = ixnet_gen.add_ip_header(static_traffic_params, IP_VERSION_4) - self.assertIsNone(result) - self.assertGreater(ixnet.setMultiAttribute.call_count, 0) - self.assertEqual(ixnet.commit.call_count, 1) - - def test_add_ip_header_v6(self): - static_traffic_profile = { - "uplink_0": { - "id": 1, - "bidir": "False", - "duration": 60, - "iload": "100", - "outer_l2": { - "dstmac": "00:00:00:00:00:03", - "framesPerSecond": True, - "framesize": {"64B": "100"}, - "srcmac": "00:00:00:00:00:01" - }, - "outer_l3": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.40.20", - "proto": "udp", - "srcip4": "152.16.100.20", - "ttl": 32 - }, - "outer_l3v6": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l4": { - "dstport": "2001", - "srcport": "1234" - }, - "traffic_type": "continuous" - }, - "downlink_0": { - "id": 2, - "bidir": "False", - "duration": 60, - "iload": "100", - "outer_l2": { - "dstmac": "00:00:00:00:00:04", - "framesPerSecond": True, - "framesize": {"64B": "100"}, - "srcmac": "00:00:00:00:00:01" - }, - "outer_l3": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l3v6": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l4": { - "dstport": "1234", - "srcport": "2001" - }, - "traffic_type": "continuous" - } - } - - ixnet = mock.MagicMock() - ixnet.getList.side_effect = [[1], [1], [1], ["srcIp", "dstIp"]] - ixnet.remapIds.return_value = ["0"] - ixnet.setMultiAttribute.return_value = [1] - ixnet.commit.return_value = [1] - - ixnet_gen = IxNextgen(ixnet) - - result = ixnet_gen.add_ip_header(static_traffic_profile, IP_VERSION_6) - self.assertIsNone(result) - self.assertGreater(ixnet.setMultiAttribute.call_count, 0) - self.assertEqual(ixnet.commit.call_count, 1) - - def test_add_ip_header_v6_nothing_to_do(self): - static_traffic_params = { - "uplink_0": { - "id": 1, - "bidir": "False", - "duration": 60, - "iload": "100", - "outer_l2": { - "dstmac": "00:00:00:00:00:03", - "framesPerSecond": True, - "framesize": {"64B": "100"}, - "srcmac": "00:00:00:00:00:01" - }, - "outer_l3": { - "dscp": 0, - "dstip4": "152.16.40.20", - "proto": "udp", - "srcip4": "152.16.100.20", - "count": 1024, - "ttl": 32 - }, - "outer_l3v6": { - "dscp": 0, - "dstip4": "152.16.40.20", - "proto": "udp", - "srcip4": "152.16.100.20", - "ttl": 32 - }, - "outer_l4": { - "dstport": "2001", - "srcport": "1234" - }, - "traffic_type": "continuous" - }, - "downlink_0": { - "id": 2, - "bidir": "False", - "duration": 60, - "iload": "100", - "outer_l2": { - "dstmac": "00:00:00:00:00:04", - "framesPerSecond": True, - "framesize": {"64B": "100"}, - "srcmac": "00:00:00:00:00:01" - }, - "outer_l3": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l3v6": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l4": { - "dstport": "1234", - "srcport": "2001" - }, - "traffic_type": "continuous" - } - } - - ixnet = mock.MagicMock() - ixnet.getList.side_effect = [[1], [0, 1], [1], ["srcIP", "dstIP"]] - ixnet.remapIds.return_value = ["0"] - ixnet.setMultiAttribute.return_value = [1] - ixnet.commit.return_value = [1] - - ixnet_gen = IxNextgen(ixnet) - - result = ixnet_gen.add_ip_header(static_traffic_params, IP_VERSION_6) - self.assertIsNone(result) - self.assertEqual(ixnet.setMultiAttribute.call_count, 0) - - def test_set_random_ip_multi_attributes_bad_ip_version(self): - bad_ip_version = object() - ixnet_gen = IxNextgen(mock.Mock()) - with self.assertRaises(ValueError): - ixnet_gen.set_random_ip_multi_attributes( - mock.Mock(), bad_ip_version, mock.Mock(), mock.Mock()) - - def test_get_config(self): - tg_cfg = { - "vdu": [ - { - "external-interface": [ - { - "virtual-interface": { - "vpci": "0000:07:00.1", - }, - }, - { - "virtual-interface": { - "vpci": "0001:08:01.2", - }, - }, - ], - }, - ], - "mgmt-interface": { - "ip": "test1", - "tg-config": { - "dut_result_dir": "test2", - "version": "test3", - "ixchassis": "test4", - "tcl_port": "test5", - "py_lib_path": "test6", - }, - } - } - - expected = { - 'py_lib_path': 'test6', - 'machine': 'test1', - 'port': 'test5', - 'chassis': 'test4', - 'cards': ['0000', '0001'], - 'ports': ['07', '08'], - 'output_dir': 'test2', - 'version': 'test3', - 'bidir': True, - } - - result = IxNextgen.get_config(tg_cfg) - self.assertDictEqual(result, expected) - - def test_ix_update_ether(self): - static_traffic_params = { - "uplink_0": { - "id": 1, - "bidir": "False", - "duration": 60, - "iload": "100", - "outer_l2": { - "dstmac": "00:00:00:00:00:03", - "framesPerSecond": True, - "framesize": 64, - "srcmac": "00:00:00:00:00:01" - }, - "outer_l3": { - "dscp": 0, - "dstip4": "152.16.40.20", - "proto": "udp", - "srcip4": "152.16.100.20", - "ttl": 32 - }, - "outer_l3v4": { - "dscp": 0, - "dstip4": "152.16.40.20", - "proto": "udp", - "srcip4": "152.16.100.20", - "ttl": 32 - }, - "outer_l3v6": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l4": { - "dstport": "2001", - "srcport": "1234" - }, - "traffic_type": "continuous" - }, - "downlink_0": { - "id": 2, - "bidir": "False", - "duration": 60, - "iload": "100", - "outer_l2": { - "dstmac": "00:00:00:00:00:04", - "framesPerSecond": True, - "framesize": 64, - "srcmac": "00:00:00:00:00:01" - }, - "outer_l3": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l3v4": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l3v6": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l4": { - "dstport": "1234", - "srcport": "2001" - }, - "traffic_type": "continuous" - } - } - - ixnet = mock.MagicMock() - ixnet.setMultiAttribute.return_value = [1] - ixnet.commit.return_value = [1] - ixnet.getList.side_effect = [ - [1], - [1], - [1], - [ - "ethernet.header.destinationAddress", - "ethernet.header.sourceAddress", - ], - ] - - ixnet_gen = IxNextgen(ixnet) - - result = ixnet_gen.ix_update_ether(static_traffic_params) - self.assertIsNone(result) - self.assertGreater(ixnet.setMultiAttribute.call_count, 0) - - def test_ix_update_ether_nothing_to_do(self): - static_traffic_params = { - "uplink_0": { - "id": 1, - "bidir": "False", - "duration": 60, - "iload": "100", - "outer_l3": { - "dscp": 0, - "dstip4": "152.16.40.20", - "proto": "udp", - "srcip4": "152.16.100.20", - "ttl": 32 - }, - "outer_l3v4": { - "dscp": 0, - "dstip4": "152.16.40.20", - "proto": "udp", - "srcip4": "152.16.100.20", - "ttl": 32 - }, - "outer_l3v6": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l4": { - "dstport": "2001", - "srcport": "1234" - }, - "traffic_type": "continuous" - }, - "downlink_0": { - "id": 2, - "bidir": "False", - "duration": 60, - "iload": "100", - "outer_l3": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l3v4": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l3v6": { - "count": 1024, - "dscp": 0, - "dstip4": "152.16.100.20", - "proto": "udp", - "srcip4": "152.16.40.20", - "ttl": 32 - }, - "outer_l4": { - "dstport": "1234", - "srcport": "2001" - }, - "traffic_type": "continuous" - } - } - - ixnet = mock.MagicMock() - ixnet.setMultiAttribute.return_value = [1] - ixnet.commit.return_value = [1] - ixnet.getList.side_effect = [ - [1], - [1], - [1], - [ - "ethernet.header.destinationAddress", - "ethernet.header.sourceAddress", - ], - ] - - ixnet_gen = IxNextgen(ixnet) - - result = ixnet_gen.ix_update_ether(static_traffic_params) - self.assertIsNone(result) - self.assertEqual(ixnet.setMultiAttribute.call_count, 0) diff --git a/yardstick/tests/unit/network_services/libs/ixia_libs/test_ixnet_api.py b/yardstick/tests/unit/network_services/libs/ixia_libs/test_ixnet_api.py new file mode 100644 index 000000000..34afa3d5b --- /dev/null +++ b/yardstick/tests/unit/network_services/libs/ixia_libs/test_ixnet_api.py @@ -0,0 +1,512 @@ +# Copyright (c) 2018 Intel 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 mock +import IxNetwork +import unittest + +from yardstick.common import exceptions +from yardstick.network_services.libs.ixia_libs.ixnet import ixnet_api + + +UPLINK = 'uplink' +DOWNLINK = 'downlink' + +TRAFFIC_PARAMETERS = { + UPLINK: { + 'id': 1, + 'bidir': 'False', + 'duration': 60, + 'iload': '100', + 'outer_l2': { + 'framesize': {'64B': '25', '256B': '75'} + }, + 'outer_l3': { + 'count': 512, + 'dscp': 0, + 'dstip4': '152.16.40.20', + 'proto': 'udp', + 'srcip4': '152.16.100.20', + 'ttl': 32 + }, + 'outer_l3v4': { + 'dscp': 0, + 'dstip4': '152.16.40.20', + 'proto': 'udp', + 'srcip4': '152.16.100.20', + 'ttl': 32 + }, + 'outer_l3v6': { + 'count': 1024, + 'dscp': 0, + 'dstip4': '152.16.100.20', + 'proto': 'udp', + 'srcip4': '152.16.40.20', + 'ttl': 32 + }, + 'outer_l4': { + 'dstport': '2001', + 'srcport': '1234' + }, + 'traffic_type': 'continuous' + }, + DOWNLINK: { + 'id': 2, + 'bidir': 'False', + 'duration': 60, + 'iload': '100', + 'outer_l2': { + 'framesize': {'128B': '35', '1024B': '65'} + }, + 'outer_l3': { + 'count': 1024, + 'dscp': 0, + 'dstip4': '152.16.100.20', + 'proto': 'udp', + 'srcip4': '152.16.40.20', + 'ttl': 32 + }, + 'outer_l3v4': { + 'count': 1024, + 'dscp': 0, + 'dstip4': '152.16.100.20', + 'proto': 'udp', + 'srcip4': '152.16.40.20', + 'ttl': 32 + }, + 'outer_l3v6': { + 'count': 1024, + 'dscp': 0, + 'dstip4': '152.16.100.20', + 'proto': 'udp', + 'srcip4': '152.16.40.20', + 'ttl': 32 + }, + 'outer_l4': { + 'dstport': '1234', + 'srcport': '2001' + }, + 'traffic_type': 'continuous' + } +} + + +class TestIxNextgen(unittest.TestCase): + + def setUp(self): + self.ixnet = mock.Mock() + self.ixnet.execute = mock.Mock() + self.ixnet.getRoot.return_value = 'my_root' + + def test_get_config(self): + tg_cfg = { + 'vdu': [ + { + 'external-interface': [ + {'virtual-interface': {'vpci': '0000:07:00.1'}}, + {'virtual-interface': {'vpci': '0001:08:01.2'}} + ] + }, + ], + 'mgmt-interface': { + 'ip': 'test1', + 'tg-config': { + 'dut_result_dir': 'test2', + 'version': 'test3', + 'ixchassis': 'test4', + 'tcl_port': 'test5', + }, + } + } + + expected = { + 'machine': 'test1', + 'port': 'test5', + 'chassis': 'test4', + 'cards': ['0000', '0001'], + 'ports': ['07', '08'], + 'output_dir': 'test2', + 'version': 'test3', + 'bidir': True, + } + + result = ixnet_api.IxNextgen.get_config(tg_cfg) + self.assertEqual(result, expected) + + def test__get_config_element_by_flow_group_name(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + ixnet_gen._ixnet.getList.side_effect = [['traffic_item'], + ['fg_01']] + ixnet_gen._ixnet.getAttribute.return_value = 'flow_group_01' + output = ixnet_gen._get_config_element_by_flow_group_name( + 'flow_group_01') + self.assertEqual('traffic_item/configElement:flow_group_01', output) + + def test__get_config_element_by_flow_group_name_no_match(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + ixnet_gen._ixnet.getList.side_effect = [['traffic_item'], + ['fg_01']] + ixnet_gen._ixnet.getAttribute.return_value = 'flow_group_02' + output = ixnet_gen._get_config_element_by_flow_group_name( + 'flow_group_01') + self.assertIsNone(output) + + def test__get_stack_item(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + ixnet_gen._ixnet.getList.return_value = ['tcp1', 'tcp2', 'udp'] + with mock.patch.object( + ixnet_gen, '_get_config_element_by_flow_group_name') as \ + mock_get_cfg_element: + mock_get_cfg_element.return_value = 'cfg_element' + output = ixnet_gen._get_stack_item(mock.ANY, ixnet_api.PROTO_TCP) + self.assertEqual(['tcp1', 'tcp2'], output) + + def test__get_stack_item_no_config_element(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + with mock.patch.object( + ixnet_gen, '_get_config_element_by_flow_group_name', + return_value=None): + with self.assertRaises(exceptions.IxNetworkFlowNotPresent): + ixnet_gen._get_stack_item(mock.ANY, mock.ANY) + + def test__get_field_in_stack_item(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + ixnet_gen._ixnet.getList.return_value = ['field1', 'field2'] + output = ixnet_gen._get_field_in_stack_item(mock.ANY, 'field2') + self.assertEqual('field2', output) + + def test__get_field_in_stack_item_no_field_present(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + ixnet_gen._ixnet.getList.return_value = ['field1', 'field2'] + with self.assertRaises(exceptions.IxNetworkFieldNotPresentInStackItem): + ixnet_gen._get_field_in_stack_item(mock.ANY, 'field3') + + def test__parse_framesize(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + framesize = {'64B': '75', '512b': '25'} + output = ixnet_gen._parse_framesize(framesize) + for idx in range(len(framesize)): + if output[idx * 2] == 64: + self.assertEqual(75, output[idx * 2 + 1]) + elif output[idx * 2] == 512: + self.assertEqual(25, output[idx * 2 + 1]) + else: + raise self.failureException('Framesize (64, 512) not present') + + @mock.patch.object(IxNetwork, 'IxNet') + def test_connect(self, mock_ixnet): + mock_ixnet.return_value = self.ixnet + ixnet_gen = ixnet_api.IxNextgen() + with mock.patch.object(ixnet_gen, 'get_config') as mock_config: + mock_config.return_value = {'machine': 'machine_fake', + 'port': 'port_fake', + 'version': 12345} + ixnet_gen.connect(mock.ANY) + + self.ixnet.connect.assert_called_once_with( + 'machine_fake', '-port', 'port_fake', '-version', '12345') + mock_config.assert_called_once() + + def test_connect_invalid_config_no_machine(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + ixnet_gen.get_config = mock.Mock(return_value={ + 'port': 'port_fake', + 'version': '12345'}) + self.assertRaises(KeyError, ixnet_gen.connect, mock.ANY) + self.ixnet.connect.assert_not_called() + + def test_connect_invalid_config_no_port(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + ixnet_gen.get_config = mock.Mock(return_value={ + 'machine': 'machine_fake', + 'version': '12345'}) + self.assertRaises(KeyError, ixnet_gen.connect, mock.ANY) + self.ixnet.connect.assert_not_called() + + def test_connect_invalid_config_no_version(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + ixnet_gen.get_config = mock.Mock(return_value={ + 'machine': 'machine_fake', + 'port': 'port_fake'}) + self.assertRaises(KeyError, ixnet_gen.connect, mock.ANY) + self.ixnet.connect.assert_not_called() + + def test_connect_no_config(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + ixnet_gen.get_config = mock.Mock(return_value={}) + self.assertRaises(KeyError, ixnet_gen.connect, mock.ANY) + self.ixnet.connect.assert_not_called() + + def test_clear_config(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + ixnet_gen.clear_config() + self.ixnet.execute.assert_called_once_with('newConfig') + + @mock.patch.object(ixnet_api, 'log') + def test_assign_ports_2_ports(self, *args): + self.ixnet.getAttribute.side_effect = ['up', 'down'] + config = { + 'chassis': '1.1.1.1', + 'cards': ['1', '2'], + 'ports': ['2', '2']} + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + ixnet_gen._cfg = config + + self.assertIsNone(ixnet_gen.assign_ports()) + self.assertEqual(self.ixnet.execute.call_count, 2) + self.assertEqual(self.ixnet.commit.call_count, 4) + self.assertEqual(self.ixnet.getAttribute.call_count, 2) + + @mock.patch.object(ixnet_api, 'log') + def test_assign_ports_port_down(self, mock_log): + self.ixnet.getAttribute.return_value = 'down' + config = { + 'chassis': '1.1.1.1', + 'cards': ['1', '2'], + 'ports': ['3', '4']} + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + ixnet_gen._cfg = config + ixnet_gen.assign_ports() + mock_log.warning.assert_called() + + def test_assign_ports_no_config(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + ixnet_gen._cfg = {} + self.assertRaises(KeyError, ixnet_gen.assign_ports) + + def test__create_traffic_item(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + self.ixnet.add.return_value = 'my_new_traffic_item' + self.ixnet.remapIds.return_value = ['my_traffic_item_id'] + + ixnet_gen._create_traffic_item() + self.ixnet.add.assert_called_once_with( + 'my_root/traffic', 'trafficItem') + self.ixnet.setMultiAttribute.assert_called_once_with( + 'my_new_traffic_item', '-name', 'RFC2544', '-trafficType', 'raw') + self.assertEqual(2, self.ixnet.commit.call_count) + self.ixnet.remapIds.assert_called_once_with('my_new_traffic_item') + self.ixnet.setAttribute('my_traffic_item_id/tracking', + '-trackBy', 'trafficGroupId0') + + def test__create_flow_groups(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + ixnet_gen.ixnet.getList.side_effect = [['traffic_item'], ['1', '2']] + ixnet_gen.ixnet.add.side_effect = ['endp1', 'endp2'] + ixnet_gen._create_flow_groups() + ixnet_gen.ixnet.add.assert_has_calls([ + mock.call('traffic_item', 'endpointSet'), + mock.call('traffic_item', 'endpointSet')]) + ixnet_gen.ixnet.setMultiAttribute.assert_has_calls([ + mock.call('endp1', '-name', '1', '-sources', ['1/protocols'], + '-destinations', ['2/protocols']), + mock.call('endp2', '-name', '2', '-sources', ['2/protocols'], + '-destinations', ['1/protocols'])]) + + def test__append_protocol_to_stack(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + + ixnet_gen._append_procotol_to_stack('my_protocol', 'prev_element') + self.ixnet.execute.assert_called_with( + 'append', 'prev_element', + 'my_root/traffic/protocolTemplate:"my_protocol"') + + def test__setup_config_elements(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + ixnet_gen.ixnet.getList.side_effect = [['traffic_item'], + ['cfg_element']] + with mock.patch.object(ixnet_gen, '_append_procotol_to_stack') as \ + mock_append_proto: + ixnet_gen._setup_config_elements() + mock_append_proto.assert_has_calls([ + mock.call(ixnet_api.PROTO_UDP, 'cfg_element/stack:"ethernet-1"'), + mock.call(ixnet_api.PROTO_IPV4, 'cfg_element/stack:"ethernet-1"')]) + ixnet_gen.ixnet.setAttribute.assert_has_calls([ + mock.call('cfg_element/frameRateDistribution', '-portDistribution', + 'splitRateEvenly'), + mock.call('cfg_element/frameRateDistribution', + '-streamDistribution', 'splitRateEvenly')]) + + @mock.patch.object(ixnet_api.IxNextgen, '_create_traffic_item') + @mock.patch.object(ixnet_api.IxNextgen, '_create_flow_groups') + @mock.patch.object(ixnet_api.IxNextgen, '_setup_config_elements') + def test_create_traffic_model(self, mock__setup_config_elements, + mock__create_flow_groups, + mock__create_traffic_item): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + + ixnet_gen.create_traffic_model() + mock__create_traffic_item.assert_called_once() + mock__create_flow_groups.assert_called_once() + mock__setup_config_elements.assert_called_once() + + def test__update_frame_mac(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + with mock.patch.object(ixnet_gen, '_get_field_in_stack_item') as \ + mock_get_field: + mock_get_field.return_value = 'field_descriptor' + ixnet_gen._update_frame_mac('ethernet_descriptor', 'field', 'mac') + mock_get_field.assert_called_once_with('ethernet_descriptor', 'field') + ixnet_gen.ixnet.setMultiAttribute( + 'field_descriptor', '-singleValue', 'mac', '-fieldValue', 'mac', + '-valueType', 'singleValue') + ixnet_gen.ixnet.commit.assert_called_once() + + def test_update_frame(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + with mock.patch.object( + ixnet_gen, '_get_config_element_by_flow_group_name', + return_value='cfg_element'), \ + mock.patch.object(ixnet_gen, '_update_frame_mac') as \ + mock_update_frame, \ + mock.patch.object(ixnet_gen, '_get_stack_item') as \ + mock_get_stack_item: + mock_get_stack_item.side_effect = [['item1'], ['item2'], + ['item3'], ['item4']] + ixnet_gen.update_frame(TRAFFIC_PARAMETERS) + + self.assertEqual(6, len(ixnet_gen.ixnet.setMultiAttribute.mock_calls)) + self.assertEqual(4, len(mock_update_frame.mock_calls)) + + def test_update_frame_flow_not_present(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + with mock.patch.object( + ixnet_gen, '_get_config_element_by_flow_group_name', + return_value=None): + with self.assertRaises(exceptions.IxNetworkFlowNotPresent): + ixnet_gen.update_frame(TRAFFIC_PARAMETERS) + + def test_get_statistics(self): + ixnet_gen = ixnet_api.IxNextgen() + port_statistics = '::ixNet::OBJ-/statistics/view:"Port Statistics"' + flow_statistics = '::ixNet::OBJ-/statistics/view:"Flow Statistics"' + with mock.patch.object(ixnet_gen, '_build_stats_map') as \ + mock_build_stats: + ixnet_gen.get_statistics() + + mock_build_stats.assert_has_calls([ + mock.call(port_statistics, ixnet_gen.PORT_STATS_NAME_MAP), + mock.call(flow_statistics, ixnet_gen.LATENCY_NAME_MAP)]) + + def test__update_ipv4_address(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + with mock.patch.object(ixnet_gen, '_get_field_in_stack_item', + return_value='field_desc'): + ixnet_gen._update_ipv4_address(mock.ANY, mock.ANY, '192.168.1.1', + 100, '255.255.255.0', 25) + ixnet_gen.ixnet.setMultiAttribute.assert_called_once_with( + 'field_desc', '-seed', 100, '-fixedBits', '192.168.1.1', + '-randomMask', '255.255.255.0', '-valueType', 'random', + '-countValue', 25) + + def test_update_ip_packet(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + with mock.patch.object(ixnet_gen, '_update_ipv4_address') as \ + mock_update_add, \ + mock.patch.object(ixnet_gen, '_get_stack_item'), \ + mock.patch.object(ixnet_gen, + '_get_config_element_by_flow_group_name', return_value='celm'): + ixnet_gen.update_ip_packet(TRAFFIC_PARAMETERS) + + self.assertEqual(4, len(mock_update_add.mock_calls)) + + def test_update_ip_packet_exception_no_config_element(self): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + with mock.patch.object(ixnet_gen, + '_get_config_element_by_flow_group_name', + return_value=None): + with self.assertRaises(exceptions.IxNetworkFlowNotPresent): + ixnet_gen.update_ip_packet(TRAFFIC_PARAMETERS) + + @mock.patch.object(ixnet_api.IxNextgen, '_get_traffic_state') + def test_start_traffic(self, mock_ixnextgen_get_traffic_state): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + ixnet_gen._ixnet.getList.return_value = [0] + + mock_ixnextgen_get_traffic_state.side_effect = [ + 'stopped', 'started', 'started', 'started'] + + result = ixnet_gen.start_traffic() + self.assertIsNone(result) + self.ixnet.getList.assert_called_once() + self.assertEqual(3, ixnet_gen._ixnet.execute.call_count) + + @mock.patch.object(ixnet_api.IxNextgen, '_get_traffic_state') + def test_start_traffic_traffic_running( + self, mock_ixnextgen_get_traffic_state): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + ixnet_gen._ixnet.getList.return_value = [0] + mock_ixnextgen_get_traffic_state.side_effect = [ + 'started', 'stopped', 'started'] + + result = ixnet_gen.start_traffic() + self.assertIsNone(result) + self.ixnet.getList.assert_called_once() + self.assertEqual(4, ixnet_gen._ixnet.execute.call_count) + + @mock.patch.object(ixnet_api.IxNextgen, '_get_traffic_state') + def test_start_traffic_wait_for_traffic_to_stop( + self, mock_ixnextgen_get_traffic_state): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + ixnet_gen._ixnet.getList.return_value = [0] + mock_ixnextgen_get_traffic_state.side_effect = [ + 'started', 'started', 'started', 'stopped', 'started'] + + result = ixnet_gen.start_traffic() + self.assertIsNone(result) + self.ixnet.getList.assert_called_once() + self.assertEqual(4, ixnet_gen._ixnet.execute.call_count) + + @mock.patch.object(ixnet_api.IxNextgen, '_get_traffic_state') + def test_start_traffic_wait_for_traffic_start( + self, mock_ixnextgen_get_traffic_state): + ixnet_gen = ixnet_api.IxNextgen() + ixnet_gen._ixnet = self.ixnet + ixnet_gen._ixnet.getList.return_value = [0] + mock_ixnextgen_get_traffic_state.side_effect = [ + 'stopped', 'stopped', 'stopped', 'started'] + + result = ixnet_gen.start_traffic() + self.assertIsNone(result) + self.ixnet.getList.assert_called_once() + self.assertEqual(3, ixnet_gen._ixnet.execute.call_count) diff --git a/yardstick/tests/unit/network_services/test_yang_model.py b/yardstick/tests/unit/network_services/test_yang_model.py deleted file mode 100644 index a7eb36b8a..000000000 --- a/yardstick/tests/unit/network_services/test_yang_model.py +++ /dev/null @@ -1,129 +0,0 @@ -# Copyright (c) 2016-2017 Intel 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 mock -import unittest - -from yardstick.network_services.yang_model import YangModel - - -class YangModelTestCase(unittest.TestCase): - """Test all Yang Model methods.""" - - ENTRIES = { - 'access-list1': { - 'acl': { - 'access-list-entries': [{ - 'ace': { - 'ace-oper-data': { - 'match-counter': 0}, - 'actions': 'drop,count', - 'matches': { - 'destination-ipv4-network': - '152.16.40.20/24', - 'destination-port-range': { - 'lower-port': 0, - 'upper-port': 65535}, - 'source-ipv4-network': '0.0.0.0/0', - 'source-port-range': { - 'lower-port': 0, - 'upper-port': 65535}}, - 'rule-name': 'rule1588'}}, - { - 'ace': { - 'ace-oper-data': { - 'match-counter': 0}, - 'actions': 'drop,count', - 'matches': { - 'destination-ipv4-network': - '0.0.0.0/0', - 'destination-port-range': { - 'lower-port': 0, - 'upper-port': 65535}, - 'source-ipv4-network': - '152.16.100.20/24', - 'source-port-range': { - 'lower-port': 0, - 'upper-port': 65535}}, - 'rule-name': 'rule1589'}}], - 'acl-name': 'sample-ipv4-acl', - 'acl-type': 'ipv4-acl'} - } - } - - def test__init__(self): - cfg = "yang.yaml" - y = YangModel(cfg) - self.assertEqual(y.config_file, cfg) - - def test_config_file_setter(self): - cfg = "yang.yaml" - y = YangModel(cfg) - self.assertEqual(y.config_file, cfg) - cfg2 = "yang2.yaml" - y.config_file = cfg2 - self.assertEqual(y.config_file, cfg2) - - def test__get_entries(self): - cfg = "yang.yaml" - y = YangModel(cfg) - y._options = self.ENTRIES - y._get_entries() - self.assertIn("p acl add", y._rules) - - def test__get_entries_no_options(self): - cfg = "yang.yaml" - y = YangModel(cfg) - y._get_entries() - self.assertEqual(y._rules, '') - - @mock.patch('yardstick.network_services.yang_model.open') - @mock.patch('yardstick.network_services.yang_model.yaml_load') - def test__read_config(self, mock_safe_load, *args): - cfg = "yang.yaml" - y = YangModel(cfg) - mock_safe_load.return_value = expected = {'key1': 'value1', 'key2': 'value2'} - y._read_config() - self.assertDictEqual(y._options, expected) - - @mock.patch('yardstick.network_services.yang_model.open') - def test__read_config_open_error(self, mock_open): - cfg = "yang.yaml" - y = YangModel(cfg) - mock_open.side_effect = IOError('my error') - - self.assertEqual(y._options, {}) - with self.assertRaises(IOError) as raised: - y._read_config() - - self.assertIn('my error', str(raised.exception)) - self.assertEqual(y._options, {}) - - def test_get_rules(self): - cfg = "yang.yaml" - y = YangModel(cfg) - y._read_config = read_mock = mock.Mock() - y._get_entries = get_mock = mock.Mock() - - y._rules = None - self.assertIsNone(y.get_rules()) - self.assertEqual(read_mock.call_count, 1) - self.assertEqual(get_mock.call_count, 1) - - # True value should prevent calling read and get - y._rules = 999 - self.assertEqual(y.get_rules(), 999) - self.assertEqual(read_mock.call_count, 1) - self.assertEqual(get_mock.call_count, 1) diff --git a/yardstick/network_services/libs/ixia_libs/IxNet/__init__.py b/yardstick/tests/unit/network_services/traffic_profile/__init__.py index e69de29bb..e69de29bb 100644 --- a/yardstick/network_services/libs/ixia_libs/IxNet/__init__.py +++ b/yardstick/tests/unit/network_services/traffic_profile/__init__.py diff --git a/tests/unit/network_services/traffic_profile/test_base.py b/yardstick/tests/unit/network_services/traffic_profile/test_base.py index 3b8804976..641064cbe 100644 --- a/tests/unit/network_services/traffic_profile/test_base.py +++ b/yardstick/tests/unit/network_services/traffic_profile/test_base.py @@ -44,7 +44,7 @@ class TestTrafficProfile(unittest.TestCase): traffic_profile = base.TrafficProfile(self.TRAFFIC_PROFILE) self.assertEqual(self.TRAFFIC_PROFILE, traffic_profile.params) - def test_execute(self): + def test_execute_traffic(self): traffic_profile = base.TrafficProfile(self.TRAFFIC_PROFILE) self.assertRaises(NotImplementedError, traffic_profile.execute_traffic, {}) diff --git a/tests/unit/network_services/traffic_profile/test_fixed.py b/yardstick/tests/unit/network_services/traffic_profile/test_fixed.py index dec94964b..39905e6b1 100644 --- a/tests/unit/network_services/traffic_profile/test_fixed.py +++ b/yardstick/tests/unit/network_services/traffic_profile/test_fixed.py @@ -13,12 +13,10 @@ # limitations under the License. # -from __future__ import absolute_import - -import unittest import mock +import unittest -from tests.unit import STL_MOCKS +from yardstick.tests import STL_MOCKS STLClient = mock.MagicMock() stl_patch = mock.patch.dict("sys.modules", STL_MOCKS) diff --git a/tests/unit/network_services/traffic_profile/test_http.py b/yardstick/tests/unit/network_services/traffic_profile/test_http.py index 5d8029ea0..0d1b916a7 100644 --- a/tests/unit/network_services/traffic_profile/test_http.py +++ b/yardstick/tests/unit/network_services/traffic_profile/test_http.py @@ -13,7 +13,6 @@ # limitations under the License. # -from __future__ import absolute_import import unittest from yardstick.network_services.traffic_profile.base import TrafficProfile @@ -29,7 +28,7 @@ class TestTrafficProfileGenericHTTP(unittest.TestCase): def test_execute(self): traffic_profile_generic_htt_p = \ - TrafficProfileGenericHTTP(TrafficProfile) + TrafficProfileGenericHTTP(TrafficProfile) traffic_generator = {} self.assertIsNone( traffic_profile_generic_htt_p.execute(traffic_generator)) diff --git a/tests/unit/network_services/traffic_profile/test_http_ixload.py b/yardstick/tests/unit/network_services/traffic_profile/test_http_ixload.py index 5110439fd..57de6602d 100644 --- a/tests/unit/network_services/traffic_profile/test_http_ixload.py +++ b/yardstick/tests/unit/network_services/traffic_profile/test_http_ixload.py @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. - -from __future__ import absolute_import import unittest import mock @@ -187,9 +185,9 @@ class TestIxLoadTrafficGen(unittest.TestCase): with self.assertRaises(Exception): ixload.load_config_file("ixload.cfg") - @mock.patch('yardstick.network_services.traffic_profile.http_ixload.IxLoad') @mock.patch('yardstick.network_services.traffic_profile.http_ixload.StatCollectorUtils') - def test_start_http_test_connect_error(self, mock_collector_type, mock_ixload_type): + @mock.patch('yardstick.network_services.traffic_profile.http_ixload.IxLoad') + def test_start_http_test_connect_error(self, mock_ixload_type, *args): ports = [1, 2, 3] test_input = { "remote_server": "REMOTE_SERVER", @@ -204,8 +202,7 @@ class TestIxLoadTrafficGen(unittest.TestCase): j = jsonutils.dump_as_bytes(test_input) - mock_ixload = mock_ixload_type() - mock_ixload.connect.side_effect = RuntimeError + mock_ixload_type.return_value.connect.side_effect = RuntimeError ixload = http_ixload.IXLOADHttpTest(j) ixload.results_on_windows = 'windows_result_dir' @@ -216,7 +213,7 @@ class TestIxLoadTrafficGen(unittest.TestCase): @mock.patch('yardstick.network_services.traffic_profile.http_ixload.IxLoad') @mock.patch('yardstick.network_services.traffic_profile.http_ixload.StatCollectorUtils') - def test_start_http_test(self, mock_collector_type, mock_ixload_type): + def test_start_http_test(self, *args): ports = [1, 2, 3] test_input = { "remote_server": "REMOTE_SERVER", @@ -240,7 +237,7 @@ class TestIxLoadTrafficGen(unittest.TestCase): @mock.patch('yardstick.network_services.traffic_profile.http_ixload.IxLoad') @mock.patch('yardstick.network_services.traffic_profile.http_ixload.StatCollectorUtils') - def test_start_http_test_reassign_error(self, mock_collector_type, mock_ixload_type): + def test_start_http_test_reassign_error(self, *args): ports = [1, 2, 3] test_input = { "remote_server": "REMOTE_SERVER", @@ -264,9 +261,9 @@ class TestIxLoadTrafficGen(unittest.TestCase): ixload.result_dir = 'my_result_dir' ixload.start_http_test() - self.assertEqual(reassign_ports.call_count, 1) + reassign_ports.assert_called_once() @mock.patch("yardstick.network_services.traffic_profile.http_ixload.IXLOADHttpTest") - def test_main(self, IXLOADHttpTest): + def test_main(self, *args): args = ["1", "2", "3"] http_ixload.main(args) diff --git a/tests/unit/network_services/traffic_profile/test_ixia_rfc2544.py b/yardstick/tests/unit/network_services/traffic_profile/test_ixia_rfc2544.py index e8910d62b..d14dbd4b5 100644 --- a/tests/unit/network_services/traffic_profile/test_ixia_rfc2544.py +++ b/yardstick/tests/unit/network_services/traffic_profile/test_ixia_rfc2544.py @@ -11,27 +11,17 @@ # 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. -# from __future__ import absolute_import from __future__ import division -import unittest -import mock from copy import deepcopy -from tests.unit import STL_MOCKS - -STLClient = mock.MagicMock() -stl_patch = mock.patch.dict("sys.modules", STL_MOCKS) -stl_patch.start() +import mock +import unittest -if stl_patch: - from yardstick.network_services.traffic_profile.trex_traffic_profile \ - import TrexProfile - from yardstick.network_services.traffic_profile.ixia_rfc2544 import \ - IXIARFC2544Profile - from yardstick.network_services.traffic_profile import ixia_rfc2544 +from yardstick.network_services.traffic_profile import ixia_rfc2544 +from yardstick.network_services.traffic_profile import trex_traffic_profile class TestIXIARFC2544Profile(unittest.TestCase): @@ -48,45 +38,66 @@ class TestIXIARFC2544Profile(unittest.TestCase): }, } - PROFILE = {'description': 'Traffic profile to run RFC2544 latency', - 'name': 'rfc2544', - 'traffic_profile': {'traffic_type': 'IXIARFC2544Profile', - 'frame_rate': 100}, - IXIARFC2544Profile.DOWNLINK: {'ipv4': - {'outer_l2': {'framesize': - {'64B': '100', '1518B': '0', - '128B': '0', '1400B': '0', - '256B': '0', '373b': '0', - '570B': '0'}}, - 'outer_l3v4': {'dstip4': '1.1.1.1-1.15.255.255', - 'proto': 'udp', 'count': '1', - 'srcip4': '90.90.1.1-90.105.255.255', - 'dscp': 0, 'ttl': 32}, - 'outer_l4': {'srcport': '2001', - 'dsrport': '1234'}}}, - IXIARFC2544Profile.UPLINK: {'ipv4': - {'outer_l2': {'framesize': - {'64B': '100', '1518B': '0', - '128B': '0', '1400B': '0', - '256B': '0', '373b': '0', - '570B': '0'}}, - 'outer_l3v4': {'dstip4': '9.9.1.1-90.105.255.255', - 'proto': 'udp', 'count': '1', - 'srcip4': '1.1.1.1-1.15.255.255', - 'dscp': 0, 'ttl': 32}, - 'outer_l4': {'dstport': '2001', - 'srcport': '1234'}}}, - 'schema': 'isb:traffic_profile:0.1'} + PROFILE = { + 'description': 'Traffic profile to run RFC2544 latency', + 'name': 'rfc2544', + 'traffic_profile': { + 'traffic_type': 'IXIARFC2544Profile', + 'frame_rate': 100}, + ixia_rfc2544.IXIARFC2544Profile.DOWNLINK: { + 'ipv4': { + 'outer_l2': { + 'framesize': { + '64B': '100', + '1518B': '0', + '128B': '0', + '1400B': '0', + '256B': '0', + '373b': '0', + '570B': '0'}}, + 'outer_l3v4': { + 'dstip4': '1.1.1.1-1.15.255.255', + 'proto': 'udp', + 'count': '1', + 'srcip4': '90.90.1.1-90.105.255.255', + 'dscp': 0, + 'ttl': 32}, + 'outer_l4': { + 'srcport': '2001', + 'dsrport': '1234'}}}, + ixia_rfc2544.IXIARFC2544Profile.UPLINK: { + 'ipv4': { + 'outer_l2': { + 'framesize': { + '64B': '100', + '1518B': '0', + '128B': '0', + '1400B': '0', + '256B': '0', + '373b': '0', + '570B': '0'}}, + 'outer_l3v4': { + 'dstip4': '9.9.1.1-90.105.255.255', + 'proto': 'udp', + 'count': '1', + 'srcip4': '1.1.1.1-1.15.255.255', + 'dscp': 0, + 'ttl': 32}, + 'outer_l4': { + 'dstport': '2001', + 'srcport': '1234'}}}, + 'schema': 'isb:traffic_profile:0.1'} def test_get_ixia_traffic_profile_error(self): - traffic_generator = mock.Mock(autospec=TrexProfile) + traffic_generator = mock.Mock( + autospec=trex_traffic_profile.TrexProfile) traffic_generator.my_ports = [0, 1] traffic_generator.uplink_ports = [-1] traffic_generator.downlink_ports = [1] traffic_generator.client = \ mock.Mock(return_value=True) STATIC_TRAFFIC = { - IXIARFC2544Profile.UPLINK: { + ixia_rfc2544.IXIARFC2544Profile.UPLINK: { "id": 1, "bidir": "False", "duration": 60, @@ -125,7 +136,7 @@ class TestIXIARFC2544Profile(unittest.TestCase): }, "traffic_type": "continuous" }, - IXIARFC2544Profile.DOWNLINK: { + ixia_rfc2544.IXIARFC2544Profile.DOWNLINK: { "id": 2, "bidir": "False", "duration": 60, @@ -169,7 +180,8 @@ class TestIXIARFC2544Profile(unittest.TestCase): } ixia_rfc2544.STATIC_TRAFFIC = STATIC_TRAFFIC - r_f_c2544_profile = IXIARFC2544Profile(self.TRAFFIC_PROFILE) + r_f_c2544_profile = ixia_rfc2544.IXIARFC2544Profile( + self.TRAFFIC_PROFILE) r_f_c2544_profile.rate = 100 mac = {"src_mac_0": "00:00:00:00:00:01", "src_mac_1": "00:00:00:00:00:02", @@ -181,14 +193,15 @@ class TestIXIARFC2544Profile(unittest.TestCase): self.assertIsNotNone(result) def test_get_ixia_traffic_profile(self): - traffic_generator = mock.Mock(autospec=TrexProfile) + traffic_generator = mock.Mock( + autospec=trex_traffic_profile.TrexProfile) traffic_generator.my_ports = [0, 1] traffic_generator.uplink_ports = [-1] traffic_generator.downlink_ports = [1] traffic_generator.client = \ mock.Mock(return_value=True) STATIC_TRAFFIC = { - IXIARFC2544Profile.UPLINK: { + ixia_rfc2544.IXIARFC2544Profile.UPLINK: { "id": 1, "bidir": "False", "duration": 60, @@ -215,7 +228,6 @@ class TestIXIARFC2544Profile(unittest.TestCase): "count": "1" }, "outer_l3v6": { - "count": 1024, "dscp": 0, "dstip4": "152.16.100.20", "proto": "udp", @@ -229,7 +241,7 @@ class TestIXIARFC2544Profile(unittest.TestCase): }, "traffic_type": "continuous" }, - IXIARFC2544Profile.DOWNLINK: { + ixia_rfc2544.IXIARFC2544Profile.DOWNLINK: { "id": 2, "bidir": "False", "duration": 60, @@ -249,7 +261,6 @@ class TestIXIARFC2544Profile(unittest.TestCase): "ttl": 32 }, "outer_l3v4": { - "count": 1024, "dscp": 0, "dstip4": "152.16.100.20", "proto": "udp", @@ -257,7 +268,6 @@ class TestIXIARFC2544Profile(unittest.TestCase): "ttl": 32, }, "outer_l3v6": { - "count": 1024, "dscp": 0, "dstip4": "152.16.100.20", "proto": "udp", @@ -274,7 +284,8 @@ class TestIXIARFC2544Profile(unittest.TestCase): } ixia_rfc2544.STATIC_TRAFFIC = STATIC_TRAFFIC - r_f_c2544_profile = IXIARFC2544Profile(self.TRAFFIC_PROFILE) + r_f_c2544_profile = ixia_rfc2544.IXIARFC2544Profile( + self.TRAFFIC_PROFILE) r_f_c2544_profile.rate = 100 mac = {"src_mac_0": "00:00:00:00:00:01", "src_mac_1": "00:00:00:00:00:02", @@ -287,14 +298,15 @@ class TestIXIARFC2544Profile(unittest.TestCase): @mock.patch("yardstick.network_services.traffic_profile.ixia_rfc2544.open") def test_get_ixia_traffic_profile_v6(self, *args): - traffic_generator = mock.Mock(autospec=TrexProfile) + traffic_generator = mock.Mock( + autospec=trex_traffic_profile.TrexProfile) traffic_generator.my_ports = [0, 1] traffic_generator.uplink_ports = [-1] traffic_generator.downlink_ports = [1] traffic_generator.client = \ mock.Mock(return_value=True) STATIC_TRAFFIC = { - IXIARFC2544Profile.UPLINK: { + ixia_rfc2544.IXIARFC2544Profile.UPLINK: { "id": 1, "bidir": "False", "duration": 60, @@ -333,7 +345,7 @@ class TestIXIARFC2544Profile(unittest.TestCase): }, "traffic_type": "continuous" }, - IXIARFC2544Profile.DOWNLINK: { + ixia_rfc2544.IXIARFC2544Profile.DOWNLINK: { "id": 2, "bidir": "False", "duration": 60, @@ -377,7 +389,8 @@ class TestIXIARFC2544Profile(unittest.TestCase): } ixia_rfc2544.STATIC_TRAFFIC = STATIC_TRAFFIC - r_f_c2544_profile = IXIARFC2544Profile(self.TRAFFIC_PROFILE) + r_f_c2544_profile = ixia_rfc2544.IXIARFC2544Profile( + self.TRAFFIC_PROFILE) r_f_c2544_profile.rate = 100 mac = {"src_mac_0": "00:00:00:00:00:01", "src_mac_1": "00:00:00:00:00:02", @@ -390,7 +403,7 @@ class TestIXIARFC2544Profile(unittest.TestCase): 'traffic_profile': {'traffic_type': 'IXIARFC2544Profile', 'frame_rate': 100}, - IXIARFC2544Profile.DOWNLINK: + ixia_rfc2544.IXIARFC2544Profile.DOWNLINK: {'ipv4': {'outer_l2': {'framesize': {'64B': '100', '1518B': '0', @@ -407,7 +420,7 @@ class TestIXIARFC2544Profile(unittest.TestCase): 'dscp': 0, 'ttl': 32}, 'outer_l4': {'srcport': '2001', 'dsrport': '1234'}}}, - IXIARFC2544Profile.UPLINK: {'ipv4': + ixia_rfc2544.IXIARFC2544Profile.UPLINK: {'ipv4': {'outer_l2': {'framesize': {'64B': '100', '1518B': '0', '128B': '0', '1400B': '0', @@ -423,7 +436,6 @@ class TestIXIARFC2544Profile(unittest.TestCase): 'proto': 'udp', 'count': '1', 'srcip6': '1.1.1.1-1.15.255.255', 'dscp': 0, 'ttl': 32}, - 'outer_l4': {'dstport': '2001', 'srcport': '1234'}}}, 'schema': 'isb:traffic_profile:0.1'} @@ -431,51 +443,75 @@ class TestIXIARFC2544Profile(unittest.TestCase): self.assertIsNotNone(result) def test__get_ixia_traffic_profile_default_args(self): - r_f_c2544_profile = IXIARFC2544Profile(self.TRAFFIC_PROFILE) + r_f_c2544_profile = ixia_rfc2544.IXIARFC2544Profile( + self.TRAFFIC_PROFILE) expected = {} result = r_f_c2544_profile._get_ixia_traffic_profile({}) self.assertDictEqual(result, expected) def test__ixia_traffic_generate(self): - traffic_generator = mock.Mock(autospec=TrexProfile) + traffic_generator = mock.Mock( + autospec=trex_traffic_profile.TrexProfile) traffic_generator.networks = { "uplink_0": ["xe0"], "downlink_0": ["xe1"], } traffic_generator.client = \ mock.Mock(return_value=True) - traffic = {IXIARFC2544Profile.DOWNLINK: {'iload': 10}, - IXIARFC2544Profile.UPLINK: {'iload': 10}} + traffic = {ixia_rfc2544.IXIARFC2544Profile.DOWNLINK: {'iload': 10}, + ixia_rfc2544.IXIARFC2544Profile.UPLINK: {'iload': 10}} ixia_obj = mock.MagicMock() - r_f_c2544_profile = IXIARFC2544Profile(self.TRAFFIC_PROFILE) + r_f_c2544_profile = ixia_rfc2544.IXIARFC2544Profile( + self.TRAFFIC_PROFILE) r_f_c2544_profile.rate = 100 result = r_f_c2544_profile._ixia_traffic_generate(traffic, ixia_obj) self.assertIsNone(result) - def test_execute(self): - traffic_generator = mock.Mock(autospec=TrexProfile) - traffic_generator.networks = { - "uplink_0": ["xe0"], - "downlink_0": ["xe1"], - } - traffic_generator.client = \ - mock.Mock(return_value=True) - r_f_c2544_profile = IXIARFC2544Profile(self.TRAFFIC_PROFILE) - r_f_c2544_profile.first_run = True - r_f_c2544_profile.params = {IXIARFC2544Profile.DOWNLINK: {'iload': 10}, - IXIARFC2544Profile.UPLINK: {'iload': 10}} + def test_execute_traffic_first_run(self): + rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) + rfc2544_profile.first_run = True + rfc2544_profile.rate = 50 + with mock.patch.object(rfc2544_profile, '_get_ixia_traffic_profile') \ + as mock_get_tp, \ + mock.patch.object(rfc2544_profile, '_ixia_traffic_generate') \ + as mock_tgenerate, \ + mock.patch.object(rfc2544_profile, 'update_traffic_profile') \ + as mock_update_tp: + mock_get_tp.return_value = 'fake_tprofile' + output = rfc2544_profile.execute_traffic(mock.ANY, + ixia_obj=mock.ANY) - r_f_c2544_profile.get_streams = mock.Mock() - r_f_c2544_profile.full_profile = {} - r_f_c2544_profile._get_ixia_traffic_profile = mock.Mock() - r_f_c2544_profile.get_multiplier = mock.Mock() - r_f_c2544_profile._ixia_traffic_generate = mock.Mock() - ixia_obj = mock.MagicMock() - self.assertIsNone(r_f_c2544_profile.execute_traffic(traffic_generator, ixia_obj)) + self.assertTrue(output) + self.assertFalse(rfc2544_profile.first_run) + self.assertEqual(50, rfc2544_profile.max_rate) + self.assertEqual(0, rfc2544_profile.min_rate) + mock_get_tp.assert_called_once() + mock_tgenerate.assert_called_once() + mock_update_tp.assert_called_once() + + def test_execute_traffic_not_first_run(self): + rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) + rfc2544_profile.first_run = False + rfc2544_profile.max_rate = 70 + rfc2544_profile.min_rate = 0 + with mock.patch.object(rfc2544_profile, '_get_ixia_traffic_profile') \ + as mock_get_tp, \ + mock.patch.object(rfc2544_profile, '_ixia_traffic_generate') \ + as mock_tgenerate: + mock_get_tp.return_value = 'fake_tprofile' + rfc2544_profile.full_profile = mock.ANY + output = rfc2544_profile.execute_traffic(mock.ANY, + ixia_obj=mock.ANY) + + self.assertFalse(output) + self.assertEqual(35.0, rfc2544_profile.rate) + mock_get_tp.assert_called_once() + mock_tgenerate.assert_called_once() def test_update_traffic_profile(self): - traffic_generator = mock.Mock(autospec=TrexProfile) + traffic_generator = mock.Mock( + autospec=trex_traffic_profile.TrexProfile) traffic_generator.networks = { "uplink_0": ["xe0"], # private, one value for intfs "downlink_0": ["xe1", "xe2"], # public, two values for intfs @@ -493,119 +529,97 @@ class TestIXIARFC2544Profile(unittest.TestCase): "downlink_0": ["xe1", "xe2"], }) - r_f_c2544_profile = IXIARFC2544Profile(traffic_profile) + r_f_c2544_profile = ixia_rfc2544.IXIARFC2544Profile(traffic_profile) r_f_c2544_profile.full_profile = {} r_f_c2544_profile.get_streams = mock.Mock() - self.assertIsNone(r_f_c2544_profile.update_traffic_profile(traffic_generator)) + self.assertIsNone( + r_f_c2544_profile.update_traffic_profile(traffic_generator)) self.assertEqual(r_f_c2544_profile.ports, ports_expected) - def test_get_drop_percentage(self): - r_f_c2544_profile = IXIARFC2544Profile(self.TRAFFIC_PROFILE) - r_f_c2544_profile.params = self.PROFILE - ixia_obj = mock.MagicMock() - r_f_c2544_profile.execute = mock.Mock() - r_f_c2544_profile._get_ixia_traffic_profile = mock.Mock() - r_f_c2544_profile._ixia_traffic_generate = mock.Mock() - r_f_c2544_profile.get_multiplier = mock.Mock() - r_f_c2544_profile.tmp_throughput = 0 - r_f_c2544_profile.tmp_drop = 0 - r_f_c2544_profile.full_profile = {} - samples = {} - for ifname in range(1): - name = "xe{}".format(ifname) - samples[name] = {"rx_throughput_fps": 20, - "tx_throughput_fps": 20, - "rx_throughput_mbps": 10, - "tx_throughput_mbps": 10, - "RxThroughput": 10, - "TxThroughput": 10, - "in_packets": 1000, - "out_packets": 1000} - tol_min = 100.0 - tolerance = 0.0 - self.assertIsNotNone( - r_f_c2544_profile.get_drop_percentage(samples, tol_min, tolerance, - ixia_obj)) + def test_get_drop_percentage_completed(self): + samples = {'iface_name_1': + {'RxThroughput': 10, 'TxThroughput': 10, + 'in_packets': 1000, 'out_packets': 1000}, + 'iface_name_2': + {'RxThroughput': 11, 'TxThroughput': 13, + 'in_packets': 1005, 'out_packets': 1007} + } + rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) + completed, samples = rfc2544_profile.get_drop_percentage(samples, 0, 1) + self.assertTrue(completed) + self.assertEqual(23.0, samples['TxThroughput']) + self.assertEqual(21.0, samples['RxThroughput']) + self.assertEqual(0.1, samples['DropPercentage']) - def test_get_drop_percentage_update(self): - r_f_c2544_profile = IXIARFC2544Profile(self.TRAFFIC_PROFILE) - r_f_c2544_profile.params = self.PROFILE - ixia_obj = mock.MagicMock() - r_f_c2544_profile.execute = mock.Mock() - r_f_c2544_profile._get_ixia_traffic_profile = mock.Mock() - r_f_c2544_profile._ixia_traffic_generate = mock.Mock() - r_f_c2544_profile.get_multiplier = mock.Mock() - r_f_c2544_profile.tmp_throughput = 0 - r_f_c2544_profile.tmp_drop = 0 - r_f_c2544_profile.full_profile = {} - samples = {} - for ifname in range(1): - name = "xe{}".format(ifname) - samples[name] = {"rx_throughput_fps": 20, - "tx_throughput_fps": 20, - "rx_throughput_mbps": 10, - "tx_throughput_mbps": 10, - "RxThroughput": 10, - "TxThroughput": 10, - "in_packets": 1000, - "out_packets": 1002} - tol_min = 0.0 - tolerance = 1.0 - self.assertIsNotNone( - r_f_c2544_profile.get_drop_percentage(samples, tol_min, tolerance, - ixia_obj)) + def test_get_drop_percentage_over_drop_percentage(self): + samples = {'iface_name_1': + {'RxThroughput': 10, 'TxThroughput': 10, + 'in_packets': 1000, 'out_packets': 1000}, + 'iface_name_2': + {'RxThroughput': 11, 'TxThroughput': 13, + 'in_packets': 1005, 'out_packets': 1007} + } + rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) + rfc2544_profile.rate = 1000 + completed, samples = rfc2544_profile.get_drop_percentage( + samples, 0, 0.05) + self.assertFalse(completed) + self.assertEqual(23.0, samples['TxThroughput']) + self.assertEqual(21.0, samples['RxThroughput']) + self.assertEqual(0.1, samples['DropPercentage']) + self.assertEqual(rfc2544_profile.rate, rfc2544_profile.max_rate) - def test_get_drop_percentage_div_zero(self): - r_f_c2544_profile = IXIARFC2544Profile(self.TRAFFIC_PROFILE) - r_f_c2544_profile.params = self.PROFILE - ixia_obj = mock.MagicMock() - r_f_c2544_profile.execute = mock.Mock() - r_f_c2544_profile._get_ixia_traffic_profile = mock.Mock() - r_f_c2544_profile._ixia_traffic_generate = mock.Mock() - r_f_c2544_profile.get_multiplier = mock.Mock() - r_f_c2544_profile.tmp_throughput = 0 - r_f_c2544_profile.tmp_drop = 0 - r_f_c2544_profile.full_profile = {} - samples = {} - for ifname in range(1): - name = "xe{}".format(ifname) - samples[name] = {"rx_throughput_fps": 20, - "tx_throughput_fps": 20, - "rx_throughput_mbps": 10, - "tx_throughput_mbps": 10, - "RxThroughput": 10, - "TxThroughput": 10, - "in_packets": 1000, - "out_packets": 0} - tol_min = 0.0 - tolerance = 0.0 - r_f_c2544_profile.tmp_throughput = 0 - self.assertIsNotNone( - r_f_c2544_profile.get_drop_percentage(samples, tol_min, tolerance, - ixia_obj)) + def test_get_drop_percentage_under_drop_percentage(self): + samples = {'iface_name_1': + {'RxThroughput': 10, 'TxThroughput': 10, + 'in_packets': 1000, 'out_packets': 1000}, + 'iface_name_2': + {'RxThroughput': 11, 'TxThroughput': 13, + 'in_packets': 1005, 'out_packets': 1007} + } + rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) + rfc2544_profile.rate = 1000 + completed, samples = rfc2544_profile.get_drop_percentage( + samples, 0.2, 1) + self.assertFalse(completed) + self.assertEqual(23.0, samples['TxThroughput']) + self.assertEqual(21.0, samples['RxThroughput']) + self.assertEqual(0.1, samples['DropPercentage']) + self.assertEqual(rfc2544_profile.rate, rfc2544_profile.min_rate) - def test_get_multiplier(self): - r_f_c2544_profile = IXIARFC2544Profile(self.TRAFFIC_PROFILE) - r_f_c2544_profile.max_rate = 100 - r_f_c2544_profile.min_rate = 100 - self.assertEqual("1.0", r_f_c2544_profile.get_multiplier()) + @mock.patch.object(ixia_rfc2544.LOG, 'info') + def test_get_drop_percentage_not_flow(self, *args): + samples = {'iface_name_1': + {'RxThroughput': 0, 'TxThroughput': 10, + 'in_packets': 1000, 'out_packets': 0}, + 'iface_name_2': + {'RxThroughput': 0, 'TxThroughput': 13, + 'in_packets': 1005, 'out_packets': 0} + } + rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) + rfc2544_profile.rate = 1000 + completed, samples = rfc2544_profile.get_drop_percentage( + samples, 0.2, 1) + self.assertFalse(completed) + self.assertEqual(23.0, samples['TxThroughput']) + self.assertEqual(0, samples['RxThroughput']) + self.assertEqual(100, samples['DropPercentage']) + self.assertEqual(rfc2544_profile.rate, rfc2544_profile.max_rate) - def test_start_ixia_latency(self): - traffic_generator = mock.Mock(autospec=TrexProfile) - traffic_generator.networks = { - "uplink_0": ["xe0"], - "downlink_0": ["xe1"], - } - traffic_generator.client = \ - mock.Mock(return_value=True) - r_f_c2544_profile = IXIARFC2544Profile(self.TRAFFIC_PROFILE) - r_f_c2544_profile.max_rate = 100 - r_f_c2544_profile.min_rate = 100 - ixia_obj = mock.MagicMock() - r_f_c2544_profile._get_ixia_traffic_profile = \ - mock.Mock(return_value={}) - r_f_c2544_profile.full_profile = {} - r_f_c2544_profile._ixia_traffic_generate = mock.Mock() - self.assertIsNone( - r_f_c2544_profile.start_ixia_latency(traffic_generator, ixia_obj)) + def test_get_drop_percentage_first_run(self): + samples = {'iface_name_1': + {'RxThroughput': 10, 'TxThroughput': 10, + 'in_packets': 1000, 'out_packets': 1000}, + 'iface_name_2': + {'RxThroughput': 11, 'TxThroughput': 13, + 'in_packets': 1005, 'out_packets': 1007} + } + rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) + completed, samples = rfc2544_profile.get_drop_percentage( + samples, 0, 1, first_run=True) + self.assertTrue(completed) + self.assertEqual(23.0, samples['TxThroughput']) + self.assertEqual(21.0, samples['RxThroughput']) + self.assertEqual(0.1, samples['DropPercentage']) + self.assertEqual(33.45, rfc2544_profile.rate) diff --git a/tests/unit/network_services/traffic_profile/test_prox_acl.py b/yardstick/tests/unit/network_services/traffic_profile/test_prox_acl.py index ef5bac0d5..48c449b20 100644 --- a/tests/unit/network_services/traffic_profile/test_prox_acl.py +++ b/yardstick/tests/unit/network_services/traffic_profile/test_prox_acl.py @@ -13,12 +13,10 @@ # limitations under the License. # -from __future__ import absolute_import - import unittest import mock -from tests.unit import STL_MOCKS +from yardstick.tests import STL_MOCKS STLClient = mock.MagicMock() stl_patch = mock.patch.dict("sys.modules", STL_MOCKS) @@ -32,7 +30,7 @@ if stl_patch: class TestProxACLProfile(unittest.TestCase): def test_run_test_with_pkt_size(self): - def target(*args, **kwargs): + def target(*args): runs.append(args[2]) if args[2] < 0 or args[2] > 100: raise RuntimeError(' '.join([str(args), str(runs)])) @@ -40,13 +38,8 @@ class TestProxACLProfile(unittest.TestCase): return fail_tuple, {} return success_tuple, {} - def get_mock_samples(*args, **kwargs): - if args[2] < 0: - raise RuntimeError(' '.join([str(args), str(runs)])) - return success_tuple - tp_config = { - 'traffic_profile': { + 'traffic_profile': { 'upper_bound': 100.0, 'lower_bound': 0.0, 'tolerated_loss': 50.0, @@ -55,8 +48,10 @@ class TestProxACLProfile(unittest.TestCase): } runs = [] - success_tuple = ProxTestDataTuple(10.0, 1, 2, 3, 4, [5.1, 5.2, 5.3], 995, 1000, 123.4) - fail_tuple = ProxTestDataTuple(10.0, 1, 2, 3, 4, [5.6, 5.7, 5.8], 850, 1000, 123.4) + success_tuple = ProxTestDataTuple( + 10.0, 1, 2, 3, 4, [5.1, 5.2, 5.3], 995, 1000, 123.4) + fail_tuple = ProxTestDataTuple( + 10.0, 1, 2, 3, 4, [5.6, 5.7, 5.8], 850, 1000, 123.4) traffic_gen = mock.MagicMock() @@ -75,4 +70,5 @@ class TestProxACLProfile(unittest.TestCase): profile.tolerated_loss = 100.0 profile._profile_helper = profile_helper - profile.run_test_with_pkt_size(traffic_gen, profile.pkt_size, profile.duration) + profile.run_test_with_pkt_size( + traffic_gen, profile.pkt_size, profile.duration) diff --git a/tests/unit/network_services/traffic_profile/test_prox_binsearch.py b/yardstick/tests/unit/network_services/traffic_profile/test_prox_binsearch.py index 1b4189b48..da550ade9 100644 --- a/tests/unit/network_services/traffic_profile/test_prox_binsearch.py +++ b/yardstick/tests/unit/network_services/traffic_profile/test_prox_binsearch.py @@ -13,12 +13,10 @@ # limitations under the License. # -from __future__ import absolute_import - import unittest import mock -from tests.unit import STL_MOCKS +from yardstick.tests import STL_MOCKS STLClient = mock.MagicMock() stl_patch = mock.patch.dict("sys.modules", STL_MOCKS) diff --git a/tests/unit/network_services/traffic_profile/test_prox_profile.py b/yardstick/tests/unit/network_services/traffic_profile/test_prox_profile.py index e5b36096f..e466305ea 100644 --- a/tests/unit/network_services/traffic_profile/test_prox_profile.py +++ b/yardstick/tests/unit/network_services/traffic_profile/test_prox_profile.py @@ -13,12 +13,10 @@ # limitations under the License. # -from __future__ import absolute_import - import unittest import mock -from tests.unit import STL_MOCKS +from yardstick.tests import STL_MOCKS STLClient = mock.MagicMock() stl_patch = mock.patch.dict("sys.modules", STL_MOCKS) diff --git a/tests/unit/network_services/traffic_profile/test_prox_ramp.py b/yardstick/tests/unit/network_services/traffic_profile/test_prox_ramp.py index 1acec2f68..7a77e3295 100644 --- a/tests/unit/network_services/traffic_profile/test_prox_ramp.py +++ b/yardstick/tests/unit/network_services/traffic_profile/test_prox_ramp.py @@ -13,12 +13,10 @@ # limitations under the License. # -from __future__ import absolute_import - import unittest import mock -from tests.unit import STL_MOCKS +from yardstick.tests import STL_MOCKS STLClient = mock.MagicMock() stl_patch = mock.patch.dict("sys.modules", STL_MOCKS) diff --git a/tests/unit/network_services/traffic_profile/test_rfc2544.py b/yardstick/tests/unit/network_services/traffic_profile/test_rfc2544.py index cb3a547ee..2684e0ba1 100644 --- a/tests/unit/network_services/traffic_profile/test_rfc2544.py +++ b/yardstick/tests/unit/network_services/traffic_profile/test_rfc2544.py @@ -16,7 +16,7 @@ import unittest import mock -from tests.unit import STL_MOCKS +from yardstick.tests import STL_MOCKS STLClient = mock.MagicMock() @@ -191,7 +191,8 @@ class TestRFC2544Profile(unittest.TestCase): mock.Mock(return_value=True) r_f_c2544_profile = RFC2544Profile(self.TRAFFIC_PROFILE) r_f_c2544_profile.params = self.PROFILE - self.assertIsNone(r_f_c2544_profile.execute_traffic(traffic_generator)) + self.assertIsNone( + r_f_c2544_profile.execute_traffic(traffic_generator)) samples = {} for ifname in range(1): name = "xe{}".format(ifname) diff --git a/tests/unit/network_services/traffic_profile/test_trex_traffic_profile.py b/yardstick/tests/unit/network_services/traffic_profile/test_trex_traffic_profile.py index d1009a5e8..5fe1b7326 100644 --- a/tests/unit/network_services/traffic_profile/test_trex_traffic_profile.py +++ b/yardstick/tests/unit/network_services/traffic_profile/test_trex_traffic_profile.py @@ -18,7 +18,7 @@ import mock import six import unittest -from tests.unit import STL_MOCKS +from yardstick.tests import STL_MOCKS from yardstick.common import exceptions as y_exc STLClient = mock.MagicMock() diff --git a/yardstick/tests/unit/service/test_environment.py b/yardstick/tests/unit/service/test_environment.py index 4af9a3958..be4882e30 100644 --- a/yardstick/tests/unit/service/test_environment.py +++ b/yardstick/tests/unit/service/test_environment.py @@ -6,16 +6,16 @@ # which accompanies this distribution, and is available at # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -import unittest import mock +from yardstick.common.exceptions import UnsupportedPodFormatError from yardstick.service.environment import Environment from yardstick.service.environment import AnsibleCommon -from yardstick.common.exceptions import UnsupportedPodFormatError +from yardstick.tests.unit import base as ut_base -class EnvironmentTestCase(unittest.TestCase): +class EnvironmentTestCase(ut_base.BaseUnitTestCase): def test_get_sut_info(self): pod_info = { @@ -31,11 +31,11 @@ class EnvironmentTestCase(unittest.TestCase): ] } - AnsibleCommon.gen_inventory_ini_dict = mock.MagicMock() - AnsibleCommon.get_sut_info = mock.MagicMock(return_value={'node1': {}}) - - env = Environment(pod=pod_info) - env.get_sut_info() + with mock.patch.object(AnsibleCommon, 'gen_inventory_ini_dict'), \ + mock.patch.object(AnsibleCommon, 'get_sut_info', + return_value={'node1': {}}): + env = Environment(pod=pod_info) + env.get_sut_info() def test_get_sut_info_pod_str(self): pod_info = 'nodes' @@ -43,7 +43,3 @@ class EnvironmentTestCase(unittest.TestCase): env = Environment(pod=pod_info) with self.assertRaises(UnsupportedPodFormatError): env.get_sut_info() - - -if __name__ == '__main__': - unittest.main() diff --git a/yardstick/tests/unit/test_ssh.py b/yardstick/tests/unit/test_ssh.py index f92290070..080d27837 100644 --- a/yardstick/tests/unit/test_ssh.py +++ b/yardstick/tests/unit/test_ssh.py @@ -13,10 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -# yardstick comment: this file is a modified copy of -# rally/tests/unit/common/test_sshutils.py - -from __future__ import absolute_import import os import socket import unittest @@ -26,8 +22,8 @@ from itertools import count import mock from oslo_utils import encodeutils +from yardstick.common import exceptions from yardstick import ssh -from yardstick.ssh import SSHError, SSHTimeout from yardstick.ssh import SSH from yardstick.ssh import AutoConnectSSH @@ -127,7 +123,7 @@ class SSHTestCase(unittest.TestCase): dss = mock_paramiko.dsskey.DSSKey rsa.from_private_key.side_effect = mock_paramiko.SSHException dss.from_private_key.side_effect = mock_paramiko.SSHException - self.assertRaises(ssh.SSHError, self.test_client._get_pkey, "key") + self.assertRaises(exceptions.SSHError, self.test_client._get_pkey, "key") @mock.patch("yardstick.ssh.six.moves.StringIO") @mock.patch("yardstick.ssh.paramiko") @@ -194,7 +190,7 @@ class SSHTestCase(unittest.TestCase): test_ssh = ssh.SSH("admin", "example.net", pkey="key") - with self.assertRaises(SSHError) as raised: + with self.assertRaises(exceptions.SSHError) as raised: test_ssh._get_client() self.assertEqual(mock_paramiko.SSHClient.call_count, 1) @@ -245,18 +241,18 @@ class SSHTestCase(unittest.TestCase): @mock.patch("yardstick.ssh.time") def test_wait_timeout(self, mock_time): mock_time.time.side_effect = [1, 50, 150] - self.test_client.execute = mock.Mock(side_effect=[ssh.SSHError, - ssh.SSHError, + self.test_client.execute = mock.Mock(side_effect=[exceptions.SSHError, + exceptions.SSHError, 0]) - self.assertRaises(ssh.SSHTimeout, self.test_client.wait) + self.assertRaises(exceptions.SSHTimeout, self.test_client.wait) self.assertEqual([mock.call("uname")] * 2, self.test_client.execute.mock_calls) @mock.patch("yardstick.ssh.time") def test_wait(self, mock_time): mock_time.time.side_effect = [1, 50, 100] - self.test_client.execute = mock.Mock(side_effect=[ssh.SSHError, - ssh.SSHError, + self.test_client.execute = mock.Mock(side_effect=[exceptions.SSHError, + exceptions.SSHError, 0]) self.test_client.wait() self.assertEqual([mock.call("uname")] * 3, @@ -333,7 +329,7 @@ class SSHRunTestCase(unittest.TestCase): def test_run_nonzero_status(self, mock_select): mock_select.select.return_value = ([], [], []) self.fake_session.recv_exit_status.return_value = 1 - self.assertRaises(ssh.SSHError, self.test_client.run, "cmd") + self.assertRaises(exceptions.SSHError, self.test_client.run, "cmd") self.assertEqual(1, self.test_client.run("cmd", raise_on_error=False)) @mock.patch("yardstick.ssh.select") @@ -401,7 +397,7 @@ class SSHRunTestCase(unittest.TestCase): def test_run_select_error(self, mock_select): self.fake_session.exit_status_ready.return_value = False mock_select.select.return_value = ([], [], [True]) - self.assertRaises(ssh.SSHError, self.test_client.run, "cmd") + self.assertRaises(exceptions.SSHError, self.test_client.run, "cmd") @mock.patch("yardstick.ssh.time") @mock.patch("yardstick.ssh.select") @@ -409,7 +405,7 @@ class SSHRunTestCase(unittest.TestCase): mock_time.time.side_effect = [1, 3700] mock_select.select.return_value = ([], [], []) self.fake_session.exit_status_ready.return_value = False - self.assertRaises(ssh.SSHTimeout, self.test_client.run, "cmd") + self.assertRaises(exceptions.SSHTimeout, self.test_client.run, "cmd") @mock.patch("yardstick.ssh.open", create=True) def test__put_file_shell(self, mock_open): @@ -529,9 +525,9 @@ class TestAutoConnectSSH(unittest.TestCase): auto_connect_ssh = AutoConnectSSH('user1', 'host1', wait=10) auto_connect_ssh._get_client = mock__get_client = mock.Mock() - mock__get_client.side_effect = SSHError + mock__get_client.side_effect = exceptions.SSHError - with self.assertRaises(SSHTimeout): + with self.assertRaises(exceptions.SSHTimeout): auto_connect_ssh._connect() self.assertEqual(mock_time.time.call_count, 12) |