aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dashboard/RFC2544_2Port.json76
-rw-r--r--dashboard/RFC2544_2Port_Multiframesize.json63
-rw-r--r--docs/testing/user/userguide/14-nsb-operation.rst34
-rw-r--r--docs/testing/user/userguide/nsb/nsb-list-of-tcs.rst1
-rw-r--r--docs/testing/user/userguide/nsb/tc_vpp_baremetal_crypto_ipsec.rst113
-rwxr-xr-xsamples/vnf_samples/nsut/cmts/k8s_vcmts_topology.yaml36
-rwxr-xr-xsamples/vnf_samples/nsut/cmts/tc_vcmts_k8s_pktgen.yaml360
-rwxr-xr-xsamples/vnf_samples/vnf_descriptors/tg_vcmts_tpl.yaml77
-rwxr-xr-xsamples/vnf_samples/vnf_descriptors/vnf_vcmts_tpl.yaml77
-rwxr-xr-xyardstick/network_services/vnf_generic/vnf/tg_vcmts_pktgen.py215
-rwxr-xr-xyardstick/network_services/vnf_generic/vnf/vcmts_vnf.py273
-rwxr-xr-xyardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_vcmts_pktgen.py652
-rwxr-xr-xyardstick/tests/unit/network_services/vnf_generic/vnf/test_vcmts_vnf.py651
13 files changed, 2585 insertions, 43 deletions
diff --git a/dashboard/RFC2544_2Port.json b/dashboard/RFC2544_2Port.json
index e6f3265f9..de9448cee 100644
--- a/dashboard/RFC2544_2Port.json
+++ b/dashboard/RFC2544_2Port.json
@@ -1,14 +1,50 @@
{
+ "__inputs": [
+ {
+ "name": "DS_YARDSTICK",
+ "label": "yardstick",
+ "description": "",
+ "type": "datasource",
+ "pluginId": "influxdb",
+ "pluginName": "InfluxDB"
+ }
+ ],
+ "__requires": [
+ {
+ "type": "grafana",
+ "id": "grafana",
+ "name": "Grafana",
+ "version": "4.4.3"
+ },
+ {
+ "type": "panel",
+ "id": "graph",
+ "name": "Graph",
+ "version": ""
+ },
+ {
+ "type": "datasource",
+ "id": "influxdb",
+ "name": "InfluxDB",
+ "version": "1.0.0"
+ },
+ {
+ "type": "panel",
+ "id": "table",
+ "name": "Table",
+ "version": ""
+ }
+ ],
"annotations": {
"list": [
{
- "datasource": "yardstick",
+ "datasource": "${DS_YARDSTICK}",
"enable": true,
"hide": false,
"iconColor": "rgb(248, 255, 0)",
"limit": 100,
"name": "status",
- "query": "SELECT tg__0.collect_stats.Status FROM \"tc_heat_rfc2544_ipv4_1rule_1flow_trex\" WHERE \"tg__0.collect_stats.Status\"='Success' AND task_id='$task_id'",
+ "query": "SELECT tg__0.collect_stats.Status FROM $test_name WHERE \"tg__0.collect_stats.Status\"='Success' AND task_id='$task_id'",
"showIn": 0,
"titleColumn": "Status",
"type": "alert"
@@ -19,7 +55,7 @@
"gnetId": null,
"graphTooltip": 0,
"hideControls": false,
- "id": 6,
+ "id": null,
"links": [],
"refresh": false,
"rows": [
@@ -32,7 +68,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": "yardstick",
+ "datasource": "${DS_YARDSTICK}",
"fill": 0,
"hideTimeOverride": true,
"id": 3,
@@ -66,8 +102,8 @@
"measurement": "/^$test_name$/",
"orderByTime": "ASC",
"policy": "default",
- "query": "SELECT \"tg__0.collect_stats.xe0.InBytes\", \"tg__0.collect_stats.xe0.OutBytes\", \"tg__0.collect_stats.xe1.InBytes\", \"tg__0.collect_stats.xe1.OutBytes\" FROM /^$test_name$/ WHERE \"task_id\" =~ /^$task_id$/ AND \"tg__0.collect_stats.PktSize\" =~ /^$framesize$/ AND $timeFilter",
- "rawQuery": false,
+ "query": "SELECT \"tg__0.collect_stats.xe0.InBytes\", \"tg__0.collect_stats.xe0.OutBytes\", \"tg__0.collect_stats.xe1.InBytes\", \"tg__0.collect_stats.xe1.OutBytes\" FROM /^$test_name$/ WHERE \"task_id\" =~ /^$task_id$/ AND $timeFilter",
+ "rawQuery": true,
"refId": "A",
"resultFormat": "time_series",
"select": [
@@ -166,7 +202,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": "yardstick",
+ "datasource": "${DS_YARDSTICK}",
"fill": 1,
"id": 1,
"legend": {
@@ -196,7 +232,7 @@
"alias": "$col",
"dsType": "influxdb",
"groupBy": [],
- "measurement": "tc_heat_rfc2544_ipv4_1rule_1flow_trex",
+ "measurement": "/^$test_name$/",
"orderByTime": "ASC",
"policy": "default",
"refId": "A",
@@ -273,7 +309,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": null,
+ "datasource": "${DS_YARDSTICK}",
"fill": 1,
"id": 5,
"legend": {
@@ -528,11 +564,8 @@
"list": [
{
"allValue": null,
- "current": {
- "text": "tc_heat_rfc2544_ipv4_1rule_1flow_trex",
- "value": "tc_heat_rfc2544_ipv4_1rule_1flow_trex"
- },
- "datasource": "yardstick",
+ "current": {},
+ "datasource": "${DS_YARDSTICK}",
"hide": 0,
"includeAll": false,
"label": "test_name",
@@ -551,18 +584,15 @@
},
{
"allValue": null,
- "current": {
- "text": "004b5387-74b3-4cf0-9597-5198db9e2731",
- "value": "004b5387-74b3-4cf0-9597-5198db9e2731"
- },
- "datasource": "yardstick",
+ "current": {},
+ "datasource": "${DS_YARDSTICK}",
"hide": 0,
"includeAll": false,
"label": "task_id",
"multi": false,
"name": "task_id",
"options": [],
- "query": "SHOW TAG VALUES FROM $test_name WITH KEY = \"task_id\" ",
+ "query": "SHOW TAG VALUES FROM $test_name WITH KEY = \"task_id\" ",
"refresh": 2,
"regex": "",
"sort": 0,
@@ -575,8 +605,8 @@
]
},
"time": {
- "from": "2019-03-05T12:44:02.829Z",
- "to": "2019-03-05T13:32:27.585Z"
+ "from": "2019-03-06T13:54:13.610Z",
+ "to": "2019-03-06T13:56:59.693Z"
},
"timepicker": {
"refresh_intervals": [
@@ -605,5 +635,5 @@
},
"timezone": "",
"title": "RFC2544",
- "version": 2
+ "version": 4
}
diff --git a/dashboard/RFC2544_2Port_Multiframesize.json b/dashboard/RFC2544_2Port_Multiframesize.json
index f08cf3d47..2d8e9522f 100644
--- a/dashboard/RFC2544_2Port_Multiframesize.json
+++ b/dashboard/RFC2544_2Port_Multiframesize.json
@@ -1,14 +1,44 @@
{
+ "__inputs": [
+ {
+ "name": "DS_YARDSTICK",
+ "label": "yardstick",
+ "description": "",
+ "type": "datasource",
+ "pluginId": "influxdb",
+ "pluginName": "InfluxDB"
+ }
+ ],
+ "__requires": [
+ {
+ "type": "grafana",
+ "id": "grafana",
+ "name": "Grafana",
+ "version": "4.4.3"
+ },
+ {
+ "type": "panel",
+ "id": "graph",
+ "name": "Graph",
+ "version": ""
+ },
+ {
+ "type": "datasource",
+ "id": "influxdb",
+ "name": "InfluxDB",
+ "version": "1.0.0"
+ }
+ ],
"annotations": {
"list": [
{
- "datasource": "yardstick",
+ "datasource": "${DS_YARDSTICK}",
"enable": true,
"hide": false,
"iconColor": "rgb(248, 255, 0)",
"limit": 100,
"name": "status",
- "query": "SELECT tg__0.collect_stats.Status FROM \"tc_heat_rfc2544_ipv4_1rule_1flow_trex\" WHERE \"tg__0.collect_stats.Status\"='Success' AND task_id='$task_id'",
+ "query": "SELECT tg__0.collect_stats.Status FROM $test_name WHERE \"tg__0.collect_stats.Status\"='Success' AND task_id='$task_id'",
"showIn": 0,
"titleColumn": "Status",
"type": "alert"
@@ -19,7 +49,7 @@
"gnetId": null,
"graphTooltip": 0,
"hideControls": false,
- "id": 2,
+ "id": null,
"links": [],
"refresh": false,
"rows": [
@@ -32,7 +62,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": "yardstick",
+ "datasource": "${DS_YARDSTICK}",
"fill": 0,
"hideTimeOverride": true,
"id": 3,
@@ -172,7 +202,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": "yardstick",
+ "datasource": "${DS_YARDSTICK}",
"fill": 1,
"id": 1,
"legend": {
@@ -202,7 +232,7 @@
"alias": "$col",
"dsType": "influxdb",
"groupBy": [],
- "measurement": "tc_heat_rfc2544_ipv4_1rule_1flow_trex",
+ "measurement": "/^$test_name$/",
"orderByTime": "ASC",
"policy": "default",
"refId": "A",
@@ -285,7 +315,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": null,
+ "datasource": "${DS_YARDSTICK}",
"fill": 1,
"id": 5,
"legend": {
@@ -408,7 +438,7 @@
"bars": true,
"dashLength": 10,
"dashes": false,
- "datasource": null,
+ "datasource": "${DS_YARDSTICK}",
"fill": 1,
"id": 4,
"legend": {
@@ -723,11 +753,8 @@
"list": [
{
"allValue": null,
- "current": {
- "text": "tc_heat_rfc2544_ipv4_1rule_1flow_trex",
- "value": "tc_heat_rfc2544_ipv4_1rule_1flow_trex"
- },
- "datasource": "yardstick",
+ "current": {},
+ "datasource": "${DS_YARDSTICK}",
"hide": 0,
"includeAll": false,
"label": "test_name",
@@ -746,11 +773,8 @@
},
{
"allValue": null,
- "current": {
- "text": "fdb337ec-11ea-410f-b45e-83f30edb7590",
- "value": "fdb337ec-11ea-410f-b45e-83f30edb7590"
- },
- "datasource": "yardstick",
+ "current": {},
+ "datasource": "${DS_YARDSTICK}",
"hide": 0,
"includeAll": false,
"label": "task_id",
@@ -770,7 +794,6 @@
{
"allValue": null,
"current": {
- "tags": [],
"text": "64B + 128B + 512B",
"value": [
"64B",
@@ -881,5 +904,5 @@
},
"timezone": "",
"title": "RFC2544 Multi framesize",
- "version": 14
+ "version": 15
}
diff --git a/docs/testing/user/userguide/14-nsb-operation.rst b/docs/testing/user/userguide/14-nsb-operation.rst
index 941a0bb65..69ffb8a3b 100644
--- a/docs/testing/user/userguide/14-nsb-operation.rst
+++ b/docs/testing/user/userguide/14-nsb-operation.rst
@@ -640,3 +640,37 @@ A testcase can be started with the following command as an example:
.. code-block:: bash
yardstick task start /yardstick/samples/vnf_samples/nsut/vpe/tc_baremetal_rfc2544_ipv4_1flow_64B_ixia.yaml
+
+Preparing test run of vIPSEC test case
+------------------------------------
+
+Location of vIPSEC test cases: ``samples/vnf_samples/nsut/ipsec/``.
+
+Before running a specific vIPSEC test case using NSB, some dependencies have to be
+preinstalled and properly configured.
+- VPP
+
+.. code-block:: console
+
+ export UBUNTU="xenial"
+ export RELEASE=".stable.1810"
+ sudo rm /etc/apt/sources.list.d/99fd.io.list
+ echo "deb [trusted=yes] https://nexus.fd.io/content/repositories/fd.io$RELEASE.ubuntu.$UBUNTU.main/ ./" | sudo tee -a /etc/apt/sources.list.d/99fd.io.list
+ sudo apt-get update
+ sudo apt-get install vpp vpp-lib vpp-plugin vpp-dbg vpp-dev vpp-api-java vpp-api-python vpp-api-lua
+
+- VAT templates
+
+ VAT templates is required for the VPP API.
+
+.. code-block:: console
+
+ mkdir -p /opt/nsb_bin/vpp/templates/
+ echo 'exec trace add dpdk-input 50' > /opt/nsb_bin/vpp/templates/enable_dpdk_traces.vat
+ echo 'exec trace add vhost-user-input 50' > /opt/nsb_bin/vpp/templates/enable_vhost_user_traces.vat
+ echo 'exec trace add memif-input 50' > /opt/nsb_bin/vpp/templates/enable_memif_traces.vat
+ cat > /opt/nsb_bin/vpp/templates/dump_interfaces.vat << EOL
+ sw_interface_dump
+ dump_interface_table
+ quit
+ EOL
diff --git a/docs/testing/user/userguide/nsb/nsb-list-of-tcs.rst b/docs/testing/user/userguide/nsb/nsb-list-of-tcs.rst
index 6c18c7d89..a578216da 100644
--- a/docs/testing/user/userguide/nsb/nsb-list-of-tcs.rst
+++ b/docs/testing/user/userguide/nsb/nsb-list-of-tcs.rst
@@ -36,3 +36,4 @@ NSB PROX Test Case Descriptions
tc_vfw_rfc2544
tc_vfw_rfc2544_correlated
tc_vfw_rfc3511
+ tc_vpp_baremetal_crypto_ipsec
diff --git a/docs/testing/user/userguide/nsb/tc_vpp_baremetal_crypto_ipsec.rst b/docs/testing/user/userguide/nsb/tc_vpp_baremetal_crypto_ipsec.rst
new file mode 100644
index 000000000..6a4a37697
--- /dev/null
+++ b/docs/testing/user/userguide/nsb/tc_vpp_baremetal_crypto_ipsec.rst
@@ -0,0 +1,113 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International
+.. License.
+.. http://creativecommons.org/licenses/by/4.0
+.. (c) OPNFV, 2019 Viosoft Corporation.
+
+***********************************************
+Yardstick Test Case Description: NSB VPP IPSEC
+***********************************************
+
++------------------------------------------------------------------------------+
+|NSB VPP test for vIPSEC characterization |
+| |
++--------------+---------------------------------------------------------------+
+|test case id | tc_baremetal_rfc2544_ipv4_{crypto_dev}_{crypto_alg} |
+| | |
+| | * crypto_dev = HW_cryptodev or SW_cryptodev; |
+| | * crypto_alg = aes-gcm or cbc-sha1; |
+| | |
++--------------+---------------------------------------------------------------+
+|metric | * Network Throughput NDR or PDR; |
+| | * Connections Per Second (CPS); |
+| | * Latency; |
+| | * Number of tunnels; |
+| | * TG Packets Out; |
+| | * TG Packets In; |
+| | * VNF Packets Out; |
+| | * VNF Packets In; |
+| | * Dropped packets; |
+| | |
++--------------+---------------------------------------------------------------+
+|test purpose | IPv4 IPsec tunnel mode performance test: |
+| | |
+| | * Finds and reports throughput NDR (Non Drop Rate) with zero |
+| | packet loss tolerance or throughput PDR (Partial Drop Rate) |
+| | with non-zero packet loss tolerance (LT) expressed in |
+| | number of packets transmitted. |
+| | |
+| | * The IPSEC test cases are implemented to run in baremetal |
+| | |
++--------------+---------------------------------------------------------------+
+|configuration | The IPSEC test cases are listed below: |
+| | |
+| | * tc_baremetal_rfc2544_ipv4_hw_aesgcm_IMIX_trex.yaml |
+| | * tc_baremetal_rfc2544_ipv4_hw_aesgcm_trex.yaml |
+| | * tc_baremetal_rfc2544_ipv4_hw_cbcsha1_IMIX_trex.yaml |
+| | * tc_baremetal_rfc2544_ipv4_hw_cbcsha1_trex.yaml |
+| | * tc_baremetal_rfc2544_ipv4_sw_aesgcm_IMIX_trex.yaml |
+| | * tc_baremetal_rfc2544_ipv4_sw_aesgcm_trex.yaml |
+| | * tc_baremetal_rfc2544_ipv4_sw_cbcsha1_IMIX_trex.yaml |
+| | * tc_baremetal_rfc2544_ipv4_sw_cbcsha1_trex.yaml |
+| | |
+| | Test duration is set as 500sec for each test. |
+| | Packet size set as 64 bytes or higher. |
+| | Number of tunnels set as 1 or higher. |
+| | Number of connections set as 1 or higher |
+| | These can be configured |
+| | |
++--------------+---------------------------------------------------------------+
+|test tool | Vector Packet Processing (VPP) |
+| | The VPP platform is an extensible framework that provides |
+| | out-of-the-box production quality switch/router functionality.|
+| | Its high performance, proven technology, its modularity and, |
+| | flexibility and rich feature set |
+| | |
++--------------+---------------------------------------------------------------+
+|applicability | This VPP IPSEC test cases can be configured with different: |
+| | |
+| | * packet sizes; |
+| | * test durations; |
+| | * tolerated loss; |
+| | * crypto device type; |
+| | * number of physical cores; |
+| | * number of tunnels; |
+| | * number of connections; |
+| | * encryption algorithms - integrity algorithm; |
+| | |
+| | Default values exist. |
+| | |
++--------------+---------------------------------------------------------------+
+|pre-test | For Baremetal tests cases VPP and DPDK must be installed in |
+|conditions | the hosts where the test is executed. The pod.yaml file must |
+| | have the necessary system and NIC information |
+| | |
++--------------+---------------------------------------------------------------+
+|test sequence | description and expected result |
+| | |
++--------------+---------------------------------------------------------------+
+|step 1 | For Baremetal test: The TG and VNF are started on the hosts |
+| | based on the pod file. |
+| | |
++--------------+---------------------------------------------------------------+
+|step 2 | Yardstick is connected with the TG and VNF by using ssh. |
+| | The test will resolve the topology and instantiate the VNF |
+| | and TG and collect the KPI's/metrics. |
+| | |
++--------------+---------------------------------------------------------------+
+|step 3 | Test packets are generated by TG on links to DUTs. If the |
+| | number of dropped packets is more than the tolerated loss |
+| | the line rate or throughput is halved. This is done until |
+| | the dropped packets are within an acceptable tolerated loss. |
+| | |
+| | The KPI is the number of packets per second for a packet size |
+| | specified in the test case with an accepted minimal packet |
+| | loss for the default configuration. |
+| | |
++--------------+---------------------------------------------------------------+
+|step 4 | In Baremetal test: The test quits the application and unbind |
+| | the DPDK ports. |
+| | |
++--------------+---------------------------------------------------------------+
+|test verdict | The test case will achieve a Throughput with an accepted |
+| | minimal tolerated packet loss. |
++--------------+---------------------------------------------------------------+ \ No newline at end of file
diff --git a/samples/vnf_samples/nsut/cmts/k8s_vcmts_topology.yaml b/samples/vnf_samples/nsut/cmts/k8s_vcmts_topology.yaml
new file mode 100755
index 000000000..95ac76964
--- /dev/null
+++ b/samples/vnf_samples/nsut/cmts/k8s_vcmts_topology.yaml
@@ -0,0 +1,36 @@
+# Copyright (c) 2019 Viosoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{% set num_tg = get(extra_args, 'num_tg', 2) %}
+{% set num_sg = get(extra_args, 'num_sg', 2) %}
+
+nsd:nsd-catalog:
+ nsd:
+ - id: vcmts-topology
+ name: vcmts-topology
+ short-name: vcmts-topology
+ description: vcmts-topology
+ constituent-vnfd:
+ {% for tg_num in range(0, num_tg) %}
+ - member-vnf-index: '{{ tg_num + 1 }}'
+ vnfd-id-ref: tg__{{ tg_num }}
+ VNF model: ../../vnf_descriptors/tg_vcmts_tpl.yaml #VNF type
+ {% endfor %}
+ {% for vnf_num in range(0, num_sg * 2) %}
+ - member-vnf-index: '{{ vnf_num + num_tg + 1 }}'
+ vnfd-id-ref: vnf__{{ vnf_num }}
+ VNF model: ../../vnf_descriptors/vnf_vcmts_tpl.yaml #VNF type
+ {% endfor %}
+
+ vld: []
diff --git a/samples/vnf_samples/nsut/cmts/tc_vcmts_k8s_pktgen.yaml b/samples/vnf_samples/nsut/cmts/tc_vcmts_k8s_pktgen.yaml
new file mode 100755
index 000000000..6c85a0892
--- /dev/null
+++ b/samples/vnf_samples/nsut/cmts/tc_vcmts_k8s_pktgen.yaml
@@ -0,0 +1,360 @@
+# Copyright (c) 2019 Viosoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+{% set num_tg = 2 %}
+{% set num_sg = 4 %}
+
+{% set vcmtsd_image = "vcmts-d:perf" %}
+{% set qat_on = false %}
+
+schema: "yardstick:task:0.1"
+
+scenarios:
+- type: NSPerf
+ traffic_profile: ../../traffic_profiles/fixed.yaml
+ extra_args:
+ num_sg: {{ num_sg }}
+ num_tg: {{ num_tg }}
+ topology: k8s_vcmts_topology.yaml
+ nodes:
+{% for tg_num in range(0, num_tg) %}
+ tg__{{ tg_num }}: pktgen{{ tg_num }}-k8syardstick
+{% endfor %}
+{% for vnf_index in range(0, num_sg) %}
+ vnf__{{ vnf_index * 2 }}: vnf{{ vnf_index }}us-k8syardstick
+ vnf__{{ (vnf_index * 2) + 1}}: vnf{{ vnf_index }}ds-k8syardstick
+{% endfor %}
+
+ runner:
+ type: Duration
+ duration: 120
+
+ options:
+ vcmts_influxdb_ip: "10.80.5.150"
+ vcmts_influxdb_port: 8086
+ vcmtsd_values: /etc/yardstick/vcmtsd_values.yaml
+ pktgen_values: /etc/yardstick/pktgen_values.yaml
+ pktgen_rate: 6.5
+{% for vnf_index in range(0, num_sg) %}
+ vnf__{{ vnf_index * 2 }}:
+ sg_id: {{ vnf_index }}
+ stream_dir: "us"
+ vnf__{{ (vnf_index * 2) + 1}}:
+ sg_id: {{ vnf_index }}
+ stream_dir: "ds"
+{% endfor %}
+{% for tg_num in range(0, num_tg) %}
+ tg__{{ tg_num }}:
+ pktgen_id: {{ tg_num }}
+{% endfor %}
+
+context:
+ name: k8syardstick
+ type: Kubernetes
+
+ servers:
+{% for vnf_index in range(0, num_sg) %}
+ vnf{{ vnf_index }}us:
+ nodeSelector:
+ vcmts: "true"
+ containers:
+ - image: {{ vcmtsd_image }}
+ imagePullPolicy: IfNotPresent
+ env:
+ - name: CMK_PROC_FS
+ value: "/host/proc"
+ command: /bin/bash
+ args: ['-c', 'mkdir /root/.ssh; cp /tmp/.ssh/authorized_keys ~/.ssh/.;
+ chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; service ssh restart;
+ while true ; do sleep 10000; done']
+ resources:
+ requests:
+ memory: 10Ki
+{% if qat_on %}
+ hugepages-1Gi: 2Gi
+ qat.intel.com/generic: '1'
+{% else %}
+ hugepages-1Gi: 1Gi
+{% endif %}
+ limits:
+ memory: 1Gi
+{% if qat_on %}
+ hugepages-1Gi: 2Gi
+ qat.intel.com/generic: '1'
+{% else %}
+ hugepages-1Gi: 1Gi
+{% endif %}
+ lifecycle:
+ postStart:
+ exec:
+ command: [ "/bin/bash", "-c", "env > /tmp/qat" ]
+ volumeMounts:
+ - name: vcmts-configmap-vcmtspod
+ mountPath: /vcmts-config
+ - name: hugepages
+ mountPath: /hugepages
+ readOnly: false
+ - name: collectd
+ mountPath: /opt/collectd/var
+ readOnly: false
+ - name: sysfs
+ mountPath: /sys
+ readOnly: false
+ - name: sriov
+ mountPath: /sriov-cni
+ readOnly: false
+ - name: host-proc
+ mountPath: /host/proc
+ readOnly: true
+ - name: cmk-install-dir
+ mountPath: /opt/bin
+ - name: cmk-conf-dir
+ mountPath: /etc/cmk
+ - name: power-mgmt
+ mountPath: /opt/power_mgmt
+ ports:
+ - containerPort: 22022
+ securityContext:
+ allowPrivilegeEscalation: true
+ privileged: true
+ node_ports:
+ - name: lua # Lower case alphanumeric characters or '-'
+ port: 22022
+ networks:
+ - flannel
+ - xe0
+ - xe1
+ volumes:
+ - name: vcmts-configmap-vcmtspod
+ configMap:
+ name: vcmts-configmap-vcmtspod
+ defaultMode: 0744
+ - name: hugepages
+ emptyDir:
+ medium: HugePages
+ - name: collectd
+ hostPath:
+ path: /opt/collectd/var
+ - name: sysfs
+ hostPath:
+ path: /sys
+ - name: sriov
+ hostPath:
+ path: /var/lib/cni/sriov
+ - name: cmk-install-dir
+ hostPath:
+ path: /opt/bin
+ - name: host-proc
+ hostPath:
+ path: /proc
+ - name: cmk-conf-dir
+ hostPath:
+ path: /etc/cmk
+ - name: power-mgmt
+ hostPath:
+ path: /opt/power_mgmt
+
+ vnf{{ vnf_index }}ds:
+ nodeSelector:
+ vcmts: "true"
+ containers:
+ - image: {{ vcmtsd_image }}
+ imagePullPolicy: IfNotPresent
+ env:
+ - name: CMK_PROC_FS
+ value: "/host/proc"
+ command: /bin/bash
+ args: ['-c', 'mkdir /root/.ssh; cp /tmp/.ssh/authorized_keys ~/.ssh/.;
+ chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; service ssh restart;
+ while true ; do sleep 10000; done']
+ resources:
+ requests:
+ memory: 10Ki
+{% if qat_on %}
+ hugepages-1Gi: 2Gi
+ qat.intel.com/generic: '1'
+{% else %}
+ hugepages-1Gi: 1Gi
+{% endif %}
+ limits:
+ memory: 1Gi
+{% if qat_on %}
+ hugepages-1Gi: 2Gi
+ qat.intel.com/generic: '1'
+{% else %}
+ hugepages-1Gi: 1Gi
+{% endif %}
+ lifecycle:
+ postStart:
+ exec:
+ command: [ "/bin/bash", "-c", "env > /tmp/qat" ]
+ volumeMounts:
+ - name: vcmts-configmap-vcmtspod
+ mountPath: /vcmts-config
+ - name: hugepages
+ mountPath: /hugepages
+ readOnly: false
+ - name: collectd
+ mountPath: /opt/collectd/var
+ readOnly: false
+ - name: sysfs
+ mountPath: /sys
+ readOnly: false
+ - name: sriov
+ mountPath: /sriov-cni
+ readOnly: false
+ - name: host-proc
+ mountPath: /host/proc
+ readOnly: true
+ - name: cmk-install-dir
+ mountPath: /opt/bin
+ - name: cmk-conf-dir
+ mountPath: /etc/cmk
+ - name: power-mgmt
+ mountPath: /opt/power_mgmt
+ ports:
+ - containerPort: 22022
+ securityContext:
+ allowPrivilegeEscalation: true
+ privileged: true
+ node_ports:
+ - name: lua # Lower case alphanumeric characters or '-'
+ port: 22022
+ networks:
+ - flannel
+ - xe0
+ - xe1
+ volumes:
+ - name: vcmts-configmap-vcmtspod
+ configMap:
+ name: vcmts-configmap-vcmtspod
+ defaultMode: 0744
+ - name: hugepages
+ emptyDir:
+ medium: HugePages
+ - name: collectd
+ hostPath:
+ path: /opt/collectd/var
+ - name: sysfs
+ hostPath:
+ path: /sys
+ - name: sriov
+ hostPath:
+ path: /var/lib/cni/sriov
+ - name: cmk-install-dir
+ hostPath:
+ path: /opt/bin
+ - name: host-proc
+ hostPath:
+ path: /proc
+ - name: cmk-conf-dir
+ hostPath:
+ path: /etc/cmk
+ - name: power-mgmt
+ hostPath:
+ path: /opt/power_mgmt
+{% endfor %}
+
+{% for index in range(0, num_tg) %}
+ pktgen{{index}}:
+ nodeSelector:
+ vcmtspktgen: "true"
+ containers:
+ - image: vcmts-pktgen:v18.10
+ imagePullPolicy: IfNotPresent
+ tty: true
+ stdin: true
+ env:
+ - name: LUA_PATH
+ value: "/vcmts/Pktgen.lua"
+ - name: CMK_PROC_FS
+ value: "/host/proc"
+ command: /bin/bash
+ args: ['-c', 'mkdir /root/.ssh; cp /tmp/.ssh/authorized_keys ~/.ssh/.;
+ chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; service ssh restart;
+ while true ; do sleep 10000; done']
+ resources:
+ requests:
+ hugepages-1Gi: 9Gi
+ memory: 200Mi
+ limits:
+ hugepages-1Gi: 9Gi
+ memory: 200Mi
+ volumeMounts:
+ - name: sysfs
+ mountPath: /sys
+ readOnly: false
+ - name: hugepages
+ mountPath: /hugepages
+ readOnly: false
+ - name: sriov
+ mountPath: /sriov-cni
+ readOnly: false
+ - name: host-proc
+ mountPath: /host/proc
+ readOnly: true
+ - name: cmk-install-dir
+ mountPath: /opt/bin
+ - name: cmk-conf-dir
+ mountPath: /etc/cmk
+ - name: pktgen-config
+ mountPath: /pktgen-config
+ ports:
+ - containerPort: 22022
+ securityContext:
+ allowPrivilegeEscalation: true
+ privileged: true
+ volumes:
+ - name: sysfs
+ hostPath:
+ path: /sys
+ - name: hugepages
+ emptyDir:
+ medium: HugePages
+ - name: sriov
+ hostPath:
+ path: /var/lib/cni/sriov
+ - name: cmk-install-dir
+ hostPath:
+ path: /opt/bin
+ - name: host-proc
+ hostPath:
+ path: /proc
+ - name: cmk-conf-dir
+ hostPath:
+ path: /etc/cmk
+ - name: pktgen-config
+ configMap:
+ name: vcmts-configmap-pktgen
+ defaultMode: 0744
+ node_ports:
+ - name: lua # Lower case alphanumeric characters or '-'
+ port: 22022
+ networks:
+ - flannel
+ - xe0
+ - xe1
+{% endfor %}
+
+ networks:
+ flannel:
+ args: '[{ "delegate": { "isDefaultGateway": true }}]'
+ plugin: flannel
+ xe0:
+ args: '[{ "delegate": { "isDefaultGateway": true }}]'
+ plugin: flannel
+ xe1:
+ args: '[{ "delegate": { "isDefaultGateway": true }}]'
+ plugin: flannel
diff --git a/samples/vnf_samples/vnf_descriptors/tg_vcmts_tpl.yaml b/samples/vnf_samples/vnf_descriptors/tg_vcmts_tpl.yaml
new file mode 100755
index 000000000..bb56fcb6a
--- /dev/null
+++ b/samples/vnf_samples/vnf_descriptors/tg_vcmts_tpl.yaml
@@ -0,0 +1,77 @@
+# Copyright (c) 2019 Viosoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+vnfd:vnfd-catalog:
+ vnfd:
+ - id: VcmtsPktgen
+ name: vcmtspktgen
+ short-name: vcmtspktgen
+ description: vCMTS Pktgen Kubernetes
+ vm-flavor:
+ vcpu-count: '4'
+ memory-mb: '4096'
+ mgmt-interface:
+ vdu-id: vcmtspktgen-kubernetes
+ {% if user is defined %}
+ user: '{{user}}' # Value filled by vnfdgen
+ {% endif %}
+ {% if password is defined %}
+ password: '{{password}}' # Value filled by vnfdgen
+ {% endif %}
+ {% if ip is defined %}
+ ip: '{{ip}}' # Value filled by vnfdgen
+ {% endif %}
+ {% if key_filename is defined %}
+ key_filename: '{{key_filename}}' # Value filled by vnfdgen
+ {% endif %}
+ connection-point:
+ - name: xe0
+ type: VPORT
+ - name: xe1
+ type: VPORT
+ vdu:
+ - id: vcmtspktgen-kubernetes
+ name: vcmtspktgen-kubernetes
+ description: vCMTS Pktgen Kubernetes
+ external-interface:
+ - name: xe0
+ virtual-interface:
+ type: virtio
+ # Substitution variables MUST be quoted. Otherwise Python can misinterpet them.
+ vpci: '{{ interfaces.xe0.vpci }}' # Value filled by vnfdgen
+ local_iface_name: eth0 # '{{ interfaces.xe0.local_iface_name }}'
+ driver: '{{ interfaces.xe0.driver}}' # Value filled by vnfdgen
+ local_ip: '{{ interfaces.xe0.local_ip }}' # Value filled by vnfdgen
+ dst_ip: '{{ interfaces.xe0.dst_ip }}' # Value filled by vnfdgen
+ local_mac: '{{ interfaces.xe0.local_mac }}' # Value filled by vnfdgen
+ dst_mac: '{{ interfaces.xe0.dst_mac }}' # Value filled by vnfdgen
+ bandwidth: 10 Gbps
+ vnfd-connection-point-ref: xe0
+ - name: xe1
+ virtual-interface:
+ type: virtio
+ # Substitution variables MUST be quoted. Otherwise Python can misinterpet them.
+ vpci: '{{ interfaces.xe1.vpci }}' # Value filled by vnfdgen
+ local_iface_name: eth0 # '{{ interfaces.xe1.local_iface_name }}'
+ local_ip: '{{ interfaces.xe1.local_ip }}' # Value filled by vnfdgen
+ driver: '{{ interfaces.xe1.driver}}' # Value filled by vnfdgen
+ dst_ip: '{{ interfaces.xe1.dst_ip }}' # Value filled by vnfdgen
+ local_mac: '{{ interfaces.xe1.local_mac }}' # Value filled by vnfdgen
+ dst_mac: '{{ interfaces.xe1.dst_mac }}' # Value filled by vnfdgen
+ bandwidth: 10 Gbps
+ vnfd-connection-point-ref: xe0
+ benchmark:
+ kpi:
+ - upstream/bits_per_second
+
diff --git a/samples/vnf_samples/vnf_descriptors/vnf_vcmts_tpl.yaml b/samples/vnf_samples/vnf_descriptors/vnf_vcmts_tpl.yaml
new file mode 100755
index 000000000..d1eb6a869
--- /dev/null
+++ b/samples/vnf_samples/vnf_descriptors/vnf_vcmts_tpl.yaml
@@ -0,0 +1,77 @@
+# Copyright (c) 2019 Viosoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+vnfd:vnfd-catalog:
+ vnfd:
+ - id: VcmtsVNF
+ name: vcmtsvnf
+ short-name: vcmtsvnf
+ description: vCMTS Upstream-Downstream Kubernetes
+ vm-flavor:
+ vcpu-count: '4'
+ memory-mb: '4096'
+ mgmt-interface:
+ vdu-id: vcmtsvnf-kubernetes
+ {% if user is defined %}
+ user: '{{user}}' # Value filled by vnfdgen
+ {% endif %}
+ {% if password is defined %}
+ password: '{{password}}' # Value filled by vnfdgen
+ {% endif %}
+ {% if ip is defined %}
+ ip: '{{ip}}' # Value filled by vnfdgen
+ {% endif %}
+ {% if key_filename is defined %}
+ key_filename: '{{key_filename}}' # Value filled by vnfdgen
+ {% endif %}
+ connection-point:
+ - name: xe0
+ type: VPORT
+ - name: xe1
+ type: VPORT
+ vdu:
+ - id: vcmtsvnf-kubernetes
+ name: vcmtsvnf-kubernetes
+ description: vCMTS Upstream-Downstream Kubernetes
+ external-interface:
+ - name: xe0
+ virtual-interface:
+ type: virtio
+ # Substitution variables MUST be quoted. Otherwise Python can misinterpet them.
+ vpci: '{{ interfaces.xe0.vpci }}' # Value filled by vnfdgen
+ local_iface_name: eth0 # NOT TESTED YET '{{ interfaces.xe0.local_iface_name }}' # Value filled by vnfdgen
+ driver: '{{ interfaces.xe0.driver}}' # Value filled by vnfdgen
+ local_ip: '{{ interfaces.xe0.local_ip }}' # Value filled by vnfdgen
+ dst_ip: '{{ interfaces.xe0.dst_ip }}' # Value filled by vnfdgen
+ local_mac: '{{ interfaces.xe0.local_mac }}' # Value filled by vnfdgen
+ dst_mac: '{{ interfaces.xe0.dst_mac }}' # Value filled by vnfdgen
+ bandwidth: 10 Gbps
+ vnfd-connection-point-ref: xe0
+ - name: xe1
+ virtual-interface:
+ type: virtio
+ # Substitution variables MUST be quoted. Otherwise Python can misinterpet them.
+ vpci: '{{ interfaces.xe1.vpci }}' # Value filled by vnfdgen
+ local_iface_name: eth0 # NOT TESTED YET '{{ interfaces.xe1.local_iface_name }}' # Value filled by vnfdgen
+ local_ip: '{{ interfaces.xe1.local_ip }}' # Value filled by vnfdgen
+ driver: '{{ interfaces.xe1.driver}}' # Value filled by vnfdgen
+ dst_ip: '{{ interfaces.xe1.dst_ip }}' # Value filled by vnfdgen
+ local_mac: '{{ interfaces.xe1.local_mac }}' # Value filled by vnfdgen
+ dst_mac: '{{ interfaces.xe1.dst_mac }}' # Value filled by vnfdgen
+ bandwidth: 10 Gbps
+ vnfd-connection-point-ref: xe0
+ benchmark:
+ kpi:
+ - upstream/bits_per_second
+
diff --git a/yardstick/network_services/vnf_generic/vnf/tg_vcmts_pktgen.py b/yardstick/network_services/vnf_generic/vnf/tg_vcmts_pktgen.py
new file mode 100755
index 000000000..c6df9d04c
--- /dev/null
+++ b/yardstick/network_services/vnf_generic/vnf/tg_vcmts_pktgen.py
@@ -0,0 +1,215 @@
+# Copyright (c) 2019 Viosoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import time
+import socket
+import yaml
+import os
+
+from yardstick.network_services.vnf_generic.vnf import sample_vnf
+from yardstick.common import exceptions
+
+
+LOG = logging.getLogger(__name__)
+
+
+class PktgenHelper(object):
+
+ RETRY_SECONDS = 0.5
+ RETRY_COUNT = 20
+ CONNECT_TIMEOUT = 5
+
+ def __init__(self, host, port=23000):
+ self.host = host
+ self.port = port
+ self.connected = False
+
+ def _connect(self):
+ self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ ret = True
+ try:
+ self._sock.settimeout(self.CONNECT_TIMEOUT)
+ self._sock.connect((self.host, self.port))
+ except (socket.gaierror, socket.error, socket.timeout):
+ self._sock.close()
+ ret = False
+
+ return ret
+
+ def connect(self):
+ if self.connected:
+ return True
+ LOG.info("Connecting to pktgen instance at %s...", self.host)
+ for idx in range(self.RETRY_COUNT):
+ self.connected = self._connect()
+ if self.connected:
+ return True
+ LOG.debug("Connection attempt %d: Unable to connect to %s, " \
+ "retrying in %d seconds",
+ idx, self.host, self.RETRY_SECONDS)
+ time.sleep(self.RETRY_SECONDS)
+
+ LOG.error("Unable to connect to pktgen instance on %s !",
+ self.host)
+ return False
+
+
+ def send_command(self, command):
+ if not self.connected:
+ LOG.error("Pktgen socket is not connected")
+ return False
+
+ try:
+ self._sock.sendall((command + "\n").encode())
+ time.sleep(1)
+ except (socket.timeout, socket.error):
+ LOG.error("Error sending command '%s'", command)
+ return False
+
+ return True
+
+
+class VcmtsPktgenSetupEnvHelper(sample_vnf.SetupEnvHelper):
+
+ BASE_PARAMETERS = "export LUA_PATH=/vcmts/Pktgen.lua;"\
+ + "export CMK_PROC_FS=/host/proc;"
+
+ PORTS_COUNT = 8
+
+ def generate_pcap_filename(self, port_cfg):
+ return port_cfg['traffic_type'] + "_" + port_cfg['num_subs'] \
+ + "cms_" + port_cfg['num_ofdm'] + "ofdm.pcap"
+
+ def find_port_cfg(self, ports_cfg, port_name):
+ for port_cfg in ports_cfg:
+ if port_name in port_cfg:
+ return port_cfg
+ return None
+
+ def build_pktgen_parameters(self, pod_cfg):
+ ports_cfg = pod_cfg['ports']
+ port_cfg = list()
+
+ for i in range(self.PORTS_COUNT):
+ port_cfg.append(self.find_port_cfg(ports_cfg, 'port_' + str(i)))
+
+ pktgen_parameters = self.BASE_PARAMETERS + " " \
+ + " /pktgen-config/setup.sh " + pod_cfg['pktgen_id'] \
+ + " " + pod_cfg['num_ports']
+
+ for i in range(self.PORTS_COUNT):
+ pktgen_parameters += " " + port_cfg[i]['net_pktgen']
+
+ for i in range(self.PORTS_COUNT):
+ pktgen_parameters += " " + self.generate_pcap_filename(port_cfg[i])
+
+ return pktgen_parameters
+
+ def start_pktgen(self, pod_cfg):
+ self.ssh_helper.drop_connection()
+ cmd = self.build_pktgen_parameters(pod_cfg)
+ LOG.debug("Executing: '%s'", cmd)
+ self.ssh_helper.send_command(cmd)
+ LOG.info("Pktgen executed")
+
+ def setup_vnf_environment(self):
+ pass
+
+
+class VcmtsPktgen(sample_vnf.SampleVNFTrafficGen):
+
+ TG_NAME = 'VcmtsPktgen'
+ APP_NAME = 'VcmtsPktgen'
+ RUN_WAIT = 4
+ DEFAULT_RATE = 8.0
+
+ PKTGEN_BASE_PORT = 23000
+
+ def __init__(self, name, vnfd, setup_env_helper_type=None,
+ resource_helper_type=None):
+ if setup_env_helper_type is None:
+ setup_env_helper_type = VcmtsPktgenSetupEnvHelper
+ super(VcmtsPktgen, self).__init__(
+ name, vnfd, setup_env_helper_type, resource_helper_type)
+
+ self.pktgen_address = vnfd['mgmt-interface']['ip']
+ LOG.info("Pktgen container '%s', IP: %s", name, self.pktgen_address)
+
+ def extract_pod_cfg(self, pktgen_pods_cfg, pktgen_id):
+ for pod_cfg in pktgen_pods_cfg:
+ if pod_cfg['pktgen_id'] == pktgen_id:
+ return pod_cfg
+ return None
+
+ def instantiate(self, scenario_cfg, context_cfg):
+ super(VcmtsPktgen, self).instantiate(scenario_cfg, context_cfg)
+ self._start_server()
+ options = scenario_cfg.get('options', {})
+ self.pktgen_rate = options.get('pktgen_rate', self.DEFAULT_RATE)
+
+ try:
+ pktgen_values_filepath = options['pktgen_values']
+ except KeyError:
+ raise KeyError("Missing pktgen_values key in scenario options" \
+ "section of the task definition file")
+
+ if not os.path.isfile(pktgen_values_filepath):
+ raise RuntimeError("The pktgen_values file path provided " \
+ "does not exists")
+
+ # The yaml_loader.py (SafeLoader) underlying regex has an issue
+ # with reading PCI addresses (processed as double). so the
+ # BaseLoader is used here.
+ with open(pktgen_values_filepath) as stream:
+ pktgen_values = yaml.load(stream, Loader=yaml.BaseLoader)
+
+ if pktgen_values == None:
+ raise RuntimeError("Error reading pktgen_values file provided (" +
+ pktgen_values_filepath + ")")
+
+ self.pktgen_id = int(options[self.name]['pktgen_id'])
+ self.resource_helper.pktgen_id = self.pktgen_id
+
+ self.pktgen_helper = PktgenHelper(self.pktgen_address,
+ self.PKTGEN_BASE_PORT + self.pktgen_id)
+
+ pktgen_pods_cfg = pktgen_values['topology']['pktgen_pods']
+
+ self.pod_cfg = self.extract_pod_cfg(pktgen_pods_cfg,
+ str(self.pktgen_id))
+
+ if self.pod_cfg == None:
+ raise KeyError("Pktgen with id " + str(self.pktgen_id) + \
+ " was not found")
+
+ self.setup_helper.start_pktgen(self.pod_cfg)
+
+ def run_traffic(self, traffic_profile):
+ if not self.pktgen_helper.connect():
+ raise exceptions.PktgenActionError(command="connect")
+ LOG.info("Connected to pktgen instance at %s", self.pktgen_address)
+
+ commands = []
+ for i in range(self.setup_helper.PORTS_COUNT):
+ commands.append('pktgen.set("' + str(i) + '", "rate", ' +
+ "%0.1f" % self.pktgen_rate + ');')
+
+ commands.append('pktgen.start("all");')
+
+ for command in commands:
+ if self.pktgen_helper.send_command(command):
+ LOG.debug("Command '%s' sent to pktgen", command)
+ LOG.info("Traffic started on %s...", self.name)
+ return True
diff --git a/yardstick/network_services/vnf_generic/vnf/vcmts_vnf.py b/yardstick/network_services/vnf_generic/vnf/vcmts_vnf.py
new file mode 100755
index 000000000..0b48ef4e9
--- /dev/null
+++ b/yardstick/network_services/vnf_generic/vnf/vcmts_vnf.py
@@ -0,0 +1,273 @@
+# Copyright (c) 2019 Viosoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import os
+import yaml
+
+from influxdb import InfluxDBClient
+
+from yardstick.network_services.vnf_generic.vnf.sample_vnf import SetupEnvHelper
+from yardstick.common import constants
+from yardstick.common import exceptions
+from yardstick.network_services.vnf_generic.vnf.base import GenericVNF
+from yardstick.network_services.vnf_generic.vnf.sample_vnf import ScenarioHelper
+from yardstick.network_services.vnf_generic.vnf.vnf_ssh_helper import VnfSshHelper
+from yardstick.network_services.utils import get_nsb_option
+
+
+LOG = logging.getLogger(__name__)
+
+
+class InfluxDBHelper(object):
+
+ INITIAL_VALUE = 'now() - 1m'
+
+ def __init__(self, vcmts_influxdb_ip, vcmts_influxdb_port):
+ self._vcmts_influxdb_ip = vcmts_influxdb_ip
+ self._vcmts_influxdb_port = vcmts_influxdb_port
+ self._last_upstream_rx = self.INITIAL_VALUE
+ self._last_values_time = dict()
+
+ def start(self):
+ self._read_client = InfluxDBClient(host=self._vcmts_influxdb_ip,
+ port=self._vcmts_influxdb_port,
+ database='collectd')
+ self._write_client = InfluxDBClient(host=constants.INFLUXDB_IP,
+ port=constants.INFLUXDB_PORT,
+ database='collectd')
+
+ def _get_last_value_time(self, measurement):
+ if measurement in self._last_values_time:
+ return self._last_values_time[measurement]
+ return self.INITIAL_VALUE
+
+ def _set_last_value_time(self, measurement, time):
+ self._last_values_time[measurement] = "'" + time + "'"
+
+ def _query_measurement(self, measurement):
+ # There is a delay before influxdb flushes the data
+ query = "SELECT * FROM " + measurement + " WHERE time > " \
+ + self._get_last_value_time(measurement) \
+ + " ORDER BY time ASC;"
+ query_result = self._read_client.query(query)
+ if len(query_result.keys()) == 0:
+ return None
+ return query_result.get_points(measurement)
+
+ def _rw_measurment(self, measurement, columns):
+ query_result = self._query_measurement(measurement)
+ if query_result == None:
+ return
+
+ points_to_write = list()
+ for entry in query_result:
+ point = {
+ "measurement": measurement,
+ "tags": {
+ "type": entry['type'],
+ "host": entry['host']
+ },
+ "time": entry['time'],
+ "fields": {}
+ }
+
+ for column in columns:
+ if column == 'value':
+ point["fields"][column] = float(entry[column])
+ else:
+ point["fields"][column] = entry[column]
+
+ points_to_write.append(point)
+ self._set_last_value_time(measurement, entry['time'])
+
+ # Write the points to yardstick database
+ if self._write_client.write_points(points_to_write):
+ LOG.debug("%d new points written to '%s' measurement",
+ len(points_to_write), measurement)
+
+ def copy_kpi(self):
+ self._rw_measurment("cpu_value", ["instance", "type_instance", "value"])
+ self._rw_measurment("cpufreq_value", ["type_instance", "value"])
+ self._rw_measurment("downstream_rx", ["value"])
+ self._rw_measurment("downstream_tx", ["value"])
+ self._rw_measurment("downstream_value", ["value"])
+ self._rw_measurment("ds_per_cm_value", ["instance", "value"])
+ self._rw_measurment("intel_rdt_value", ["instance", "type_instance", "value"])
+ self._rw_measurment("turbostat_value", ["instance", "type_instance", "value"])
+ self._rw_measurment("upstream_rx", ["value"])
+ self._rw_measurment("upstream_tx", ["value"])
+ self._rw_measurment("upstream_value", ["value"])
+
+
+class VcmtsdSetupEnvHelper(SetupEnvHelper):
+
+ BASE_PARAMETERS = "export LD_LIBRARY_PATH=/opt/collectd/lib:;"\
+ + "export CMK_PROC_FS=/host/proc;"
+
+ def build_us_parameters(self, pod_cfg):
+ return self.BASE_PARAMETERS + " " \
+ + " /opt/bin/cmk isolate --conf-dir=/etc/cmk" \
+ + " --socket-id=" + pod_cfg['cpu_socket_id'] \
+ + " --pool=shared" \
+ + " /vcmts-config/run_upstream.sh " + pod_cfg['sg_id'] \
+ + " " + pod_cfg['ds_core_type'] \
+ + " " + pod_cfg['num_ofdm'] + "ofdm" \
+ + " " + pod_cfg['num_subs'] + "cm" \
+ + " " + pod_cfg['cm_crypto'] \
+ + " " + pod_cfg['qat'] \
+ + " " + pod_cfg['net_us'] \
+ + " " + pod_cfg['power_mgmt']
+
+ def build_ds_parameters(self, pod_cfg):
+ return self.BASE_PARAMETERS + " " \
+ + " /opt/bin/cmk isolate --conf-dir=/etc/cmk" \
+ + " --socket-id=" + pod_cfg['cpu_socket_id'] \
+ + " --pool=" + pod_cfg['ds_core_type'] \
+ + " /vcmts-config/run_downstream.sh " + pod_cfg['sg_id'] \
+ + " " + pod_cfg['ds_core_type'] \
+ + " " + pod_cfg['ds_core_pool_index'] \
+ + " " + pod_cfg['num_ofdm'] + "ofdm" \
+ + " " + pod_cfg['num_subs'] + "cm" \
+ + " " + pod_cfg['cm_crypto'] \
+ + " " + pod_cfg['qat'] \
+ + " " + pod_cfg['net_ds'] \
+ + " " + pod_cfg['power_mgmt']
+
+ def build_cmd(self, stream_dir, pod_cfg):
+ if stream_dir == 'ds':
+ return self.build_ds_parameters(pod_cfg)
+ else:
+ return self.build_us_parameters(pod_cfg)
+
+ def run_vcmtsd(self, stream_dir, pod_cfg):
+ cmd = self.build_cmd(stream_dir, pod_cfg)
+ LOG.debug("Executing %s", cmd)
+ self.ssh_helper.send_command(cmd)
+
+ def setup_vnf_environment(self):
+ pass
+
+
+class VcmtsVNF(GenericVNF):
+
+ RUN_WAIT = 4
+
+ def __init__(self, name, vnfd):
+ super(VcmtsVNF, self).__init__(name, vnfd)
+ self.name = name
+ self.bin_path = get_nsb_option('bin_path', '')
+ self.scenario_helper = ScenarioHelper(self.name)
+ self.ssh_helper = VnfSshHelper(self.vnfd_helper.mgmt_interface, self.bin_path)
+
+ self.setup_helper = VcmtsdSetupEnvHelper(self.vnfd_helper,
+ self.ssh_helper,
+ self.scenario_helper)
+
+ def extract_pod_cfg(self, vcmts_pods_cfg, sg_id):
+ for pod_cfg in vcmts_pods_cfg:
+ if pod_cfg['sg_id'] == sg_id:
+ return pod_cfg
+
+ def instantiate(self, scenario_cfg, context_cfg):
+ self._update_collectd_options(scenario_cfg, context_cfg)
+ self.scenario_helper.scenario_cfg = scenario_cfg
+ self.context_cfg = context_cfg
+
+ options = scenario_cfg.get('options', {})
+
+ try:
+ self.vcmts_influxdb_ip = options['vcmts_influxdb_ip']
+ self.vcmts_influxdb_port = options['vcmts_influxdb_port']
+ except KeyError:
+ raise KeyError("Missing destination InfluxDB details in scenario" \
+ " section of the task definition file")
+
+ try:
+ vcmtsd_values_filepath = options['vcmtsd_values']
+ except KeyError:
+ raise KeyError("Missing vcmtsd_values key in scenario options" \
+ "section of the task definition file")
+
+ if not os.path.isfile(vcmtsd_values_filepath):
+ raise RuntimeError("The vcmtsd_values file path provided " \
+ "does not exists")
+
+ # The yaml_loader.py (SafeLoader) underlying regex has an issue
+ # with reading PCI addresses (processed as double). so the
+ # BaseLoader is used here.
+ with open(vcmtsd_values_filepath) as stream:
+ vcmtsd_values = yaml.load(stream, Loader=yaml.BaseLoader)
+
+ if vcmtsd_values == None:
+ raise RuntimeError("Error reading vcmtsd_values file provided (" +
+ vcmtsd_values_filepath + ")")
+
+ vnf_options = options.get(self.name, {})
+ sg_id = str(vnf_options['sg_id'])
+ stream_dir = vnf_options['stream_dir']
+
+ try:
+ vcmts_pods_cfg = vcmtsd_values['topology']['vcmts_pods']
+ except KeyError:
+ raise KeyError("Missing vcmts_pods key in the " \
+ "vcmtsd_values file provided")
+
+ pod_cfg = self.extract_pod_cfg(vcmts_pods_cfg, sg_id)
+ if pod_cfg == None:
+ raise exceptions.IncorrectConfig(error_msg="Service group " + sg_id + " not found")
+
+ self.setup_helper.run_vcmtsd(stream_dir, pod_cfg)
+
+ def _update_collectd_options(self, scenario_cfg, context_cfg):
+ scenario_options = scenario_cfg.get('options', {})
+ generic_options = scenario_options.get('collectd', {})
+ scenario_node_options = scenario_options.get(self.name, {})\
+ .get('collectd', {})
+ context_node_options = context_cfg.get('nodes', {})\
+ .get(self.name, {}).get('collectd', {})
+
+ options = generic_options
+ self._update_options(options, scenario_node_options)
+ self._update_options(options, context_node_options)
+
+ self.setup_helper.collectd_options = options
+
+ def _update_options(self, options, additional_options):
+ for k, v in additional_options.items():
+ if isinstance(v, dict) and k in options:
+ options[k].update(v)
+ else:
+ options[k] = v
+
+ def wait_for_instantiate(self):
+ pass
+
+ def terminate(self):
+ pass
+
+ def scale(self, flavor=""):
+ pass
+
+ def collect_kpi(self):
+ self.influxdb_helper.copy_kpi()
+ return {"n/a": "n/a"}
+
+ def start_collect(self):
+ self.influxdb_helper = InfluxDBHelper(self.vcmts_influxdb_ip,
+ self.vcmts_influxdb_port)
+ self.influxdb_helper.start()
+
+ def stop_collect(self):
+ pass
diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_vcmts_pktgen.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_vcmts_pktgen.py
new file mode 100755
index 000000000..3b226d3f1
--- /dev/null
+++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_vcmts_pktgen.py
@@ -0,0 +1,652 @@
+# Copyright (c) 2019 Viosoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+import mock
+import socket
+import threading
+import time
+import os
+import copy
+
+from yardstick.benchmark.contexts import base as ctx_base
+from yardstick.network_services.vnf_generic.vnf.base import VnfdHelper
+from yardstick.network_services.vnf_generic.vnf import tg_vcmts_pktgen
+from yardstick.common import exceptions
+
+
+NAME = "tg__0"
+
+
+class TestPktgenHelper(unittest.TestCase):
+
+ def test___init__(self):
+ pktgen_helper = tg_vcmts_pktgen.PktgenHelper("localhost", 23000)
+ self.assertEqual(pktgen_helper.host, "localhost")
+ self.assertEqual(pktgen_helper.port, 23000)
+ self.assertFalse(pktgen_helper.connected)
+
+ def _run_fake_server(self):
+ server_sock = socket.socket()
+ server_sock.bind(('localhost', 23000))
+ server_sock.listen(0)
+ client_socket, _ = server_sock.accept()
+ client_socket.close()
+ server_sock.close()
+
+ def test__connect(self):
+ pktgen_helper = tg_vcmts_pktgen.PktgenHelper("localhost", 23000)
+ self.assertFalse(pktgen_helper._connect())
+ server_thread = threading.Thread(target=self._run_fake_server)
+ server_thread.start()
+ time.sleep(0.5)
+ self.assertTrue(pktgen_helper._connect())
+ pktgen_helper._sock.close()
+ server_thread.join()
+
+ @mock.patch('yardstick.network_services.vnf_generic.vnf.tg_vcmts_pktgen.time')
+ def test_connect(self, *args):
+ pktgen_helper = tg_vcmts_pktgen.PktgenHelper("localhost", 23000)
+ pktgen_helper.connected = True
+ self.assertTrue(pktgen_helper.connect())
+ pktgen_helper.connected = False
+
+ pktgen_helper._connect = mock.MagicMock(return_value=True)
+ self.assertTrue(pktgen_helper.connect())
+ self.assertTrue(pktgen_helper.connected)
+
+ pktgen_helper = tg_vcmts_pktgen.PktgenHelper("localhost", 23000)
+ pktgen_helper._connect = mock.MagicMock(return_value=False)
+ self.assertFalse(pktgen_helper.connect())
+ self.assertFalse(pktgen_helper.connected)
+
+ def test_send_command(self):
+ pktgen_helper = tg_vcmts_pktgen.PktgenHelper("localhost", 23000)
+ self.assertFalse(pktgen_helper.send_command(""))
+
+ pktgen_helper.connected = True
+ pktgen_helper._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.assertFalse(pktgen_helper.send_command(""))
+
+ pktgen_helper._sock = mock.MagicMock()
+ self.assertTrue(pktgen_helper.send_command(""))
+
+
+class TestVcmtsPktgenSetupEnvHelper(unittest.TestCase):
+
+ PKTGEN_PARAMETERS = "export LUA_PATH=/vcmts/Pktgen.lua;"\
+ "export CMK_PROC_FS=/host/proc;"\
+ " /pktgen-config/setup.sh 0 4 18:02.0 "\
+ "18:02.1 18:02.2 18:02.3 00:00.0 00:00.0 "\
+ "00:00.0 00:00.0 imix1_100cms_1ofdm.pcap "\
+ "imix1_100cms_1ofdm.pcap imix1_100cms_1ofdm.pcap "\
+ "imix1_100cms_1ofdm.pcap imix1_100cms_1ofdm.pcap "\
+ "imix1_100cms_1ofdm.pcap imix1_100cms_1ofdm.pcap "\
+ "imix1_100cms_1ofdm.pcap"
+
+ OPTIONS = {
+ "pktgen_values": "/tmp/pktgen_values.yaml",
+ "tg__0": {
+ "pktgen_id": 0
+ },
+ "vcmts_influxdb_ip": "10.80.5.150",
+ "vcmts_influxdb_port": 8086,
+ "vcmtsd_values": "/tmp/vcmtsd_values.yaml",
+ "vnf__0": {
+ "sg_id": 0,
+ "stream_dir": "us"
+ },
+ "vnf__1": {
+ "sg_id": 0,
+ "stream_dir": "ds"
+ }
+ }
+
+ def setUp(self):
+ vnfd_helper = VnfdHelper(
+ TestVcmtsPktgen.VNFD['vnfd:vnfd-catalog']['vnfd'][0])
+ ssh_helper = mock.Mock()
+ scenario_helper = mock.Mock()
+ scenario_helper.options = self.OPTIONS
+
+ self.setup_helper = tg_vcmts_pktgen.VcmtsPktgenSetupEnvHelper(
+ vnfd_helper, ssh_helper, scenario_helper)
+
+ def test_generate_pcap_filename(self):
+ pcap_file_name = self.setup_helper.generate_pcap_filename(\
+ TestVcmtsPktgen.PKTGEN_POD_VALUES[0]['ports'][0])
+ self.assertEquals(pcap_file_name, "imix1_100cms_1ofdm.pcap")
+
+ def test_find_port_cfg(self):
+ port_cfg = self.setup_helper.find_port_cfg(\
+ TestVcmtsPktgen.PKTGEN_POD_VALUES[0]['ports'], "port_0")
+ self.assertIsNotNone(port_cfg)
+
+ port_cfg = self.setup_helper.find_port_cfg(\
+ TestVcmtsPktgen.PKTGEN_POD_VALUES[0]['ports'], "port_8")
+ self.assertIsNone(port_cfg)
+
+ def test_build_pktgen_parameters(self):
+ parameters = self.setup_helper.build_pktgen_parameters(
+ TestVcmtsPktgen.PKTGEN_POD_VALUES[0])
+ self.assertEquals(parameters, self.PKTGEN_PARAMETERS)
+
+ def test_start_pktgen(self):
+ self.setup_helper.ssh_helper = mock.MagicMock()
+ self.setup_helper.start_pktgen(TestVcmtsPktgen.PKTGEN_POD_VALUES[0])
+ self.setup_helper.ssh_helper.send_command.assert_called_with(
+ self.PKTGEN_PARAMETERS)
+
+ def test_setup_vnf_environment(self):
+ self.assertIsNone(self.setup_helper.setup_vnf_environment())
+
+class TestVcmtsPktgen(unittest.TestCase):
+
+ VNFD = {'vnfd:vnfd-catalog':
+ {'vnfd':
+ [{
+ "benchmark": {
+ "kpi": [
+ "upstream/bits_per_second"
+ ]
+ },
+ "connection-point": [
+ {
+ "name": "xe0",
+ "type": "VPORT"
+ },
+ {
+ "name": "xe1",
+ "type": "VPORT"
+ }
+ ],
+ "description": "vCMTS Pktgen Kubernetes",
+ "id": "VcmtsPktgen",
+ "mgmt-interface": {
+ "ip": "192.168.24.150",
+ "key_filename": "/tmp/yardstick_key-a3b663c2",
+ "user": "root",
+ "vdu-id": "vcmtspktgen-kubernetes"
+ },
+ "name": "vcmtspktgen",
+ "short-name": "vcmtspktgen",
+ "vdu": [
+ {
+ "description": "vCMTS Pktgen Kubernetes",
+ "external-interface": [],
+ "id": "vcmtspktgen-kubernetes",
+ "name": "vcmtspktgen-kubernetes"
+ }
+ ],
+ "vm-flavor": {
+ "memory-mb": "4096",
+ "vcpu-count": "4"
+ }
+ }]
+ }}
+
+ PKTGEN_POD_VALUES = [
+ {
+ "num_ports": "4",
+ "pktgen_id": "0",
+ "ports": [
+ {
+ "net_pktgen": "18:02.0",
+ "num_ofdm": "1",
+ "num_subs": "100",
+ "port_0": "",
+ "traffic_type": "imix1"
+ },
+ {
+ "net_pktgen": "18:02.1",
+ "num_ofdm": "1",
+ "num_subs": "100",
+ "port_1": "",
+ "traffic_type": "imix1"
+ },
+ {
+ "net_pktgen": "18:02.2",
+ "num_ofdm": "1",
+ "num_subs": "100",
+ "port_2": "",
+ "traffic_type": "imix1"
+ },
+ {
+ "net_pktgen": "18:02.3",
+ "num_ofdm": "1",
+ "num_subs": "100",
+ "port_3": "",
+ "traffic_type": "imix1"
+ },
+ {
+ "net_pktgen": "00:00.0",
+ "num_ofdm": "1",
+ "num_subs": "100",
+ "port_4": "",
+ "traffic_type": "imix1"
+ },
+ {
+ "net_pktgen": "00:00.0",
+ "num_ofdm": "1",
+ "num_subs": "100",
+ "port_5": "",
+ "traffic_type": "imix1"
+ },
+ {
+ "net_pktgen": "00:00.0",
+ "num_ofdm": "1",
+ "num_subs": "100",
+ "port_6": "",
+ "traffic_type": "imix1"
+ },
+ {
+ "net_pktgen": "00:00.0",
+ "num_ofdm": "1",
+ "num_subs": "100",
+ "port_7": "",
+ "traffic_type": "imix1"
+ }
+ ]
+ },
+ {
+ "num_ports": 4,
+ "pktgen_id": 1,
+ "ports": [
+ {
+ "net_pktgen": "18:0a.0",
+ "num_ofdm": "1",
+ "num_subs": "100",
+ "port_0": "",
+ "traffic_type": "imix1"
+ },
+ {
+ "net_pktgen": "18:0a.1",
+ "num_ofdm": "1",
+ "num_subs": "100",
+ "port_1": "",
+ "traffic_type": "imix1"
+ },
+ {
+ "net_pktgen": "18:0a.2",
+ "num_ofdm": "1",
+ "num_subs": "100",
+ "port_2": "",
+ "traffic_type": "imix1"
+ },
+ {
+ "net_pktgen": "18:0a.3",
+ "num_ofdm": "1",
+ "num_subs": "100",
+ "port_3": "",
+ "traffic_type": "imix1"
+ },
+ {
+ "net_pktgen": "00:00.0",
+ "num_ofdm": "1",
+ "num_subs": "100",
+ "port_4": "",
+ "traffic_type": "imix1"
+ },
+ {
+ "net_pktgen": "00:00.0",
+ "num_ofdm": "1",
+ "num_subs": "100",
+ "port_5": "",
+ "traffic_type": "imix1"
+ },
+ {
+ "net_pktgen": "00:00.0",
+ "num_ofdm": "1",
+ "num_subs": "100",
+ "port_6": "",
+ "traffic_type": "imix1"
+ },
+ {
+ "net_pktgen": "00:00.0",
+ "num_ofdm": "1",
+ "num_subs": "100",
+ "port_7": "",
+ "traffic_type": "imix1"
+ }
+ ]
+ }
+ ]
+
+ SCENARIO_CFG = {
+ "nodes": {
+ "tg__0": "pktgen0-k8syardstick-a3b663c2",
+ "vnf__0": "vnf0us-k8syardstick-a3b663c2",
+ "vnf__1": "vnf0ds-k8syardstick-a3b663c2"
+ },
+ "options": {
+ "pktgen_values": "/tmp/pktgen_values.yaml",
+ "tg__0": {
+ "pktgen_id": 0
+ },
+ "vcmts_influxdb_ip": "10.80.5.150",
+ "vcmts_influxdb_port": 8086,
+ "vcmtsd_values": "/tmp/vcmtsd_values.yaml",
+ "vnf__0": {
+ "sg_id": 0,
+ "stream_dir": "us"
+ },
+ "vnf__1": {
+ "sg_id": 0,
+ "stream_dir": "ds"
+ }
+ },
+ "task_id": "a3b663c2-e616-4777-b6d0-ec2ea7a06f42",
+ "task_path": "samples/vnf_samples/nsut/cmts",
+ "tc": "tc_vcmts_k8s_pktgen",
+ "topology": "k8s_vcmts_topology.yaml",
+ "traffic_profile": "../../traffic_profiles/fixed.yaml",
+ "type": "NSPerf"
+ }
+
+ CONTEXT_CFG = {
+ "networks": {
+ "flannel": {
+ "name": "flannel"
+ },
+ "xe0": {
+ "name": "xe0"
+ },
+ "xe1": {
+ "name": "xe1"
+ }
+ },
+ "nodes": {
+ "tg__0": {
+ "VNF model": "../../vnf_descriptors/tg_vcmts_tpl.yaml",
+ "interfaces": {
+ "flannel": {
+ "local_ip": "192.168.24.150",
+ "local_mac": None,
+ "network_name": "flannel"
+ },
+ "xe0": {
+ "local_ip": "192.168.24.150",
+ "local_mac": None,
+ "network_name": "xe0"
+ },
+ "xe1": {
+ "local_ip": "192.168.24.150",
+ "local_mac": None,
+ "network_name": "xe1"
+ }
+ },
+ "ip": "192.168.24.150",
+ "key_filename": "/tmp/yardstick_key-a3b663c2",
+ "member-vnf-index": "1",
+ "name": "pktgen0-k8syardstick-a3b663c2",
+ "private_ip": "192.168.24.150",
+ "service_ports": [
+ {
+ "name": "ssh",
+ "node_port": 60270,
+ "port": 22,
+ "protocol": "TCP",
+ "target_port": 22
+ },
+ {
+ "name": "lua",
+ "node_port": 43619,
+ "port": 22022,
+ "protocol": "TCP",
+ "target_port": 22022
+ }
+ ],
+ "ssh_port": 60270,
+ "user": "root",
+ "vnfd-id-ref": "tg__0"
+ },
+ "vnf__0": {
+ "VNF model": "../../vnf_descriptors/vnf_vcmts_tpl.yaml",
+ "interfaces": {
+ "flannel": {
+ "local_ip": "192.168.100.132",
+ "local_mac": None,
+ "network_name": "flannel"
+ },
+ "xe0": {
+ "local_ip": "192.168.100.132",
+ "local_mac": None,
+ "network_name": "xe0"
+ },
+ "xe1": {
+ "local_ip": "192.168.100.132",
+ "local_mac": None,
+ "network_name": "xe1"
+ }
+ },
+ "ip": "192.168.100.132",
+ "key_filename": "/tmp/yardstick_key-a3b663c2",
+ "member-vnf-index": "3",
+ "name": "vnf0us-k8syardstick-a3b663c2",
+ "private_ip": "192.168.100.132",
+ "service_ports": [
+ {
+ "name": "ssh",
+ "node_port": 57057,
+ "port": 22,
+ "protocol": "TCP",
+ "target_port": 22
+ },
+ {
+ "name": "lua",
+ "node_port": 29700,
+ "port": 22022,
+ "protocol": "TCP",
+ "target_port": 22022
+ }
+ ],
+ "ssh_port": 57057,
+ "user": "root",
+ "vnfd-id-ref": "vnf__0"
+ },
+ "vnf__1": {
+ "VNF model": "../../vnf_descriptors/vnf_vcmts_tpl.yaml",
+ "interfaces": {
+ "flannel": {
+ "local_ip": "192.168.100.134",
+ "local_mac": None,
+ "network_name": "flannel"
+ },
+ "xe0": {
+ "local_ip": "192.168.100.134",
+ "local_mac": None,
+ "network_name": "xe0"
+ },
+ "xe1": {
+ "local_ip": "192.168.100.134",
+ "local_mac": None,
+ "network_name": "xe1"
+ }
+ },
+ "ip": "192.168.100.134",
+ "key_filename": "/tmp/yardstick_key-a3b663c2",
+ "member-vnf-index": "4",
+ "name": "vnf0ds-k8syardstick-a3b663c2",
+ "private_ip": "192.168.100.134",
+ "service_ports": [
+ {
+ "name": "ssh",
+ "node_port": 18581,
+ "port": 22,
+ "protocol": "TCP",
+ "target_port": 22
+ },
+ {
+ "name": "lua",
+ "node_port": 18469,
+ "port": 22022,
+ "protocol": "TCP",
+ "target_port": 22022
+ }
+ ],
+ "ssh_port": 18581,
+ "user": "root",
+ "vnfd-id-ref": "vnf__1"
+ }
+ }
+ }
+
+ PKTGEN_VALUES_PATH = "/tmp/pktgen_values.yaml"
+
+ PKTGEN_VALUES = \
+ "serviceAccount: cmk-serviceaccount\n" \
+ "images:\n" \
+ " vcmts_pktgen: vcmts-pktgen:v18.10\n" \
+ "topology:\n" \
+ " pktgen_replicas: 8\n" \
+ " pktgen_pods:\n" \
+ " - pktgen_id: 0\n" \
+ " num_ports: 4\n" \
+ " ports:\n" \
+ " - port_0:\n" \
+ " traffic_type: 'imix2'\n" \
+ " num_ofdm: 4\n" \
+ " num_subs: 300\n" \
+ " net_pktgen: 8a:02.0\n" \
+ " - port_1:\n" \
+ " traffic_type: 'imix2'\n" \
+ " num_ofdm: 4\n" \
+ " num_subs: 300\n" \
+ " net_pktgen: 8a:02.1\n" \
+ " - port_2:\n" \
+ " traffic_type: 'imix2'\n" \
+ " num_ofdm: 4\n" \
+ " num_subs: 300\n" \
+ " net_pktgen: 8a:02.2\n" \
+ " - port_3:\n" \
+ " traffic_type: 'imix2'\n" \
+ " num_ofdm: 4\n" \
+ " num_subs: 300\n" \
+ " net_pktgen: 8a:02.3\n" \
+ " - port_4:\n" \
+ " traffic_type: 'imix2'\n" \
+ " num_ofdm: 4\n" \
+ " num_subs: 300\n" \
+ " net_pktgen: 8a:02.4\n" \
+ " - port_5:\n" \
+ " traffic_type: 'imix2'\n" \
+ " num_ofdm: 4\n" \
+ " num_subs: 300\n" \
+ " net_pktgen: 8a:02.5\n" \
+ " - port_6:\n" \
+ " traffic_type: 'imix2'\n" \
+ " num_ofdm: 4\n" \
+ " num_subs: 300\n" \
+ " net_pktgen: 8a:02.6\n" \
+ " - port_7:\n" \
+ " traffic_type: 'imix2'\n" \
+ " num_ofdm: 4\n" \
+ " num_subs: 300\n" \
+ " net_pktgen: 8a:02.7\n"
+
+ def setUp(self):
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ self.vcmts_pktgen = tg_vcmts_pktgen.VcmtsPktgen(NAME, vnfd)
+ self.vcmts_pktgen._start_server = mock.Mock(return_value=0)
+ self.vcmts_pktgen.resource_helper = mock.MagicMock()
+ self.vcmts_pktgen.setup_helper = mock.MagicMock()
+
+ def test___init__(self):
+ self.assertFalse(self.vcmts_pktgen.traffic_finished)
+ self.assertIsNotNone(self.vcmts_pktgen.setup_helper)
+ self.assertIsNotNone(self.vcmts_pktgen.resource_helper)
+
+ def test_extract_pod_cfg(self):
+ pod_cfg = self.vcmts_pktgen.extract_pod_cfg(self.PKTGEN_POD_VALUES, "0")
+ self.assertIsNotNone(pod_cfg)
+ self.assertEqual(pod_cfg["pktgen_id"], "0")
+ pod_cfg = self.vcmts_pktgen.extract_pod_cfg(self.PKTGEN_POD_VALUES, "4")
+ self.assertIsNone(pod_cfg)
+
+ @mock.patch.object(ctx_base.Context, 'get_context_from_server',
+ return_value='fake_context')
+ def test_instantiate_missing_pktgen_values_key(self, *args):
+ err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG)
+ err_scenario_cfg['options'].pop('pktgen_values', None)
+ with self.assertRaises(KeyError):
+ self.vcmts_pktgen.instantiate(err_scenario_cfg, self.CONTEXT_CFG)
+
+ @mock.patch.object(ctx_base.Context, 'get_context_from_server',
+ return_value='fake_context')
+ def test_instantiate_missing_pktgen_values_file(self, *args):
+ if os.path.isfile(self.PKTGEN_VALUES_PATH):
+ os.remove(self.PKTGEN_VALUES_PATH)
+ err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG)
+ err_scenario_cfg['options']['pktgen_values'] = self.PKTGEN_VALUES_PATH
+ with self.assertRaises(RuntimeError):
+ self.vcmts_pktgen.instantiate(err_scenario_cfg, self.CONTEXT_CFG)
+
+ @mock.patch.object(ctx_base.Context, 'get_context_from_server',
+ return_value='fake_context')
+ def test_instantiate_empty_pktgen_values_file(self, *args):
+ yaml_sample = open(self.PKTGEN_VALUES_PATH, 'w')
+ yaml_sample.write("")
+ yaml_sample.close()
+
+ err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG)
+ err_scenario_cfg['options']['pktgen_values'] = self.PKTGEN_VALUES_PATH
+ with self.assertRaises(RuntimeError):
+ self.vcmts_pktgen.instantiate(err_scenario_cfg, self.CONTEXT_CFG)
+
+ if os.path.isfile(self.PKTGEN_VALUES_PATH):
+ os.remove(self.PKTGEN_VALUES_PATH)
+
+ @mock.patch.object(ctx_base.Context, 'get_context_from_server',
+ return_value='fake_context')
+ def test_instantiate_invalid_pktgen_id(self, *args):
+ yaml_sample = open(self.PKTGEN_VALUES_PATH, 'w')
+ yaml_sample.write(self.PKTGEN_VALUES)
+ yaml_sample.close()
+
+ err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG)
+ err_scenario_cfg['options'][NAME]['pktgen_id'] = 12
+ with self.assertRaises(KeyError):
+ self.vcmts_pktgen.instantiate(err_scenario_cfg, self.CONTEXT_CFG)
+
+ if os.path.isfile(self.PKTGEN_VALUES_PATH):
+ os.remove(self.PKTGEN_VALUES_PATH)
+
+ @mock.patch.object(ctx_base.Context, 'get_context_from_server',
+ return_value='fake_context')
+ def test_instantiate_all_valid(self, *args):
+ yaml_sample = open(self.PKTGEN_VALUES_PATH, 'w')
+ yaml_sample.write(self.PKTGEN_VALUES)
+ yaml_sample.close()
+
+ self.vcmts_pktgen.instantiate(self.SCENARIO_CFG, self.CONTEXT_CFG)
+ self.assertIsNotNone(self.vcmts_pktgen.pod_cfg)
+ self.assertEqual(self.vcmts_pktgen.pod_cfg["pktgen_id"], "0")
+
+ if os.path.isfile(self.PKTGEN_VALUES_PATH):
+ os.remove(self.PKTGEN_VALUES_PATH)
+
+ def test_run_traffic_failed_connect(self):
+ self.vcmts_pktgen.pktgen_helper = mock.MagicMock()
+ self.vcmts_pktgen.pktgen_helper.connect.return_value = False
+ with self.assertRaises(exceptions.PktgenActionError):
+ self.vcmts_pktgen.run_traffic({})
+
+ def test_run_traffic_successful_connect(self):
+ self.vcmts_pktgen.pktgen_helper = mock.MagicMock()
+ self.vcmts_pktgen.pktgen_helper.connect.return_value = True
+ self.vcmts_pktgen.pktgen_rate = 8.0
+ self.assertTrue(self.vcmts_pktgen.run_traffic({}))
+ self.vcmts_pktgen.pktgen_helper.connect.assert_called_once()
+ self.vcmts_pktgen.pktgen_helper.send_command.assert_called_with(
+ 'pktgen.start("all");')
diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vcmts_vnf.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vcmts_vnf.py
new file mode 100755
index 000000000..11e3d6e17
--- /dev/null
+++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vcmts_vnf.py
@@ -0,0 +1,651 @@
+# Copyright (c) 2019 Viosoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+import mock
+import copy
+import os
+
+from yardstick.tests.unit.network_services.vnf_generic.vnf.test_base import mock_ssh
+from yardstick.network_services.vnf_generic.vnf.base import VnfdHelper
+from yardstick.network_services.vnf_generic.vnf import vcmts_vnf
+from yardstick.common import exceptions
+
+from influxdb.resultset import ResultSet
+
+NAME = "vnf__0"
+
+
+class TestInfluxDBHelper(unittest.TestCase):
+
+ def test___init__(self):
+ influxdb_helper = vcmts_vnf.InfluxDBHelper("localhost", 8086)
+ self.assertEqual(influxdb_helper._vcmts_influxdb_ip, "localhost")
+ self.assertEqual(influxdb_helper._vcmts_influxdb_port, 8086)
+ self.assertIsNotNone(influxdb_helper._last_upstream_rx)
+ self.assertIsNotNone(influxdb_helper._last_values_time)
+
+ def test_start(self):
+ influxdb_helper = vcmts_vnf.InfluxDBHelper("localhost", 8086)
+ influxdb_helper.start()
+ self.assertIsNotNone(influxdb_helper._read_client)
+ self.assertIsNotNone(influxdb_helper._write_client)
+
+ def test__get_last_value_time(self):
+ influxdb_helper = vcmts_vnf.InfluxDBHelper("localhost", 8086)
+ self.assertEqual(influxdb_helper._get_last_value_time('cpu_value'),
+ vcmts_vnf.InfluxDBHelper.INITIAL_VALUE)
+
+ influxdb_helper._last_values_time['cpu_value'] = "RANDOM"
+ self.assertEqual(influxdb_helper._get_last_value_time('cpu_value'),
+ "RANDOM")
+
+ def test__set_last_value_time(self):
+ influxdb_helper = vcmts_vnf.InfluxDBHelper("localhost", 8086)
+ influxdb_helper._set_last_value_time('cpu_value', '00:00')
+ self.assertEqual(influxdb_helper._last_values_time['cpu_value'],
+ "'00:00'")
+
+ def test__query_measurement(self):
+ influxdb_helper = vcmts_vnf.InfluxDBHelper("localhost", 8086)
+ influxdb_helper._read_client = mock.MagicMock()
+
+ resulted_generator = mock.MagicMock()
+ resulted_generator.keys.return_value = []
+ influxdb_helper._read_client.query.return_value = resulted_generator
+ query_result = influxdb_helper._query_measurement('cpu_value')
+ self.assertIsNone(query_result)
+
+ resulted_generator = mock.MagicMock()
+ resulted_generator.keys.return_value = ["", ""]
+ resulted_generator.get_points.return_value = ResultSet({"":""})
+ influxdb_helper._read_client.query.return_value = resulted_generator
+ query_result = influxdb_helper._query_measurement('cpu_value')
+ self.assertIsNotNone(query_result)
+
+ def test__rw_measurment(self):
+ influxdb_helper = vcmts_vnf.InfluxDBHelper("localhost", 8086)
+ influxdb_helper._query_measurement = mock.MagicMock()
+ influxdb_helper._query_measurement.return_value = None
+ influxdb_helper._rw_measurment('cpu_value', [])
+ self.assertEqual(len(influxdb_helper._last_values_time), 0)
+
+ entry = {
+ "type":"type",
+ "host":"host",
+ "time":"time",
+ "id": "1",
+ "value": "1.0"
+ }
+ influxdb_helper._query_measurement.return_value = [entry]
+ influxdb_helper._write_client = mock.MagicMock()
+ influxdb_helper._rw_measurment('cpu_value', ["id", "value"])
+ self.assertEqual(len(influxdb_helper._last_values_time), 1)
+ influxdb_helper._write_client.write_points.assert_called_once()
+
+ def test_copy_kpi(self):
+ influxdb_helper = vcmts_vnf.InfluxDBHelper("localhost", 8086)
+ influxdb_helper._rw_measurment = mock.MagicMock()
+ influxdb_helper.copy_kpi()
+ influxdb_helper._rw_measurment.assert_called()
+
+
+class TestVcmtsdSetupEnvHelper(unittest.TestCase):
+ POD_CFG = {
+ "cm_crypto": "aes",
+ "cpu_socket_id": "0",
+ "ds_core_pool_index": "2",
+ "ds_core_type": "exclusive",
+ "net_ds": "1a:02.1",
+ "net_us": "1a:02.0",
+ "num_ofdm": "1",
+ "num_subs": "100",
+ "power_mgmt": "pm_on",
+ "qat": "qat_off",
+ "service_group_config": "",
+ "sg_id": "0",
+ "vcmtsd_image": "vcmts-d:perf"
+ }
+
+ OPTIONS = {
+ "pktgen_values": "/tmp/pktgen_values.yaml",
+ "tg__0": {
+ "pktgen_id": 0
+ },
+ "vcmts_influxdb_ip": "10.80.5.150",
+ "vcmts_influxdb_port": 8086,
+ "vcmtsd_values": "/tmp/vcmtsd_values.yaml",
+ "vnf__0": {
+ "sg_id": 0,
+ "stream_dir": "us"
+ },
+ "vnf__1": {
+ "sg_id": 0,
+ "stream_dir": "ds"
+ }
+ }
+
+ def setUp(self):
+ vnfd_helper = VnfdHelper(
+ TestVcmtsVNF.VNFD['vnfd:vnfd-catalog']['vnfd'][0])
+ ssh_helper = mock.Mock()
+ scenario_helper = mock.Mock()
+ scenario_helper.options = self.OPTIONS
+
+ self.setup_helper = vcmts_vnf.VcmtsdSetupEnvHelper(
+ vnfd_helper, ssh_helper, scenario_helper)
+
+ def _build_us_parameters(self):
+ return vcmts_vnf.VcmtsdSetupEnvHelper.BASE_PARAMETERS + " " \
+ + " /opt/bin/cmk isolate --conf-dir=/etc/cmk" \
+ + " --socket-id=" + str(self.POD_CFG['cpu_socket_id']) \
+ + " --pool=shared" \
+ + " /vcmts-config/run_upstream.sh " + self.POD_CFG['sg_id'] \
+ + " " + self.POD_CFG['ds_core_type'] \
+ + " " + str(self.POD_CFG['num_ofdm']) + "ofdm" \
+ + " " + str(self.POD_CFG['num_subs']) + "cm" \
+ + " " + self.POD_CFG['cm_crypto'] \
+ + " " + self.POD_CFG['qat'] \
+ + " " + self.POD_CFG['net_us'] \
+ + " " + self.POD_CFG['power_mgmt']
+
+ def test_build_us_parameters(self):
+ constructed = self._build_us_parameters()
+ result = self.setup_helper.build_us_parameters(self.POD_CFG)
+ self.assertEqual(constructed, result)
+
+ def _build_ds_parameters(self):
+ return vcmts_vnf.VcmtsdSetupEnvHelper.BASE_PARAMETERS + " " \
+ + " /opt/bin/cmk isolate --conf-dir=/etc/cmk" \
+ + " --socket-id=" + str(self.POD_CFG['cpu_socket_id']) \
+ + " --pool=" + self.POD_CFG['ds_core_type'] \
+ + " /vcmts-config/run_downstream.sh " + self.POD_CFG['sg_id'] \
+ + " " + self.POD_CFG['ds_core_type'] \
+ + " " + str(self.POD_CFG['ds_core_pool_index']) \
+ + " " + str(self.POD_CFG['num_ofdm']) + "ofdm" \
+ + " " + str(self.POD_CFG['num_subs']) + "cm" \
+ + " " + self.POD_CFG['cm_crypto'] \
+ + " " + self.POD_CFG['qat'] \
+ + " " + self.POD_CFG['net_ds'] \
+ + " " + self.POD_CFG['power_mgmt']
+
+ def test_build_ds_parameters(self):
+ constructed = self._build_ds_parameters()
+ result = self.setup_helper.build_ds_parameters(self.POD_CFG)
+ self.assertEqual(constructed, result)
+
+ def test_build_cmd(self):
+ us_constructed = self._build_us_parameters()
+ us_result = self.setup_helper.build_cmd('us', self.POD_CFG)
+ self.assertEqual(us_constructed, us_result)
+ ds_constructed = self._build_ds_parameters()
+ ds_result = self.setup_helper.build_cmd('ds', self.POD_CFG)
+ self.assertEqual(ds_constructed, ds_result)
+
+ def test_run_vcmtsd(self):
+ us_constructed = self._build_us_parameters()
+
+ vnfd_helper = VnfdHelper(
+ TestVcmtsVNF.VNFD['vnfd:vnfd-catalog']['vnfd'][0])
+ ssh_helper = mock.MagicMock()
+ scenario_helper = mock.Mock()
+ scenario_helper.options = self.OPTIONS
+
+ setup_helper = vcmts_vnf.VcmtsdSetupEnvHelper(
+ vnfd_helper, ssh_helper, scenario_helper)
+
+ setup_helper.run_vcmtsd('us', self.POD_CFG)
+ ssh_helper.send_command.assert_called_with(us_constructed)
+
+ def test_setup_vnf_environment(self):
+ self.assertIsNone(self.setup_helper.setup_vnf_environment())
+
+class TestVcmtsVNF(unittest.TestCase):
+
+ VNFD = {'vnfd:vnfd-catalog':
+ {'vnfd':
+ [{
+ "benchmark": {
+ "kpi": [
+ "upstream/bits_per_second"
+ ]
+ },
+ "connection-point": [
+ {
+ "name": "xe0",
+ "type": "VPORT"
+ },
+ {
+ "name": "xe1",
+ "type": "VPORT"
+ }
+ ],
+ "description": "vCMTS Upstream-Downstream Kubernetes",
+ "id": "VcmtsVNF",
+ "mgmt-interface": {
+ "ip": "192.168.100.35",
+ "key_filename": "/tmp/yardstick_key-81dcca91",
+ "user": "root",
+ "vdu-id": "vcmtsvnf-kubernetes"
+ },
+ "name": "vcmtsvnf",
+ "short-name": "vcmtsvnf",
+ "vdu": [
+ {
+ "description": "vCMTS Upstream-Downstream Kubernetes",
+ "external-interface": [],
+ "id": "vcmtsvnf-kubernetes",
+ "name": "vcmtsvnf-kubernetes"
+ }
+ ],
+ "vm-flavor": {
+ "memory-mb": "4096",
+ "vcpu-count": "4"
+ }
+ }]
+ }
+ }
+
+ POD_CFG = [
+ {
+ "cm_crypto": "aes",
+ "cpu_socket_id": "0",
+ "ds_core_pool_index": "2",
+ "ds_core_type": "exclusive",
+ "net_ds": "1a:02.1",
+ "net_us": "1a:02.0",
+ "num_ofdm": "1",
+ "num_subs": "100",
+ "power_mgmt": "pm_on",
+ "qat": "qat_off",
+ "service_group_config": "",
+ "sg_id": "0",
+ "vcmtsd_image": "vcmts-d:perf"
+ },
+ ]
+
+ SCENARIO_CFG = {
+ "nodes": {
+ "tg__0": "pktgen0-k8syardstick-afae18b2",
+ "vnf__0": "vnf0us-k8syardstick-afae18b2",
+ "vnf__1": "vnf0ds-k8syardstick-afae18b2"
+ },
+ "options": {
+ "pktgen_values": "/tmp/pktgen_values.yaml",
+ "tg__0": {
+ "pktgen_id": 0
+ },
+ "vcmts_influxdb_ip": "10.80.5.150",
+ "vcmts_influxdb_port": 8086,
+ "vcmtsd_values": "/tmp/vcmtsd_values.yaml",
+ "vnf__0": {
+ "sg_id": 0,
+ "stream_dir": "us"
+ },
+ "vnf__1": {
+ "sg_id": 0,
+ "stream_dir": "ds"
+ }
+ },
+ "task_id": "afae18b2-9902-477f-8128-49afde7c3040",
+ "task_path": "samples/vnf_samples/nsut/cmts",
+ "tc": "tc_vcmts_k8s_pktgen",
+ "topology": "k8s_vcmts_topology.yaml",
+ "traffic_profile": "../../traffic_profiles/fixed.yaml",
+ "type": "NSPerf"
+ }
+
+ CONTEXT_CFG = {
+ "networks": {
+ "flannel": {
+ "name": "flannel"
+ },
+ "xe0": {
+ "name": "xe0"
+ },
+ "xe1": {
+ "name": "xe1"
+ }
+ },
+ "nodes": {
+ "tg__0": {
+ "VNF model": "../../vnf_descriptors/tg_vcmts_tpl.yaml",
+ "interfaces": {
+ "flannel": {
+ "local_ip": "192.168.24.110",
+ "local_mac": None,
+ "network_name": "flannel"
+ },
+ "xe0": {
+ "local_ip": "192.168.24.110",
+ "local_mac": None,
+ "network_name": "xe0"
+ },
+ "xe1": {
+ "local_ip": "192.168.24.110",
+ "local_mac": None,
+ "network_name": "xe1"
+ }
+ },
+ "ip": "192.168.24.110",
+ "key_filename": "/tmp/yardstick_key-afae18b2",
+ "member-vnf-index": "1",
+ "name": "pktgen0-k8syardstick-afae18b2",
+ "private_ip": "192.168.24.110",
+ "service_ports": [
+ {
+ "name": "ssh",
+ "node_port": 17153,
+ "port": 22,
+ "protocol": "TCP",
+ "target_port": 22
+ },
+ {
+ "name": "lua",
+ "node_port": 51250,
+ "port": 22022,
+ "protocol": "TCP",
+ "target_port": 22022
+ }
+ ],
+ "ssh_port": 17153,
+ "user": "root",
+ "vnfd-id-ref": "tg__0"
+ },
+ "vnf__0": {
+ "VNF model": "../../vnf_descriptors/vnf_vcmts_tpl.yaml",
+ "interfaces": {
+ "flannel": {
+ "local_ip": "192.168.100.53",
+ "local_mac": None,
+ "network_name": "flannel"
+ },
+ "xe0": {
+ "local_ip": "192.168.100.53",
+ "local_mac": None,
+ "network_name": "xe0"
+ },
+ "xe1": {
+ "local_ip": "192.168.100.53",
+ "local_mac": None,
+ "network_name": "xe1"
+ }
+ },
+ "ip": "192.168.100.53",
+ "key_filename": "/tmp/yardstick_key-afae18b2",
+ "member-vnf-index": "3",
+ "name": "vnf0us-k8syardstick-afae18b2",
+ "private_ip": "192.168.100.53",
+ "service_ports": [
+ {
+ "name": "ssh",
+ "node_port": 34027,
+ "port": 22,
+ "protocol": "TCP",
+ "target_port": 22
+ },
+ {
+ "name": "lua",
+ "node_port": 32580,
+ "port": 22022,
+ "protocol": "TCP",
+ "target_port": 22022
+ }
+ ],
+ "ssh_port": 34027,
+ "user": "root",
+ "vnfd-id-ref": "vnf__0"
+ },
+ "vnf__1": {
+ "VNF model": "../../vnf_descriptors/vnf_vcmts_tpl.yaml",
+ "interfaces": {
+ "flannel": {
+ "local_ip": "192.168.100.52",
+ "local_mac": None,
+ "network_name": "flannel"
+ },
+ "xe0": {
+ "local_ip": "192.168.100.52",
+ "local_mac": None,
+ "network_name": "xe0"
+ },
+ "xe1": {
+ "local_ip": "192.168.100.52",
+ "local_mac": None,
+ "network_name": "xe1"
+ }
+ },
+ "ip": "192.168.100.52",
+ "key_filename": "/tmp/yardstick_key-afae18b2",
+ "member-vnf-index": "4",
+ "name": "vnf0ds-k8syardstick-afae18b2",
+ "private_ip": "192.168.100.52",
+ "service_ports": [
+ {
+ "name": "ssh",
+ "node_port": 58661,
+ "port": 22,
+ "protocol": "TCP",
+ "target_port": 22
+ },
+ {
+ "name": "lua",
+ "node_port": 58233,
+ "port": 22022,
+ "protocol": "TCP",
+ "target_port": 22022
+ }
+ ],
+ "ssh_port": 58661,
+ "user": "root",
+ "vnfd-id-ref": "vnf__1"
+ },
+ }
+ }
+
+ VCMTSD_VALUES_PATH = "/tmp/vcmtsd_values.yaml"
+
+ VCMTSD_VALUES = \
+ "serviceAccount: cmk-serviceaccount\n" \
+ "topology:\n" \
+ " vcmts_replicas: 16\n" \
+ " vcmts_pods:\n" \
+ " - service_group_config:\n" \
+ " sg_id: 0\n" \
+ " net_us: 18:02.0\n" \
+ " net_ds: 18:02.1\n" \
+ " num_ofdm: 4\n" \
+ " num_subs: 300\n" \
+ " cm_crypto: aes\n" \
+ " qat: qat_off\n" \
+ " power_mgmt: pm_on\n" \
+ " cpu_socket_id: 0\n" \
+ " ds_core_type: exclusive\n" \
+ " ds_core_pool_index: 0\n" \
+ " vcmtsd_image: vcmts-d:feat"
+
+ VCMTSD_VALUES_INCOMPLETE = \
+ "serviceAccount: cmk-serviceaccount\n" \
+ "topology:\n" \
+ " vcmts_replicas: 16"
+
+ def setUp(self):
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ self.vnf = vcmts_vnf.VcmtsVNF(NAME, vnfd)
+
+ def test___init__(self, *args):
+ self.assertIsNotNone(self.vnf.setup_helper)
+
+ def test_extract_pod_cfg(self):
+ pod_cfg = self.vnf.extract_pod_cfg(self.POD_CFG, "0")
+ self.assertIsNotNone(pod_cfg)
+ self.assertEqual(pod_cfg['sg_id'], '0')
+ pod_cfg = self.vnf.extract_pod_cfg(self.POD_CFG, "1")
+ self.assertIsNone(pod_cfg)
+
+ def test_instantiate_missing_influxdb_info(self):
+ err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG)
+ err_scenario_cfg['options'].pop('vcmts_influxdb_ip', None)
+ with self.assertRaises(KeyError):
+ self.vnf.instantiate(err_scenario_cfg, self.CONTEXT_CFG)
+
+ def test_instantiate_missing_vcmtsd_values_file(self):
+ if os.path.isfile(self.VCMTSD_VALUES_PATH):
+ os.remove(self.VCMTSD_VALUES_PATH)
+ err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG)
+ err_scenario_cfg['options']['vcmtsd_values'] = self.VCMTSD_VALUES_PATH
+ with self.assertRaises(RuntimeError):
+ self.vnf.instantiate(err_scenario_cfg, self.CONTEXT_CFG)
+
+ def test_instantiate_empty_vcmtsd_values_file(self):
+ yaml_sample = open(self.VCMTSD_VALUES_PATH, 'w')
+ yaml_sample.write("")
+ yaml_sample.close()
+
+ err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG)
+ err_scenario_cfg['options']['vcmtsd_values'] = self.VCMTSD_VALUES_PATH
+ with self.assertRaises(RuntimeError):
+ self.vnf.instantiate(err_scenario_cfg, self.CONTEXT_CFG)
+
+ if os.path.isfile(self.VCMTSD_VALUES_PATH):
+ os.remove(self.VCMTSD_VALUES_PATH)
+
+ def test_instantiate_missing_vcmtsd_values_key(self):
+ err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG)
+ err_scenario_cfg['options'].pop('vcmtsd_values', None)
+ with self.assertRaises(KeyError):
+ self.vnf.instantiate(err_scenario_cfg, self.CONTEXT_CFG)
+
+ def test_instantiate_invalid_vcmtsd_values(self):
+ yaml_sample = open(self.VCMTSD_VALUES_PATH, 'w')
+ yaml_sample.write(self.VCMTSD_VALUES_INCOMPLETE)
+ yaml_sample.close()
+
+ err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG)
+ with self.assertRaises(KeyError):
+ self.vnf.instantiate(err_scenario_cfg, self.CONTEXT_CFG)
+
+ if os.path.isfile(self.VCMTSD_VALUES_PATH):
+ os.remove(self.VCMTSD_VALUES_PATH)
+
+ def test_instantiate_invalid_sg_id(self):
+ yaml_sample = open(self.VCMTSD_VALUES_PATH, 'w')
+ yaml_sample.write(self.VCMTSD_VALUES)
+ yaml_sample.close()
+
+ err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG)
+ err_scenario_cfg['options'][NAME]['sg_id'] = 8
+ with self.assertRaises(exceptions.IncorrectConfig):
+ self.vnf.instantiate(err_scenario_cfg, self.CONTEXT_CFG)
+
+ if os.path.isfile(self.VCMTSD_VALUES_PATH):
+ os.remove(self.VCMTSD_VALUES_PATH)
+
+ @mock.patch('yardstick.network_services.vnf_generic.vnf.vcmts_vnf.VnfSshHelper')
+ def test_instantiate_all_valid(self, ssh, *args):
+ mock_ssh(ssh)
+
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ vnf = vcmts_vnf.VcmtsVNF(NAME, vnfd)
+
+ yaml_sample = open(self.VCMTSD_VALUES_PATH, 'w')
+ yaml_sample.write(self.VCMTSD_VALUES)
+ yaml_sample.close()
+
+ vnf.instantiate(self.SCENARIO_CFG, self.CONTEXT_CFG)
+ self.assertEqual(vnf.vcmts_influxdb_ip, "10.80.5.150")
+ self.assertEqual(vnf.vcmts_influxdb_port, 8086)
+
+ if os.path.isfile(self.VCMTSD_VALUES_PATH):
+ os.remove(self.VCMTSD_VALUES_PATH)
+
+ def test__update_collectd_options(self):
+ scenario_cfg = {'options':
+ {'collectd':
+ {'interval': 3,
+ 'plugins':
+ {'plugin3': {'param': 3}}},
+ 'vnf__0':
+ {'collectd':
+ {'interval': 2,
+ 'plugins':
+ {'plugin3': {'param': 2},
+ 'plugin2': {'param': 2}}}}}}
+ context_cfg = {'nodes':
+ {'vnf__0':
+ {'collectd':
+ {'interval': 1,
+ 'plugins':
+ {'plugin3': {'param': 1},
+ 'plugin2': {'param': 1},
+ 'plugin1': {'param': 1}}}}}}
+ expected = {'interval': 1,
+ 'plugins':
+ {'plugin3': {'param': 1},
+ 'plugin2': {'param': 1},
+ 'plugin1': {'param': 1}}}
+
+ self.vnf._update_collectd_options(scenario_cfg, context_cfg)
+ self.assertEqual(self.vnf.setup_helper.collectd_options, expected)
+
+ def test__update_options(self):
+ options1 = {'interval': 1,
+ 'param1': 'value1',
+ 'plugins':
+ {'plugin3': {'param': 3},
+ 'plugin2': {'param': 1},
+ 'plugin1': {'param': 1}}}
+ options2 = {'interval': 2,
+ 'param2': 'value2',
+ 'plugins':
+ {'plugin4': {'param': 4},
+ 'plugin2': {'param': 2},
+ 'plugin1': {'param': 2}}}
+ expected = {'interval': 1,
+ 'param1': 'value1',
+ 'param2': 'value2',
+ 'plugins':
+ {'plugin4': {'param': 4},
+ 'plugin3': {'param': 3},
+ 'plugin2': {'param': 1},
+ 'plugin1': {'param': 1}}}
+
+ vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ vnf = vcmts_vnf.VcmtsVNF('vnf1', vnfd)
+ vnf._update_options(options2, options1)
+ self.assertEqual(options2, expected)
+
+ def test_wait_for_instantiate(self):
+ self.assertIsNone(self.vnf.wait_for_instantiate())
+
+ def test_terminate(self):
+ self.assertIsNone(self.vnf.terminate())
+
+ def test_scale(self):
+ self.assertIsNone(self.vnf.scale())
+
+ def test_collect_kpi(self):
+ self.vnf.influxdb_helper = mock.MagicMock()
+ self.vnf.collect_kpi()
+ self.vnf.influxdb_helper.copy_kpi.assert_called_once()
+
+ def test_start_collect(self):
+ self.vnf.vcmts_influxdb_ip = "localhost"
+ self.vnf.vcmts_influxdb_port = 8800
+
+ self.assertIsNone(self.vnf.start_collect())
+ self.assertIsNotNone(self.vnf.influxdb_helper)
+
+ def test_stop_collect(self):
+ self.assertIsNone(self.vnf.stop_collect())