aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--contrib/nettest/Dockerfile47
-rw-r--r--contrib/nettest/README.md0
-rw-r--r--contrib/nettest/nettest/heat_2stcv.yaml170
-rw-r--r--contrib/nettest/nettest/nettest.py157
-rw-r--r--contrib/nettest/nettest/requirements.txt9
-rw-r--r--contrib/nettest/nettest/rest_server.py351
-rw-r--r--contrib/nettest/nettest/rfc2544test.py596
-rw-r--r--contrib/nettest/nettest/start.sh11
-rw-r--r--contrib/nettest/nettest/stcv_stack.py165
-rw-r--r--contrib/nettest_client/nettest_client.py190
-rw-r--r--docs/testing/developer/devguide/index.rst1
-rw-r--r--docs/testing/developer/devguide/web.rst100
-rw-r--r--docs/testing/user/configguide/index.rst1
-rw-r--r--docs/testing/user/configguide/web.rst74
-rw-r--r--docs/testing/user/userguide/index.rst3
-rw-r--r--docs/testing/user/userguide/network.rst115
-rw-r--r--docs/testing/user/userguide/network_testcase_description.rst127
-rw-r--r--docs/testing/user/userguide/web.rst70
-rw-r--r--legacy/DO-NOT-DELETE2
-rw-r--r--legacy/__init__.py8
-rw-r--r--legacy/assets/perftest/common/git_proxy_pbook.yaml19
-rw-r--r--legacy/assets/perftest/common/sys_proxy_pbook.yaml61
-rw-r--r--legacy/assets/perftest/etc/fio_test_job13
-rw-r--r--legacy/assets/perftest/etc/info_collect.py94
-rw-r--r--legacy/assets/perftest/fio.yaml120
-rw-r--r--legacy/assets/perftest/iperf.yaml170
-rw-r--r--legacy/assets/perftest/summary23
-rw-r--r--legacy/assets/testplan/default/network/iperf_bm.yaml58
-rw-r--r--legacy/assets/testplan/default/network/iperf_vm.yaml51
-rw-r--r--legacy/assets/testplan/default/network/iperf_vm_2.yaml52
-rw-r--r--legacy/assets/testplan/default/storage/fio_bm.yaml47
-rw-r--r--legacy/assets/testplan/default/storage/fio_vm.yaml52
-rw-r--r--legacy/docs/_01-compute.rst104
-rw-r--r--legacy/docs/_02-network.rst61
-rw-r--r--legacy/docs/_03-storage.rst31
-rw-r--r--legacy/docs/annex.rst18
-rw-r--r--legacy/docs/apidocs/qtip_restful_api.rst10
-rw-r--r--legacy/docs/benchmark-suites.rst15
-rw-r--r--legacy/docs/download/sample_config.yaml58
-rw-r--r--legacy/docs/index.rst13
-rw-r--r--legacy/docs/introduction.rst381
-rw-r--r--legacy/docs/overview/index.rst14
-rw-r--r--legacy/docs/overview/overview.rst21
-rw-r--r--legacy/driver/playbook/bwn_ng.yaml25
-rw-r--r--legacy/driver/playbook/top.yaml12
-rw-r--r--legacy/scripts/__init__.py0
-rw-r--r--legacy/scripts/ref_results/__init__.py0
-rw-r--r--legacy/scripts/ref_results/compute_benchmarks_indices.py168
-rw-r--r--legacy/scripts/ref_results/index_calculation.py49
-rw-r--r--legacy/scripts/ref_results/network_benchmarks_indices.py28
-rw-r--r--legacy/scripts/ref_results/reference.json97
-rw-r--r--legacy/scripts/ref_results/result_accum.py39
-rw-r--r--legacy/scripts/ref_results/storage_benchmarks_indices.py37
-rw-r--r--legacy/scripts/ref_results/suite_result.py58
-rw-r--r--tests/ci/network/docker-compose.yaml27
55 files changed, 4223 insertions, 0 deletions
diff --git a/contrib/nettest/Dockerfile b/contrib/nettest/Dockerfile
new file mode 100644
index 00000000..272569d6
--- /dev/null
+++ b/contrib/nettest/Dockerfile
@@ -0,0 +1,47 @@
+##########################################################
+# Dockerfile to run a flask-based web application# Based on an ubuntu:16.04
+##########################################################
+
+# Set the base image to use to centos
+FROM ubuntu:16.04
+
+# Set the file maintainer
+MAINTAINER Qiang.Dai@spirent.com
+LABEL version="0.1" description="Spirent networking test Docker container"
+
+# Set env varibles used in this Dockerfile (add a unique prefix, such as DOCKYARD)
+# Local directory with project source
+ENV DOCKYARD_SRC=nettest \
+ DOCKYARD_SRCHOME=/opt \
+ DOCKYARD_SRCPROJ=/opt/nettest
+
+# Update the defualt application repository source list
+RUN apt-get update && apt-get install -y \
+ gcc \
+ python-dev \
+ python-pip \
+ python-setuptools \
+ --no-install-recommends \
+ && rm -rf /var/lib/apt/lists/*
+
+# Copy application source code to SRCDIR
+COPY $DOCKYARD_SRC $DOCKYARD_SRCPROJ
+
+# Create application subdirectories
+WORKDIR $DOCKYARD_SRCPROJ
+RUN mkdir -p log
+VOLUME ["$DOCKYARD_SRCPROJ/log/"]
+
+# Install Python dependencies
+RUN pip install -U pip \
+ && pip install -U setuptools \
+ && pip install -r $DOCKYARD_SRCPROJ/requirements.txt
+
+# Port to expose
+EXPOSE 5000
+
+# Copy entrypoint script into the image
+WORKDIR $DOCKYARD_SRCPROJ
+
+#CMD ["/bin/bash"]
+CMD ["/bin/bash", "start.sh"]
diff --git a/contrib/nettest/README.md b/contrib/nettest/README.md
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/contrib/nettest/README.md
diff --git a/contrib/nettest/nettest/heat_2stcv.yaml b/contrib/nettest/nettest/heat_2stcv.yaml
new file mode 100644
index 00000000..77c6e6e8
--- /dev/null
+++ b/contrib/nettest/nettest/heat_2stcv.yaml
@@ -0,0 +1,170 @@
+##############################################################################
+# Copyright (c) 2018 Spirent Communications and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+heat_template_version: 2016-10-14
+
+description: Template for deploying 2 STCv and 1 labserver
+
+parameters:
+ public_net_name: {default: external, description: Public network to allocate floating IPs to VMs', type: string}
+ #public_net_id: {description: public_network id for exernal connectivity,type: string}
+ mgmt_net_name: {default: admin, description: Name of STCv mgmt network to be created, type: string}
+ mgmt_net_cidr: {default: 10.10.10.0/24, description: STCv mgmt network CIDR,type: string}
+ mgmt_net_gw: {default: 10.10.10.1, description: STCv mgmt network gateway address, type: string}
+ mgmt_net_pool_start: {default: 10.10.10.10, description: Start of mgmt network IP address allocation pool, type: string}
+ mgmt_net_pool_end: {default: 10.10.10.20, description: End of mgmt network IP address allocation pool, type: string}
+ tst_net_name: {default: tst, description: Name of STCv private network to be created, type: string}
+ tst_net_cidr: {default: 192.168.1.0/24, description: STCv private network CIDR,type: string}
+ tst_net_gw: {default: 192.168.1.1, description: STCv private network gateway address, type: string}
+ tst_net_pool_start: {default: 192.168.1.10, description: Start of private network IP address allocation pool, type: string}
+ tst_net_pool_end: {default: 192.168.1.20, description: End of private network IP address allocation pool, type: string}
+ stcv_image: {default: "stcv-4.79", description: Image name to use for STCv, type: string}
+ stcv_flavor: {default: "m1.tiny", description: Flavor to use for STCv, type: string}
+ #stcv_user_data: {default: "", description: user data such as ntp server ip for stcv, type: string}
+ #stcv_config_file: {default: "stcv_config_file", description: user data such as ntp server ip for stcv, type: string}
+ ntp_server_ip: {default: "", description: user data such as ntp server ip for stcv, type: string}
+ stcv_sg_name: {default: stcv_sg, description: server group name, type: string}
+ stcv_sg_affinity: {default: affinity, description: server group affinity for stcv, type: string}
+
+resources:
+ stcv_server_group:
+ type: OS::Nova::ServerGroup
+ properties:
+ name: {get_param: stcv_sg_name}
+ policies: [{get_param: stcv_sg_affinity}]
+ mgmt_net:
+ type: OS::Neutron::Net
+ properties:
+ name: {get_param: mgmt_net_name}
+ mgmt_net_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ allocation_pools:
+ - end: {get_param: mgmt_net_pool_end}
+ start: {get_param: mgmt_net_pool_start}
+ cidr: {get_param: mgmt_net_cidr}
+ gateway_ip: {get_param: mgmt_net_gw}
+ network: {get_resource: mgmt_net}
+ public_router:
+ type: OS::Neutron::Router
+ properties:
+ external_gateway_info:
+ network: {get_param: public_net_name}
+ router_interface:
+ type: OS::Neutron::RouterInterface
+ properties:
+ router: {get_resource: public_router}
+ subnet: {get_resource: mgmt_net_subnet}
+ tst_net:
+ type: OS::Neutron::Net
+ properties:
+ name: {get_param: tst_net_name}
+ tst_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ allocation_pools:
+ - end: {get_param: tst_net_pool_end}
+ start: {get_param: tst_net_pool_start}
+ cidr: {get_param: tst_net_cidr}
+ gateway_ip: {get_param: tst_net_gw}
+ network: {get_resource: tst_net}
+ stcv_1_port_1:
+ type: OS::Neutron::Port
+ properties:
+ network: {get_resource: mgmt_net}
+ fixed_ips:
+ - subnet: {get_resource: mgmt_net_subnet}
+ floating_ip1:
+ type: OS::Neutron::FloatingIP
+ properties:
+ floating_network: {get_param: public_net_name}
+ port_id: {get_resource: stcv_1_port_1}
+ stcv_1_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network: {get_resource: tst_net}
+ port_security_enabled: False
+ fixed_ips:
+ - subnet: {get_resource: tst_subnet}
+ STCv_1:
+ type: OS::Nova::Server
+ properties:
+ #availability_zone : {get_param: availability_zone_name}
+ flavor: {get_param: stcv_flavor}
+ image: {get_param: stcv_image}
+ name: STCv_1
+ user_data:
+ str_replace:
+ template: |
+ #cloud-config
+ spirent:
+ ntp: $ntp_server_ip
+ params:
+ $ntp_server_ip: {get_param: ntp_server_ip}
+ user_data_format: RAW
+ config_drive: True
+ scheduler_hints:
+ group: {get_resource: stcv_server_group}
+ networks:
+ - port: {get_resource: stcv_1_port_1}
+ - port: {get_resource: stcv_1_port_2}
+ stcv_2_port_1:
+ type: OS::Neutron::Port
+ properties:
+ network: {get_resource: mgmt_net}
+ fixed_ips:
+ - subnet: {get_resource: mgmt_net_subnet}
+ floating_ip2:
+ type: OS::Neutron::FloatingIP
+ properties:
+ floating_network: {get_param: public_net_name}
+ port_id: {get_resource: stcv_2_port_1}
+ stcv_2_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network: {get_resource: tst_net}
+ port_security_enabled: False
+ fixed_ips:
+ - subnet: {get_resource: tst_subnet}
+ STCv_2:
+ type: OS::Nova::Server
+ properties:
+ #availability_zone : {get_param: availability_zone_name}
+ flavor: {get_param: stcv_flavor}
+ image: {get_param: stcv_image}
+ name: STCv_2
+ user_data:
+ str_replace:
+ template: |
+ #cloud-config
+ spirent:
+ ntp: $ntp_server_ip
+ params:
+ $ntp_server_ip: {get_param: ntp_server_ip}
+ user_data_format: RAW
+ config_drive: True
+ scheduler_hints:
+ group: {get_resource: stcv_server_group}
+ networks:
+ - port: {get_resource: stcv_2_port_1}
+ - port: {get_resource: stcv_2_port_2}
+outputs:
+ STCv_1_Mgmt_Ip:
+ value: {get_attr: [floating_ip1, floating_ip_address]}
+ description: STCv_1 Mgmt IP
+ STCv_2_Mgmt_Ip:
+ value: {get_attr: [floating_ip2, floating_ip_address]}
+ description: STCv_2 Mgmt IP
+ STCv_1_Tst_Ip:
+ value: {get_attr: [stcv_1_port_2, fixed_ips]}
+ description: STCv_1 Tst IP
+ STCv_2_Tst_Ip:
+ value: {get_attr: [stcv_2_port_2, fixed_ips]}
+ description: STCv_2 Tst IP
+
diff --git a/contrib/nettest/nettest/nettest.py b/contrib/nettest/nettest/nettest.py
new file mode 100644
index 00000000..c5a203e0
--- /dev/null
+++ b/contrib/nettest/nettest/nettest.py
@@ -0,0 +1,157 @@
+##############################################################################
+# Copyright (c) 2018 Spirent Communications and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+import logging
+from time import sleep
+
+from rfc2544test import StcRfc2544Test
+from stcv_stack import StcvStack
+
+
+class NetTestMaster(object):
+
+ def __init__(self):
+ self.logger = logging.getLogger(__name__)
+
+ self.stacks = []
+ self.testcases = []
+
+ self.stack_created = False
+ self.status_reason = ''
+
+ def get_stack_by_id(self, id):
+ for stack in self.stacks:
+ if id == stack.stack_id:
+ return stack
+ return None
+
+ def get_stack_by_name(self, name):
+ for stack in self.stacks:
+ if name == stack.name:
+ return stack
+ return None
+
+ def create_stack(self, name, stack_type, pub_net_name, **kwargs):
+ if stack_type != 'stcv':
+ raise Exception('only support stcv stack type currently')
+
+ try:
+ stack = StcvStack(name=name,
+ pub_net_name=pub_net_name,
+ ntp_server_ip=kwargs.get('license_server_ip'),
+ lab_server_ip=kwargs.get('lab_server_ip'),
+ stcv_image=kwargs.get('stcv_image'),
+ stcv_flavor=kwargs.get('stcv_flavor'),
+ stcv_affinity=kwargs.get('stcv_affinity'))
+ stack.create_stack()
+ self.stacks.append(stack)
+
+ except Exception as err:
+ self.logger.error('create stack fail. err = %s', str(err))
+ raise err
+
+ return stack
+
+ def delete_stack(self, stack_id):
+ stack = self.get_stack_by_id(stack_id)
+ if stack is None:
+ raise Exception('stack does not exist, stack_id = %s', stack_id)
+
+ self.stacks.remove(stack)
+ stack.delete_stack()
+
+ def get_tc_result(self, tc_id):
+ tc = self.get_tc_by_id(tc_id)
+ return tc.get_result()
+
+ def get_tc_status(self, tc_id):
+ tc = self.get_tc_by_id(tc_id)
+ return tc.get_status()
+
+ def execute_testcase(self, name, category, stack_id, **kwargs):
+ if category != 'rfc2544':
+ raise Exception("currently only support rfc2544 test")
+
+ stack = self.get_stack_by_id(stack_id)
+ if stack is None:
+ raise Exception("defined stack not exist, stack_id = %s", stack_id)
+
+ tc = StcRfc2544Test(name=name,
+ lab_server_ip=stack.lab_server_ip,
+ license_server_ip=stack.ntp_server_ip,
+ west_stcv_admin_ip=stack.get_west_stcv_ip(),
+ west_stcv_tst_ip=stack.get_west_stcv_tst_ip(),
+ east_stcv_admin_ip=stack.get_east_stcv_ip(),
+ east_stcv_tst_ip=stack.get_east_stcv_tst_ip(),
+ stack_id=stack_id,
+ **kwargs)
+ self.testcases.append(tc)
+ tc.execute()
+
+ return tc.tc_id
+
+ def get_tc_by_id(self, id):
+ for tc in self.testcases:
+ if id == tc.tc_id:
+ return tc
+ return None
+
+ def delete_testcase(self, tc_id):
+ tc = self.get_tc_by_id(tc_id)
+
+ if tc.status == 'finished':
+ tc.delete_result()
+
+ if tc.status == 'running':
+ tc.cancel_run()
+
+ self.testcases.remove(tc)
+
+
+if __name__ == "__main__":
+ try:
+ nettest = NetTestMaster()
+ stack_params = {
+ "stcv_affinity": True,
+ "stcv_image": "stcv-4.79",
+ "stcv_flavor": "m1.tiny",
+ "lab_server_ip": "192.168.37.122",
+ "license_server_ip": "192.168.37.251"
+ }
+
+ stack = nettest.create_stack(name='stack1',
+ stack_type='stcv',
+ pub_net_name='external',
+ **stack_params)
+ tc_params = {
+ 'metric': 'throughput',
+ 'framesizes': [64, 128]
+ }
+ tc = nettest.execute_testcase(name='tc1',
+ category='rfc2544',
+ stack_id=stack.stack_id,
+ **tc_params)
+
+ print "test case id is %s" % tc.id
+
+ status = tc.get_status()
+ while (status != tc.TC_STATUS_FINISHED):
+ if status == tc.TC_STATUS_ERROR:
+ print "tc exectue fail, reason %s" % tc.get_err_reason()
+ break
+ sleep(2)
+ if status == tc.TC_STATUS_FINISHED:
+ print tc.get_result()
+
+ nettest.delete_testcase(tc.id)
+
+ nettest.delete_stack(stack.stack_id)
+
+ except Exception as err:
+ print err
diff --git a/contrib/nettest/nettest/requirements.txt b/contrib/nettest/nettest/requirements.txt
new file mode 100644
index 00000000..3efb124b
--- /dev/null
+++ b/contrib/nettest/nettest/requirements.txt
@@ -0,0 +1,9 @@
+flask
+flask_cors
+flask_restful
+flask_restful_swagger
+#openstacksdk
+keystoneauth1
+python-heatclient
+stcrestclient
+
diff --git a/contrib/nettest/nettest/rest_server.py b/contrib/nettest/nettest/rest_server.py
new file mode 100644
index 00000000..3558b9ac
--- /dev/null
+++ b/contrib/nettest/nettest/rest_server.py
@@ -0,0 +1,351 @@
+##############################################################################
+# Copyright (c) 2018 Spirent Communications and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+import os
+import logging
+
+from flask import Flask, abort, jsonify, request, send_from_directory
+from flask_cors import CORS
+from flask_restful import Api, Resource, fields
+from flask_restful_swagger import swagger
+
+from nettest import NetTestMaster
+
+app = Flask(__name__)
+CORS(app)
+api = swagger.docs(Api(app), apiVersion="1.0")
+
+stcv_master = NetTestMaster()
+
+
+@app.route("/tc_results/<tc_id>", methods=["GET"])
+def download_result_file(tc_id):
+ directory = os.getcwd() + "/tc_results/rfc2544/" + tc_id
+ files = os.listdir(directory)
+ return send_from_directory(directory, files[0], as_attachment=True)
+
+
+@swagger.model
+class StackRequestModel:
+ resource_fields = {
+ 'stack_name': fields.String,
+ 'stack_type': fields.String,
+ 'public_network': fields.String,
+ "stack_params": fields.Nested,
+ }
+
+
+@swagger.model
+class StackResponseModel:
+ resource_fields = {
+ 'stack_name': fields.String,
+ 'stack_created': fields.Boolean,
+ "stack_id": fields.String
+ }
+
+
+class Stack(Resource):
+ def __init__(self):
+ self.logger = logging.getLogger(__name__)
+
+ @swagger.operation(
+ notes='Fetch the stack configuration',
+ parameters=[
+ {
+ "name": "id",
+ "description": "The UUID of the stack in the format "
+ "NNNNNNNN-NNNN-NNNN-NNNN-NNNNNNNNNNNN",
+ "required": True,
+ "type": "string",
+ "allowMultiple": False,
+ "paramType": "query"
+ },
+ ],
+ type=StackResponseModel.__name__
+ )
+ def get(self):
+ stack_id = request.args.get('id')
+ stack = stcv_master.get_stack_by_id(stack_id)
+
+ if not stack:
+ abort(404)
+
+ return jsonify({
+ 'stack_name': stack.name,
+ 'stack_created': True,
+ "stack_id": stack_id})
+
+ @swagger.operation(
+ notes='''set the current agent configuration and create a stack in
+ the controller. Returns once the stack create is completed.''',
+ parameters=[
+ {
+ "name": "stack",
+ "description": '''Configuration to be set. All parameters are
+ necessory.
+ ''',
+ "required": True,
+ "type": "StackRequestModel",
+ "paramType": "body"
+ }
+ ],
+ type=StackResponseModel.__name__
+ )
+ def post(self):
+ if not request.json:
+ abort(400, "ERROR: No data specified")
+
+ self.logger.info(request.json)
+
+ try:
+ params = {
+ 'lab_server_ip': request.json['stack_params'].get('lab_server_ip'),
+ 'license_server_ip': request.json['stack_params'].get('license_server_ip'),
+ 'stcv_image': request.json['stack_params'].get('stcv_image'),
+ 'stcv_flavor': request.json['stack_params'].get('stcv_flavor'),
+ 'stcv_affinity': request.json['stack_params'].get('stcv_affinity')
+ }
+
+ stack = stcv_master.create_stack(name=request.json['stack_name'],
+ stack_type=request.json['stack_type'],
+ pub_net_name=request.json['public_network'],
+ **params)
+ if stack is None:
+ abort(400, "ERROR: create stack fail")
+
+ return jsonify({'stack_name': request.json['stack_name'],
+ 'stack_created': True,
+ 'stack_id': stack.stack_id})
+
+ except Exception as e:
+ abort(400, str(e))
+
+ @swagger.operation(
+ notes='delete deployed stack',
+ parameters=[
+ {
+ "name": "id",
+ "description": "The UUID of the stack in the format "
+ "NNNNNNNN-NNNN-NNNN-NNNN-NNNNNNNNNNNN",
+ "required": True,
+ "type": "string",
+ "allowMultiple": False,
+ "paramType": "query"
+ },
+ ],
+ responseMessages=[
+ {
+ "code": 200,
+ "message": "Stack ID found, response in JSON format"
+ },
+ {
+ "code": 404,
+ "message": "Stack ID not found"
+ }
+ ]
+ )
+ def delete(self):
+ try:
+ stack_id = request.args.get('id')
+ stcv_master.delete_stack(stack_id)
+ except Exception as e:
+ abort(400, str(e))
+
+
+@swagger.model
+class TestcaseRequestModel:
+ resource_fields = {
+ 'name': fields.String,
+ 'category': fields.String,
+ 'stack_id': fields.String,
+ 'params': fields.Nested
+ }
+
+
+@swagger.model
+class TestcaseResponseModel:
+ resource_fields = {
+ 'name': fields.String,
+ 'category': fields.String,
+ 'stack_id': fields.String,
+ 'tc_id': fields.String
+ }
+
+
+class TestCase(Resource):
+
+ """TestCase API"""
+
+ def __init__(self):
+ self.logger = logging.getLogger(__name__)
+
+ @swagger.operation(
+ notes='Fetch the metrics of the specified testcase',
+ parameters=[
+ {
+ "name": "id",
+ "description": "The UUID of the testcase in the format "
+ "NNNNNNNN-NNNN-NNNN-NNNN-NNNNNNNNNNNN",
+ "required": True,
+ "type": "string",
+ "allowMultiple": False,
+ "paramType": "query"
+ },
+ {
+ "name": "type",
+ "description": "The type of metrics to report. May be "
+ "metrics (default), metadata, or status",
+ "required": True,
+ "type": "string",
+ "allowMultiple": False,
+ "paramType": "query"
+ }
+ ],
+ responseMessages=[
+ {
+ "code": 200,
+ "message": "Workload ID found, response in JSON format"
+ },
+ {
+ "code": 404,
+ "message": "Workload ID not found"
+ }
+ ]
+ )
+ def get(self):
+ tc_id = request.args.get('id')
+ query_type = request.args.get('type')
+ ret = {}
+
+ try:
+ tc = stcv_master.get_tc_by_id(tc_id)
+ if query_type == "result":
+ ret = tc.get_result()
+
+ if query_type == "status":
+ status = tc.get_status()
+ ret['status'] = status
+ if 'error' == status:
+ reason = tc.get_err_reason()
+ ret['reason'] = reason
+
+ return jsonify(ret)
+
+ except Exception as err:
+ abort(400, str(err))
+
+ @swagger.operation(
+ parameters=[
+ {
+ "name": "body",
+ "description": """Start execution of a testcase with the
+parameters, only support rfc25cc test
+ """,
+ "required": True,
+ "type": "TestcaseRequestModel",
+ "paramType": "body"
+ }
+ ],
+ type=TestcaseResponseModel.__name__,
+ responseMessages=[
+ {
+ "code": 200,
+ "message": "TestCase submitted"
+ },
+ {
+ "code": 400,
+ "message": "Missing configuration data"
+ }
+ ]
+ )
+ def post(self):
+ if not request.json:
+ abort(400, "ERROR: Missing configuration data")
+
+ self.logger.info(request.json)
+
+ try:
+ name = request.json['name']
+ category = request.json['category']
+ stack_id = request.json['stack_id']
+ tc_id = stcv_master.execute_testcase(name=request.json['name'],
+ category=request.json['category'],
+ stack_id=request.json['stack_id'],
+ **request.json['params'])
+
+ return jsonify({'name': name,
+ 'category': category,
+ 'stack_id': stack_id,
+ 'tc_id': tc_id})
+
+ except Exception as e:
+ abort(400, str(e))
+
+ @swagger.operation(
+ notes='Cancels the currently running testcase or delete testcase result',
+ parameters=[
+ {
+ "name": "id",
+ "description": "The UUID of the testcase in the format "
+ "NNNNNNNN-NNNN-NNNN-NNNN-NNNNNNNNNNNN",
+ "required": True,
+ "type": "string",
+ "allowMultiple": False,
+ "paramType": "query"
+ },
+ ],
+ responseMessages=[
+ {
+ "code": 200,
+ "message": "Wordload ID found, response in JSON format"
+ },
+ ]
+ )
+ def delete(self):
+ try:
+ tc_id = request.args.get("id")
+ self.logger.info("receive delete testcase msg. tc_id = %s", tc_id)
+
+ stcv_master.delete_testcase(tc_id)
+
+ except Exception as e:
+ abort(400, str(e))
+
+
+api.add_resource(Stack, "/api/v1.0/stack")
+api.add_resource(TestCase, "/api/v1.0/testcase")
+
+'''
+@app.route("/")
+def hello_world():
+ return 'hello world'
+
+@app.route("/testcases")
+def get_testcases():
+ return []
+
+
+@app.route("/testcases/<int: tc_id>")
+def query_testcase(tc_id):
+ return []
+
+@app.route("/stctest/api/v1.0/testcase/<string: tc_name>", methods = ['GET'])
+def query_tc_result(tc_name):
+ return []
+
+@app.route("/stctest/api/v1.0/testcase", methods = ['POST'])
+def execut_testcase():
+ return []
+'''
+
+
+if __name__ == "__main__":
+ logger = logging.getLogger("nettest").setLevel(logging.DEBUG)
+
+ app.run(host="0.0.0.0", debug=True, threaded=True)
diff --git a/contrib/nettest/nettest/rfc2544test.py b/contrib/nettest/nettest/rfc2544test.py
new file mode 100644
index 00000000..e8c9cd64
--- /dev/null
+++ b/contrib/nettest/nettest/rfc2544test.py
@@ -0,0 +1,596 @@
+##############################################################################
+# Copyright (c) 2018 Spirent Communications and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+import base64
+import copy
+import logging
+import os
+import shutil
+import threading
+from time import sleep
+import uuid
+
+import requests
+from stcrestclient import stchttp
+
+
+class Stcv2Net1Stack(object):
+ ADMIN_NETWORK_NAME = "admin"
+ ADMIN_SUBNET_ADDR = "50.50.50.0/24"
+ ADMIN_GW_IP = "50.50.50.1"
+ TST_NETWORK_NAME = "tst"
+ TST_SUBNET_ADDR = "192.168.0.0/24"
+ TST_GW_IP = "192.168.0.1"
+ ROUTER_NAME = "router"
+ WEST_STCV_NAME = "west_stcv"
+ EAST_STCV_NAME = "east_stcv"
+ AFFINITY_SG_NAME = "affinity"
+ STCV_USER_DATA = '''#cloud-config
+spirent:
+ ntp: '''
+
+ def __init__(self, name, conn, ext_network_name, params):
+ self.logger = logging.getLogger(__name__)
+
+ self.name = name
+ self.conn = conn
+ self.ext_network_name = ext_network_name
+ self.image_name = params['stcv_image']
+ self.flavor_name = params['stcv_flavor']
+ self.ntp_server_ip = params['license_server_ip']
+ self.affinity = params['stcv_affinity']
+
+ self.stack_id = str(uuid.uuid4())
+ self.admin_network = None
+ self.admin_subnet = None
+ self.tst_network = None
+ self.tst_subnet = None
+ self.ext_network = None
+ self.router = None
+ self.affinity_sg = None
+
+ self.west_stcv = None
+ self.west_stcv_ip = ''
+ self.east_stcv = None
+ self.east_stcv_ip = ''
+
+ def _deploy_test_network(self):
+
+ # create tst network and subnet
+ self.tst_network = self.conn.network.create_network(
+ name=self.TST_NETWORK_NAME)
+ self.tst_subnet = self.conn.network.create_subnet(
+ name=self.TST_NETWORK_NAME + '_subnet',
+ network_id=self.tst_network.id,
+ ip_version='4',
+ cidr=self.TST_SUBNET_ADDR,
+ gateway_ip=self.TST_GW_IP,
+ is_dhcp_enabled=True)
+
+ # create admin network and subnet
+ self.admin_network = self.conn.network.create_network(
+ name=self.ADMIN_NETWORK_NAME)
+ self.admin_subnet = self.conn.network.create_subnet(
+ name=self.ADMIN_NETWORK_NAME + '_subnet',
+ network_id=self.admin_network.id,
+ ip_version='4',
+ cidr=self.ADMIN_SUBNET_ADDR,
+ gateway_ip=self.ADMIN_GW_IP,
+ is_dhcp_enabled=True)
+
+ # create external gateway and connect admin subnet to router
+ self.ext_network = self.conn.network.find_network(self.ext_network_name)
+ self.router = self.conn.network.create_router(name=self.ROUTER_NAME,
+ external_gateway_info={"network_id": self.ext_network.id},
+ is_admin_state_up=True)
+ self.conn.network.add_interface_to_router(self.router, subnet_id=self.admin_subnet.id)
+
+ def _depoly_stcv(self, name, image_id, flavor_id, scheduler_hints, user_data):
+
+ stcv = self.conn.compute.create_server(
+ name=name, image_id=image_id, flavor_id=flavor_id,
+ networks=[{"uuid": self.admin_network.id}, {"uuid": self.tst_network.id}],
+ config_drive=True,
+ user_data=base64.encodestring(user_data)
+ )
+ stcv = self.conn.compute.wait_for_server(stcv)
+
+ stcv_fixed_ip = stcv.addresses[self.admin_network.name][0]['addr']
+ stcv_floating_ip = self.conn.network.create_ip(floating_network_id=self.ext_network.id)
+ self.conn.compute.add_floating_ip_to_server(server=stcv, address=stcv_floating_ip.floating_ip_address,
+ fixed_address=stcv_fixed_ip)
+
+ return {'stcv': stcv, 'fixed_ip': stcv_fixed_ip, 'floating_ip': stcv_floating_ip}
+
+ def create_stack(self):
+
+ image = self.conn.compute.find_image(self.image_name)
+ flavor = self.conn.compute.find_flavor(self.flavor_name)
+
+ if self.affinity:
+ self.affinity_sg = \
+ self.conn.compute.create_server_group(name=self.AFFINITY_SG_NAME,
+ policies=["affinity"])
+ else:
+ self.affinity_sg = \
+ self.conn.compute.create_server_group(name=self.AFFINITY_SG_NAME,
+ policies=["anti-affinity"])
+ self._deploy_test_network()
+
+ user_data = self.STCV_USER_DATA + self.ntp_server_ip
+
+ stcv = self._depoly_stcv(name=self.WEST_STCV_NAME,
+ image_id=image.id,
+ flavor_id=flavor.id,
+ scheduler_hints=self.affinity_sg,
+ user_data=user_data)
+ self.west_stcv = stcv['stcv']
+ self.west_stcv_ip = stcv['floating_ip']
+
+ stcv = self._depoly_stcv(name=self.EAST_STCV_NAME,
+ image_id=image.id,
+ flavor_id=flavor.id,
+ scheduler_hints=self.affinity_sg,
+ user_data=user_data)
+ self.east_stcv = stcv['stcv']
+ self.east_stcv_ip = stcv['floating_ip']
+
+ def delete_stack(self):
+
+ self.conn.compute.delete_server(self.west_stcv, ignore_missing=True)
+ self.conn.compute.delete_server(self.east_stcv, ignore_missing=True)
+
+ self.conn.compute.delete_server_group(server_group=self.affinity_sg,
+ ignore_missing=True)
+
+ # delete external gateway
+ self.conn.network.delete_router(self.router, ignore_missing=True)
+
+ # delete tst network
+ self.conn.network.delete_subnet(self.tst_subnet, ignore_missing=True)
+ self.conn.network.delete_network(self.tst_network, ignore_missing=True)
+
+ # delete admin network
+ self.conn.network.delete_subnet(self.admin_subnet, ignore_missing=True)
+ self.conn.network.delete_network(self.admin_network, ignore_missing=True)
+
+
+class StcSession:
+ """ wrapper class for stc session"""
+
+ def __init__(self, labserver_addr, user_name, session_name):
+ self.logger = logging.getLogger(__name__)
+
+ # create connection obj
+ self.stc = stchttp.StcHttp(labserver_addr)
+ self.user_name = user_name
+ self.session_name = session_name
+
+ # create session on labserver
+ self.session_id = self.stc.new_session(self.user_name, self.session_name)
+ self.stc.join_session(self.session_id)
+ return
+
+ def __del__(self):
+ # destroy resource on labserver
+ self.stc.end_session()
+
+ def clean_all_session(self):
+ session_urls = self.stc.session_urls()
+ for session in session_urls:
+ resp = requests.delete(session)
+ self.logger.info("delete session resp: %s", str(resp))
+ return
+
+
+class StcRfc2544Test:
+ """ RFC2544 test class"""
+
+ RESULT_PATH_PREFIX = './tc_results/rfc2544/'
+ TC_STATUS_INIT = 'init'
+ TC_STATUS_RUNNING = 'running'
+ TC_STATUS_FINISHED = 'finished'
+ TC_STATUS_ERROR = 'error'
+
+ throughput_additional_params = {
+ "AcceptableFrameLoss": 0.0,
+ "Duration": 30,
+ "FrameSizeList": 64,
+ "LearningMode": 'AUTO',
+ "NumOfTrials": 1,
+ # "RateInitial": 99.0,
+ # "RateLowerLimit": 99.0,
+ # "RateStep": 10.0,
+ # "RateUpperLimit": 99.0,
+ "Resolution": 1.0,
+ "SearchMode": 'BINARY',
+ "TrafficPattern": 'PAIR'
+ }
+
+ latency_additional_params = {
+ "Duration": 30,
+ "ExecuteSynchronous": True,
+ "FrameSizeList": 64,
+ "LearningMode": 'AUTO',
+ # "LoadType": 'STEP',
+ # "LoadStart": 10.0,
+ # "LoadEnd": 100.0,
+ # "LoadStep": 10.0,
+ "LoadList": [10, 20, 30, 40, 50, 60, 70, 80, 90, 100],
+ "LoadUnits": "PERCENT_LINE_RATE",
+ "NumOfTrials": 1,
+ "TrafficPattern": 'PAIR'
+ }
+
+ def __init__(self, name, lab_server_ip, license_server_ip,
+ west_stcv_admin_ip, west_stcv_tst_ip,
+ east_stcv_admin_ip, east_stcv_tst_ip,
+ stack_id=None, **kwargs):
+ self.logger = logging.getLogger(__name__)
+
+ self.name = name
+ self.lab_server_ip = lab_server_ip
+ self.license_server_ip = license_server_ip
+ self.west_stcv_ip = west_stcv_admin_ip
+ self.west_stcv_tst_ip = west_stcv_tst_ip
+ self.east_stcv_ip = east_stcv_admin_ip
+ self.east_stcv_tst_ip = east_stcv_tst_ip
+ self.stack_id = stack_id
+ self.metric = kwargs.get('metric')
+ if self.metric == 'throughput':
+ self.additional_params = copy.copy(self.throughput_additional_params)
+ elif self.metric == 'latency':
+ self.additional_params = copy.copy(self.latency_additional_params)
+ else:
+ raise Exception('invalid metric, metric = ' + self.metric)
+ self.additional_params['FrameSizeList'] = kwargs.get('framesizes')
+
+ self.tc_id = str(uuid.uuid4())
+
+ self.stc = None
+ self.sess = None
+ self.executor = None
+ self.status = 'init'
+ self.err_reason = ''
+
+ def config_license(self, license_server_addr):
+ license_mgr = self.stc.get("system1", "children-licenseservermanager")
+ self.stc.create("LicenseServer",
+ under=license_mgr,
+ attributes={"server": license_server_addr})
+ return
+
+ def create_project(self, traffic_custom=None):
+ self.project = self.stc.get("System1", "children-Project")
+ # Configure any custom traffic parameters
+ if traffic_custom == "cont":
+ self.stc.create("ContinuousTestConfig", under=self.project)
+ return
+
+ def config_test_port(self, chassis_addr, slot_no, port_no, intf_addr, gateway_addr):
+ # create test port
+ port_loc = "//%s/%s/%s" % (chassis_addr, slot_no, port_no)
+ chassis_port = self.stc.create('port', self.project)
+ self.stc.config(chassis_port, {'location': port_loc})
+
+ # Create emulated genparam for east port
+ device_gen_params = self.stc.create("EmulatedDeviceGenParams",
+ under=self.project,
+ attributes={"Port": chassis_port})
+ # Create the DeviceGenEthIIIfParams object
+ self.stc.create("DeviceGenEthIIIfParams",
+ under=device_gen_params,
+ attributes={"UseDefaultPhyMac": "True"})
+
+ # Configuring Ipv4 interfaces
+ self.stc.create("DeviceGenIpv4IfParams",
+ under=device_gen_params,
+ attributes={"Addr": intf_addr, "Gateway": gateway_addr})
+
+ # Create Devices using the Device Wizard
+ self.stc.perform("DeviceGenConfigExpand",
+ params={"DeleteExisting": "No", "GenParams": device_gen_params})
+
+ return
+
+ def do_test(self):
+ if self.metric == "throughput":
+ self.stc.perform("Rfc2544SetupThroughputTestCommand", self.additional_params)
+ elif self.metric == "backtoback":
+ self.stc.perform("Rfc2544SetupBackToBackTestCommand", self.additional_params)
+ elif self.metric == "frameloss":
+ self.stc.perform("Rfc2544SetupFrameLossTestCommand", self.additional_params)
+ elif self.metric == "latency":
+ self.stc.perform("Rfc2544SetupLatencyTestCommand", self.additional_params)
+ else:
+ raise Exception("invalid rfc2544 test metric.")
+
+ # Save the configuration
+ self.stc.perform("SaveToTcc", params={"Filename": "2544.tcc"})
+
+ # Connect to the hardware...
+ self.stc.perform("AttachPorts",
+ params={"portList": self.stc.get("system1.project", "children-port"),
+ "autoConnect": "TRUE"})
+
+ # Apply configuration.
+ self.stc.apply()
+ self.stc.perform("SequencerStart")
+ self.stc.wait_until_complete()
+
+ return
+
+ def write_query_results_to_csv(self, results_path, csv_results_file_prefix, query_results):
+ filec = os.path.join(results_path, csv_results_file_prefix + ".csv")
+ with open(filec, "wb") as result_file:
+ result_file.write(query_results["Columns"].replace(" ", ",") + "\n")
+ for row in (query_results["Output"].replace("} {", ",").replace("{", "").replace("}", "").split(",")):
+ result_file.write(row.replace(" ", ",") + "\n")
+
+ def format_result(self, metric, original_result_dict):
+ result = {}
+ if metric == 'throughput':
+ columns = original_result_dict["Columns"].split(' ')
+ index_framesize = columns.index("ConfiguredFrameSize")
+ index_result = columns.index("Result")
+ index_throughput = columns.index("Throughput(%)")
+ index_ForwardingRate = columns.index("ForwardingRate(fps)")
+ outputs = \
+ original_result_dict["Output"].replace('} {', ',').replace("{", "").replace("}", "").split(",")
+
+ for row in outputs:
+ output = row.split(' ')
+ result[output[index_framesize]] = {'Result': output[index_result],
+ "Throughput(%)": output[index_throughput],
+ "ForwardingRate(fps)": output[index_ForwardingRate]}
+
+ elif self.metric == "latency":
+ pass
+
+ elif self.metric == "frameloss":
+ pass
+
+ elif self.metric == "backtoback":
+ pass
+
+ return result
+
+ def collect_result(self, local_dir):
+ # Determine what the results database filename is...
+ lab_server_resultsdb = self.stc.get(
+ "system1.project.TestResultSetting", "CurrentResultFileName")
+ self.stc.perform("CSSynchronizeFiles",
+ params={"DefaultDownloadDir": local_dir})
+
+ resultsdb = local_dir + lab_server_resultsdb.split("/Results")[1]
+
+ if not os.path.exists(resultsdb):
+ resultsdb = lab_server_resultsdb
+ self.logger.info("Failed to create the local summary DB File, using"
+ " the remote DB file instead.")
+ else:
+ self.logger.info(
+ "The local summary DB file has been saved to %s", resultsdb)
+
+ if self.metric == "throughput":
+ resultsdict = self.stc.perform("QueryResult",
+ params={
+ "DatabaseConnectionString": lab_server_resultsdb,
+ "ResultPath": "RFC2544ThroughputTestResultDetailedSummaryView"})
+ elif self.metric == "backtoback":
+ resultsdict = self.stc.perform("QueryResult",
+ params={
+ "DatabaseConnectionString": lab_server_resultsdb,
+ "ResultPath": "RFC2544Back2BackTestResultDetailedSummaryView"})
+ elif self.metric == "frameloss":
+ resultsdict = self.stc.perform("QueryResult",
+ params={
+ "DatabaseConnectionString": lab_server_resultsdb,
+ "ResultPath": "RFC2544FrameLossTestResultDetailedSummaryView"})
+ elif self.metric == "latency":
+ resultsdict = self.stc.perform("QueryResult",
+ params={
+ "DatabaseConnectionString": lab_server_resultsdb,
+ "ResultPath": "RFC2544LatencyTestResultDetailedSummaryView"})
+ else:
+ raise Exception("invalid rfc2544 test metric.")
+
+ self.write_query_results_to_csv(self.results_dir, self.metric, resultsdict)
+
+ self.result = self.format_result(self.metric, resultsdict)
+
+ return
+
+ def thread_entry(self):
+ self.status = self.TC_STATUS_RUNNING
+ try:
+ # create session on lab server
+ self.sess = StcSession(self.lab_server_ip, session_name=self.name, user_name=self.name)
+ self.stc = self.sess.stc
+
+ # create test result directory
+ self.results_dir = self.RESULT_PATH_PREFIX + self.tc_id + '/'
+ os.makedirs(self.results_dir)
+
+ # Bring up license server
+ self.config_license(self.license_server_ip)
+
+ self.logger.info("config license success, license_server_addr = %s.", self.license_server_ip)
+
+ # Create the root project object and Configure any custom traffic parameters
+ self.create_project()
+
+ self.logger.info("create project success.")
+
+ # configure test port
+ self.config_test_port(self.west_stcv_ip, 1, 1, self.west_stcv_tst_ip, self.east_stcv_tst_ip)
+ self.config_test_port(self.east_stcv_ip, 1, 1, self.east_stcv_tst_ip, self.west_stcv_tst_ip)
+
+ self.logger.info("config test port success, west_chassis_addr = %s, east_chassis_addr = %s.",
+ self.west_stcv_ip, self.east_stcv_ip)
+
+ # execute test
+ self.do_test()
+
+ self.logger.info("execute test success.")
+
+ # collect test result
+ self.collect_result(self.results_dir)
+
+ self.logger.info("collect result file success, results_dir = %s.", self.results_dir)
+
+ self.status = self.TC_STATUS_FINISHED
+
+ except Exception as err:
+ self.logger.error("Failed to execute Rfc2544 testcase, err: %s", str(err))
+ self.err_reason = str(err)
+ self.status = self.TC_STATUS_ERROR
+
+ finally:
+ if self.sess is not None:
+ self.sess.clean_all_session()
+
+ def execute(self):
+
+ self.executor = threading.Thread(name='rfc2544', target=self.thread_entry())
+ self.executor.start()
+
+ def get_result(self):
+ if self.status != self.TC_STATUS_FINISHED:
+ return {'name': self.name,
+ 'tc_id': self.tc_id,
+ 'status': self.status
+ }
+
+ return {'name': self.name,
+ 'category': 'rfc2544',
+ 'id': self.tc_id,
+ 'params': {
+ 'metric': self.metric,
+ 'framesizes': self.additional_params.get('FrameSizeList')},
+ 'result': self.result}
+
+ def get_status(self):
+ return self.status
+
+ def delete_result(self):
+ shutil.rmtree(self.results_dir)
+ pass
+
+ def cancel_run(self):
+ pass
+
+ def get_err_reason(self):
+ return self.err_reason
+
+
+if __name__ == '__main__':
+
+ lab_server_ip = '192.168.37.122'
+ license_server_ip = '192.168.37.251'
+ west_stcv_admin_ip = '192.168.37.202'
+ west_stcv_tst_ip = '192.168.1.20'
+ east_stcv_admin_ip = '192.168.37.212'
+ east_stcv_tst_ip = '192.168.1.17'
+
+ tc = StcRfc2544Test(name='tc1',
+ lab_server_ip=lab_server_ip,
+ license_server_ip=license_server_ip,
+ west_stcv_admin_ip=west_stcv_admin_ip,
+ west_stcv_tst_ip=west_stcv_tst_ip,
+ east_stcv_admin_ip=east_stcv_admin_ip,
+ east_stcv_tst_ip=east_stcv_tst_ip,
+ metric="throughput",
+ framesizes=[64, 128, 256, 512, 1024])
+ tc.execute()
+ status = tc.get_status()
+ while(status != tc.TC_STATUS_FINISHED):
+ if status == tc.TC_STATUS_ERROR:
+ print "tc exectue fail, reason %s" % tc.get_err_reason()
+ break
+ sleep(2)
+ if status == tc.TC_STATUS_FINISHED:
+ print tc.get_result()
+'''
+ tc = StcRfc2544Test(name='tc2',
+ lab_server_ip=lab_server_ip,
+ license_server_ip=license_server_ip,
+ west_stcv_admin_ip=west_stcv_admin_ip,
+ west_stcv_tst_ip=west_stcv_tst_ip,
+ east_stcv_admin_ip=east_stcv_admin_ip,
+ east_stcv_tst_ip=east_stcv_tst_ip,
+ metric="latency",
+ framesizes=[64, 128, 256, 512, 1024])
+ tc.execute()
+ status = tc.get_status()
+ while(status != tc.TC_STATUS_FINISHED):
+ if status == tc.TC_STATUS_ERROR:
+ print "tc exectue fail, reason %s" % tc.get_err_reason()
+ break
+ sleep(2)
+ if status == tc.TC_STATUS_FINISHED:
+ print tc.get_result()
+
+ tc = StcRfc2544Test(name='tc3',
+ lab_server_ip=lab_server_ip,
+ license_server_ip=license_server_ip,
+ west_stcv_admin_ip=west_stcv_admin_ip,
+ west_stcv_tst_ip=west_stcv_tst_ip,
+ east_stcv_admin_ip=east_stcv_admin_ip,
+ east_stcv_tst_ip=east_stcv_tst_ip,
+ metric="backtoback",
+ framesizes=[64, 128, 256, 512, 1024])
+ tc.execute()
+ status = tc.get_status()
+ while(status != tc.TC_STATUS_FINISHED):
+ if status == tc.TC_STATUS_ERROR:
+ print "tc exectue fail, reason %s" % tc.get_err_reason()
+ break
+ sleep(2)
+ if status == tc.TC_STATUS_FINISHED:
+ print tc.get_result()
+
+ tc = StcRfc2544Test(name='tc4',
+ lab_server_ip=lab_server_ip,
+ license_server_ip=license_server_ip,
+ west_stcv_admin_ip=west_stcv_admin_ip,
+ west_stcv_tst_ip=west_stcv_tst_ip,
+ east_stcv_admin_ip=east_stcv_admin_ip,
+ east_stcv_tst_ip=east_stcv_tst_ip,
+ metric="frameloss",
+ framesizes=[64, 128, 256, 512, 1024])
+ tc.execute()
+ status = tc.get_status()
+ while(status != tc.TC_STATUS_FINISHED):
+ if status == tc.TC_STATUS_ERROR:
+ print "tc exectue fail, reason %s" % tc.get_err_reason()
+ break
+ sleep(2)
+ if status == tc.TC_STATUS_FINISHED:
+ print tc.get_result()
+'''
+
+'''
+class Testcase(object):
+
+ def __init__(self, stack):
+ self.stack = stack
+
+ def execute(self):
+ pass
+
+class TestcaseFactory(object):
+
+ def __init__(self):
+
+ def create_tc(self, tc_metadata):
+ self.tc_name = tc_metadata['tc_name']
+ self.tc_id = str(uuid.uuid4())
+ if
+'''
diff --git a/contrib/nettest/nettest/start.sh b/contrib/nettest/nettest/start.sh
new file mode 100644
index 00000000..12ae3eb0
--- /dev/null
+++ b/contrib/nettest/nettest/start.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+##############################################################################
+# Copyright (c) 2018 Spirent Communications and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+exec /usr/bin/python rest_server.py
diff --git a/contrib/nettest/nettest/stcv_stack.py b/contrib/nettest/nettest/stcv_stack.py
new file mode 100644
index 00000000..7c1d4336
--- /dev/null
+++ b/contrib/nettest/nettest/stcv_stack.py
@@ -0,0 +1,165 @@
+##############################################################################
+# Copyright (c) 2018 Spirent Communications and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+import logging
+import os
+from time import sleep
+import traceback
+
+import heatclient.client as heatclient
+from keystoneauth1 import loading
+from keystoneauth1 import session
+
+
+class StcvStack(object):
+ STCV_CONFIG_FILE = 'stcv_config_file'
+ STCV_HEAT_FILE = './heat_2stcv.yaml'
+
+ def __init__(self, name, **kwargs):
+ self.logger = logging.getLogger(__name__)
+
+ self.name = name
+ self.pub_net_name = kwargs.get('pub_net_name')
+ self.ntp_server_ip = kwargs.get('ntp_server_ip')
+ self.lab_server_ip = kwargs.get('lab_server_ip')
+ self.stcv_image = kwargs.get('stcv_image')
+ self.stcv_flavor = kwargs.get('stcv_flavor')
+ if kwargs.get('stcv_affinity'):
+ self.stcv_affinity = 'affinity'
+ else:
+ self.stcv_affinity = 'anti-affinity'
+
+ self.stack_id = None
+ self._heatc_lient = None
+
+ def _attach_to_openstack(self):
+ creds = {"username": os.environ.get('OS_USERNAME'),
+ "password": os.environ.get('OS_PASSWORD'),
+ "auth_url": os.environ.get('OS_AUTH_URL'),
+ "project_domain_id": os.environ.get('OS_PROJECT_DOMAIN_ID'),
+ "project_domain_name": os.environ.get('OS_PROJECT_DOMAIN_NAME'),
+ "project_id": os.environ.get('OS_PROJECT_ID'),
+ "project_name": os.environ.get('OS_PROJECT_NAME'),
+ "tenant_name": os.environ.get('OS_TENANT_NAME'),
+ "tenant_id": os.environ.get("OS_TENANT_ID"),
+ "user_domain_id": os.environ.get('OS_USER_DOMAIN_ID'),
+ "user_domain_name": os.environ.get('OS_USER_DOMAIN_NAME')
+ }
+
+ self.logger.debug("Creds: %s" % creds)
+
+ loader = loading.get_plugin_loader('password')
+ auth = loader.load_from_options(**creds)
+ sess = session.Session(auth)
+ self._heat_client = heatclient.Client("1", session=sess)
+
+ def _make_parameters(self):
+ return {
+ 'public_net_name': self.pub_net_name,
+ 'stcv_image': self.stcv_image,
+ 'stcv_flavor': self.stcv_flavor,
+ 'stcv_sg_affinity': self.stcv_affinity,
+ 'ntp_server_ip': self.ntp_server_ip
+ }
+
+ def acquire_ip_from_stack_output(self, output, key_name):
+ ip = None
+ for item in output:
+ if item['output_key'] == key_name:
+ ip = item['output_value']
+ if isinstance(ip, list):
+ ip = ip[0]['ip_address']
+ break
+
+ return ip
+
+ def create_stack(self):
+ with open(self.STCV_HEAT_FILE) as fd:
+ template = fd.read()
+
+ self._attach_to_openstack()
+
+ self.logger.debug("Creating stack")
+
+ stack = self._heat_client.stacks.create(
+ stack_name=self.name,
+ template=template,
+ parameters=self._make_parameters())
+
+ self.stack_id = stack['stack']['id']
+
+ while True:
+ stack = self._heat_client.stacks.get(self.stack_id)
+ status = getattr(stack, 'stack_status')
+ self.logger.debug("Stack status=%s" % (status,))
+ if (status == u'CREATE_COMPLETE'):
+ self.stcv1_ip = self.acquire_ip_from_stack_output(stack.outputs, "STCv_1_Mgmt_Ip")
+ self.stcv2_ip = self.acquire_ip_from_stack_output(stack.outputs, "STCv_2_Mgmt_Ip")
+ self.stcv1_tst_ip = self.acquire_ip_from_stack_output(stack.outputs, "STCv_1_Tst_Ip")
+ self.stcv2_tst_ip = self.acquire_ip_from_stack_output(stack.outputs, "STCv_2_Tst_Ip")
+ break
+ if (status == u'DELETE_COMPLETE'):
+ self.stack_id = None
+ break
+ if (status == u'CREATE_FAILED'):
+ self.status_reason = getattr(stack, 'stack_status_reason')
+ sleep(5)
+ self._heat_client.stacks.delete(stack_id=self.stack_id)
+ sleep(2)
+
+ def delete_stack(self):
+ if self.stack_id is None:
+ raise Exception('stack does not exist')
+
+ self._attach_to_openstack()
+ while True:
+ stack = self._heat_client.stacks.get(self.stack_id)
+ status = getattr(stack, 'stack_status')
+ self.logger.debug("Stack status=%s" % (status,))
+ if (status == u'CREATE_COMPLETE'):
+ self._heat_client.stacks.delete(stack_id=self.stack_id)
+ if (status == u'DELETE_COMPLETE'):
+ self.stack_id = None
+ break
+ if (status == u'DELETE_FAILED'):
+ sleep(5)
+ self._heat_client.stacks.delete(stack_id=self.stack_id)
+ sleep(2)
+
+ def get_west_stcv_ip(self):
+ return self.stcv1_ip
+
+ def get_west_stcv_tst_ip(self):
+ return self.stcv1_tst_ip
+
+ def get_east_stcv_ip(self):
+ return self.stcv2_ip
+
+ def get_east_stcv_tst_ip(self):
+ return self.stcv2_tst_ip
+
+
+if __name__ == '__main__':
+ try:
+ stack = StcvStack(name='stack1',
+ pub_net_name='external',
+ ntp_server_ip='192.168.37.151',
+ stcv_image='stcv-4.79',
+ stcv_flavor='m1.tiny',
+ affinity=False)
+ stack.create_stack()
+
+ print stack.get_east_stcv_ip()
+ print stack.get_east_stcv_tst_ip()
+ print stack.get_west_stcv_ip()
+ print stack.get_west_stcv_tst_ip()
+
+ except Exception as err:
+ excstr = traceback.format_exc()
+ print excstr
diff --git a/contrib/nettest_client/nettest_client.py b/contrib/nettest_client/nettest_client.py
new file mode 100644
index 00000000..ec16caf0
--- /dev/null
+++ b/contrib/nettest_client/nettest_client.py
@@ -0,0 +1,190 @@
+##############################################################################
+# Copyright (c) 2018 Spirent Communications and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+import os
+import logging
+import requests
+import json
+import time
+
+
+class NettestClient(object):
+
+ def __init__(self, rest_server_ip, port, version):
+ self.logger = logging.getLogger(__name__)
+
+ self.rest_server_ip = rest_server_ip
+ self.port = port
+ self.version = version
+ self.base_url = "http://" + self.rest_server_ip + ":" + str(port) + "/api/" + "v" + self.version + "/"
+ self.headers = {"Content-Type": "application/json"}
+
+ def write_log(self, log):
+ self.logger.info(log)
+ print log
+
+ def create_stack(self, name, stack_type, public_network_name, **kargs):
+ stack_id = None
+
+ try:
+ payload = {
+ "stack_name": name,
+ "stack_type": stack_type,
+ "public_network": public_network_name,
+ "stack_params": {
+ "stcv_affinity": kargs.get("stcv_affinity"),
+ "stcv_image": kargs.get("stcv_image"),
+ "stcv_flavor": kargs.get("stcv_flavor"),
+ "lab_server_ip": kargs.get("lab_server_ip"),
+ "license_server_ip": kargs.get("license_server_ip")
+ }
+ }
+
+ stack_url = self.base_url + "stack"
+ response = requests.post(url=stack_url, headers=self.headers, json=payload)
+ if requests.codes.ok != response.status_code:
+ self.write_log("create stack fail, response_content = " + response.content)
+ return None
+ print response.content
+ stack_id = json.loads(response.content)["stack_id"]
+ except Exception as err:
+ self.write_log("create stack fail, error = " + str(err))
+
+ return stack_id
+
+ def destroy_stack(self, stack_id):
+ payload = {"id": stack_id}
+ url = self.base_url + "stack"
+ try:
+ response = requests.delete(url, params=payload)
+ if requests.codes.ok != response.status_code:
+ self.write_log("delete stack fail, err: " + response.content)
+ except Exception as err:
+ self.write_log("delete stack fail, error = " + str(err))
+ return
+
+ self.write_log("delete stack success")
+
+ def run_rfc2544_testcase(self, stack_id, tc_name, metric_type, framesizes):
+ url = self.base_url + "testcase"
+ payload = {
+ "name": tc_name,
+ "stack_id": stack_id,
+ "category": "rfc2544",
+ "params": {
+ "metric": metric_type,
+ "framesizes": framesizes
+ }
+ }
+ try:
+ response = requests.post(url, headers=self.headers, json=payload)
+ if requests.codes.ok != response.status_code:
+ self.write_log("run rfc2544 testcase fail, err = " + response.content)
+ return None
+ except Exception as err:
+ self.write_log("run rfc2544 testcase fail, err = " + str(err))
+ return None
+
+ self.write_log("run rfc2544 testcase success")
+
+ tc_id = json.loads(response.content)["tc_id"]
+
+ return tc_id
+
+ def delete_testcase(self, tc_id):
+ url = self.base_url + "testcase"
+ params = {"tc_id": tc_id}
+ try:
+ response = requests.delete(url, params=params)
+ if requests.codes.ok != response.status_code:
+ self.write_log("delete testcase fail, err = " + response.content)
+ except Exception as err:
+ self.write_log("delete testcase fail, err = " + str(err))
+
+ def write_result(self, result):
+ pass
+
+ def get_tc_result(self, tc_id):
+ ret = False
+ url = self.base_url + "testcase"
+ status_params = {
+ "id": tc_id,
+ "type": "status"
+ }
+ while True:
+ response = requests.get(url, params=status_params)
+ if requests.codes.ok == response.status_code:
+ status = json.loads(response.content)["status"]
+ if status == "running":
+ time.sleep(2)
+ continue
+ elif status == "finished":
+ url = "http://" + self.rest_server_ip + ":" + str(self.port) + "/tc_results/" + tc_id
+ response = requests.get(url)
+ if requests.codes.ok == response.status_code:
+ self.write_log("get tc result success")
+ with open(os.getcwd() + "/" + tc_id + ".csv", "w") as fd:
+ fd.write(response.content)
+ break
+ ret = True
+ else:
+ self.write_log(response.content)
+ break
+ else:
+ self.write_log(response.content)
+ break
+ else:
+ self.write_log(response.content)
+ break
+
+ return ret
+
+
+if __name__ == "__main__":
+
+ nc = NettestClient(rest_server_ip="127.0.0.1", port=5000, version="1.0")
+
+ stack_params = {
+ "name": 's2',
+ "stack_type": "stcv",
+ "public_network_name": "public",
+ "stcv_affinity": True,
+ "stcv_image": "STCv-4.80.2426",
+ "stcv_flavor": "small.shared",
+ "lab_server_ip": '10.61.67.53',
+ "license_server_ip": '10.140.88.61',
+ }
+
+ stack_id = nc.create_stack(**stack_params)
+ if stack_id is None:
+ print "create stack fail"
+ # exit(1)
+
+ # wait stcv vm into stable status
+ time.sleep(30)
+
+ tc_params = {
+ "stack_id": stack_id,
+ "tc_name": "tc1",
+ "metric_type": "throughput",
+ "framesizes": [64]
+ }
+ tc_id = nc.run_rfc2544_testcase(**tc_params)
+ if tc_id is None:
+ print "run testcase fail"
+ nc.destroy_stack(stack_id)
+ exit(1)
+
+ result = nc.get_tc_result(tc_id)
+ if result is False:
+ print "get testcase result fail"
+
+ nc.delete_testcase(tc_id)
+
+ nc.destroy_stack(stack_id)
diff --git a/docs/testing/developer/devguide/index.rst b/docs/testing/developer/devguide/index.rst
index 0b583cc5..ab411005 100644
--- a/docs/testing/developer/devguide/index.rst
+++ b/docs/testing/developer/devguide/index.rst
@@ -16,5 +16,6 @@ QTIP Developer Guide
framework.rst
cli.rst
api.rst
+ web.rst
compute-qpi.rst
storage-qpi.rst
diff --git a/docs/testing/developer/devguide/web.rst b/docs/testing/developer/devguide/web.rst
new file mode 100644
index 00000000..ae4e3156
--- /dev/null
+++ b/docs/testing/developer/devguide/web.rst
@@ -0,0 +1,100 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. http://creativecommons.org/licenses/by/4.0
+
+
+***************************************
+Web Portal for Benchmarking Services
+***************************************
+
+QTIP consists of different tools(metrics) to benchmark the NFVI. These metrics
+fall under different NFVI subsystems(QPI's) such as compute, storage and network.
+QTIP benchmarking tasks are built upon `Ansible`_ playbooks and roles.
+QTIP web portal is a platform to expose QTIP as a benchmarking service hosted on a central host.
+
+Framework
+=========
+
+The web travel has been developed on Python `Django`_ framework. Dig into the documentation to learn about Django.
+
+Design
+======
+
+Django is a MTV (Model Template View) framework. Database objects are mapped to models in ``models.py``. Views handle the
+requests from client side and interact with database using Django ORM. Templates are responsible for
+UI rendering based on response context from Views.
+
+Models
+------
+
+Repo
+~~~~
+
+Model for `workspace`_ repos
+
+::
+
+ Repo:
+ name
+ git_link
+
+
+Task
+~~~~
+
+Tasks keep track of every benchmark run through QTIP-Web Services. Whenever you run a benchmark,
+a new task is created which keep track of time stats and log task progress and ansible output for
+the respective playbook.
+
+::
+
+ Task
+ start_time
+ end_time
+ status
+ run_time
+ repo
+ log
+
+
+Views
+-----
+
+Dashboard
+~~~~~~~~~
+
+ - Base class - TemplateVIew
+
+Class based view serving as home page for the application.
+
+
+ReposView
+~~~~~~~~~
+
+ - Base class - LoginRequiredMixin, CreateView
+
+Class based view for listing and add new repos
+
+
+RepoUpdate
+~~~~~~~~~~
+
+ - Base class - LoginRequiredMixin, UpdateView
+
+Class based View for listing and updating an existing repo details.
+
+*Both ReposView and RepoUpdate View use same template ``repo_form.html``. The context has an extra variable ``template_role`` which is used to distinguish if repo form is for create or edit operation.*
+
+
+Run
+~~~
+
+ - Base class - LoginRequiredMixin, View
+ - template name - run.html
+
+Class based View for adding new task and run benchmark based on task details. The logs are saved
+in ``logs/run_<log_id>`` directory.
+
+
+.. _Ansible: https://www.ansible.com/
+.. _Django: https://docs.djangoproject.com/en/1.11/
+.. _workspace: https://github.com/opnfv/qtip/blob/master/docs/testing/developer/devguide/ansible.rst#create-workspace
diff --git a/docs/testing/user/configguide/index.rst b/docs/testing/user/configguide/index.rst
index 9c72ecd2..fa893e5e 100644
--- a/docs/testing/user/configguide/index.rst
+++ b/docs/testing/user/configguide/index.rst
@@ -12,3 +12,4 @@ QTIP Installation Guide
:maxdepth: 2
./configuration.rst
+ ./web.rst
diff --git a/docs/testing/user/configguide/web.rst b/docs/testing/user/configguide/web.rst
new file mode 100644
index 00000000..83365abe
--- /dev/null
+++ b/docs/testing/user/configguide/web.rst
@@ -0,0 +1,74 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. http://creativecommons.org/licenses/by/4.0
+
+
+***************************************
+Web Portal installation & configuration
+***************************************
+
+Web Portal for Benchmarking is developed on python `Django`_ Framework. Right now the installation
+is need to be done from source.
+
+
+
+Clone QTIP Repo
+===============
+
+::
+
+ git clone https://github.com/opnfv/qtip.git
+
+
+Setup database and Initialize user data
+=======================================
+
+CD into `web` directory.
+------------------------
+
+::
+
+ cd qtip/qtip/web
+
+
+Setup migrations
+----------------
+
+::
+
+ python manage.py makemigrations
+
+
+In usual case migrations will be already available with source. Console willll notify you
+of the same.
+
+Run migrations
+--------------
+
+::
+
+ python manage.py migrate
+
+
+Create superuser
+----------------
+::
+
+ python manage.py createsuperuser
+
+
+Console will prompt for adding new web admin. Enter new credentials.
+
+
+
+Collecting Static Dependencies
+------------------------------
+::
+
+ python manage.py importstatic
+
+
+This will import js and css dependencies for UI in static directory. Now the web application is
+ready to run.
+
+
+.. _Django: https://docs.djangoproject.com/en/1.11/
diff --git a/docs/testing/user/userguide/index.rst b/docs/testing/user/userguide/index.rst
index fbfdd394..e05a5e90 100644
--- a/docs/testing/user/userguide/index.rst
+++ b/docs/testing/user/userguide/index.rst
@@ -15,5 +15,8 @@ QTIP User Guide
getting-started.rst
cli.rst
api.rst
+ web.rst
compute.rst
storage.rst
+ network.rst
+ network_testcase_description.rst
diff --git a/docs/testing/user/userguide/network.rst b/docs/testing/user/userguide/network.rst
new file mode 100644
index 00000000..4d48d4d5
--- /dev/null
+++ b/docs/testing/user/userguide/network.rst
@@ -0,0 +1,115 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. http://creativecommons.org/licenses/by/4.0
+.. (c) 2018 Spirent Communications Corp.
+
+
+********************************
+Network Performance Benchmarking
+********************************
+Like compute or storage QPI, network QPI gives users an overall score for system network performance.
+For now it focuses on L2 virtual switch performance on NFVI. Current testcase are from RFC2544 standart and
+implemntation is based on Spirent Testcenter Virtual.
+
+For now, network QPI runs against on the baremetal/virtual scenario deployed by
+the OPNFV installer `APEX`_.
+
+Getting started
+===============
+Notice: All descriptions are based on containers.
+
+Requirements
+------------
+
+* Git must be installed.
+* Docker and docker-compose must be installed.
+* Spirent Testcenter Virtual image must be uploaded to the target cloud and the
+ associated flavor must be created before test.
+* Spirent License Server and Spirent LabServer must be set up and keep them ip
+ reachable from target cloud external network before test.
+
+Git Clone QTIP Repo
+-------------------
+
+::
+
+ git clone https://git.opnfv.org/qtip
+
+Running QTIP container and Nettest Containers
+----------------------------------------------
+
+With Docker Compose, we can use a YAML file to configure application's services and
+use a single command to create and start all the services.
+
+There is a YAML file ``./qtip/tests/ci/network/docker-compose.yaml`` from QTIP repos.
+It can help you to create and start the network QPI service.
+
+Before running docker-compose, you must specify these three variables:
+
+* DOCKER_TAG, which specified the Docker tag(ie: latest)
+* SSH_CREDENTIALS, a directory which includes an SSH key pair will be mounted into QTIP container.
+ QTIP use this SSH key pair to connect to remote hosts.
+* ENV_FILE, which includes the environment variables required by QTIP and Storperf containers
+
+ A example of ENV_FILE:
+
+ ::
+
+ INSTALLER_TYPE=apex
+ INSTALLER_IP=192.168.122.247
+ TEST_SUITE=network
+ NODE_NAME=zte-virtual5
+ SCENARIO=generic
+ TESTAPI_URL=
+ OPNFV_RELEASE=euphrates
+ # The below environment variables are Openstack Credentials.
+ OS_USERNAME=admin
+ OS_USER_DOMAIN_NAME=Default
+ OS_PROJECT_DOMAIN_NAME=Default
+ OS_BAREMETAL_API_VERSION=1.29
+ NOVA_VERSION=1.1
+ OS_PROJECT_NAME=admin
+ OS_PASSWORD=ZjmZJmkCvVXf9ry9daxgwmz3s
+ OS_NO_CACHE=True
+ COMPUTE_API_VERSION=1.1
+ no_proxy=,192.168.37.10,192.0.2.5
+ OS_CLOUDNAME=overcloud
+ OS_AUTH_URL=http://192.168.37.10:5000/v3
+ IRONIC_API_VERSION=1.29
+ OS_IDENTITY_API_VERSION=3
+ OS_AUTH_TYPE=password
+ # The below environment variables are extra info with Spirent.
+ SPT_LICENSE_SERVER_IP=192.168.37.251
+ SPT_LAB_SERVER_IP=192.168.37.122
+ SPT_STCV_IMAGE_NAME=stcv-4.79
+ SPT_STCV_FLAVOR_NAME=m1.tiny
+
+Then, you use the following commands to start network QPI service.
+
+::
+
+ docker-compose -f docker-compose.yaml pull
+ docker-compose -f docker-compose.yaml up -d
+
+Execution
+---------
+
+You can run network QPI with docker exec:
+::
+
+ docker exec <qtip container> bash -x /home/opnfv/repos/qtip/qtip/scripts/quickstart.sh
+
+QTIP generates results in the ``$PWD/results/`` directory are listed down under the
+timestamp name.
+
+Metrics
+-------
+
+Nettest provides the following `metrics`_:
+
+* RFC2544 througput
+* RFC2544 latency
+
+
+.. _APEX: https://wiki.opnfv.org/display/apex
+.. _metrics: https://tools.ietf.org/html/rfc2544
+
diff --git a/docs/testing/user/userguide/network_testcase_description.rst b/docs/testing/user/userguide/network_testcase_description.rst
new file mode 100644
index 00000000..66fda073
--- /dev/null
+++ b/docs/testing/user/userguide/network_testcase_description.rst
@@ -0,0 +1,127 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. http://creativecommons.org/licenses/by/4.0
+.. (c) 2018 Spirent Communications Corp.
+.. Template to be used for test case descriptions in QTIP Project.
+
+
+Test Case Description
+=====================
+
++-----------------------------------------------------------------------------+
+|Network throughput |
++==============+==============================================================+
+|test case id | qtip_throughput |
++--------------+--------------------------------------------------------------+
+|metric | rfc2544 throughput |
++--------------+--------------------------------------------------------------+
+|test purpose | get the max throughput of the pathway on same host or accross|
+| | hosts |
++--------------+--------------------------------------------------------------+
+|configuration | None |
++--------------+--------------------------------------------------------------+
+|test tool | Spirent Test Center Virtual |
++--------------+--------------------------------------------------------------+
+|references | RFC2544 |
++--------------+--------------------------------------------------------------+
+|applicability | 1. test the switch throughput on same host or accross hosts |
+| | 2. test the switch throughput for different packet sizes |
++--------------+--------------------------------------------------------------+
+|pre-test | 1. deploy STC license server and LabServer on public network |
+|conditions | and verify it can operate correctlly |
+| | 2. upload STC virtual image and create STCv flavor on the |
+| | deployed cloud environment |
++--------------+------+----------------------------------+--------------------+
+|test sequence | step | description | result |
+| +------+----------------------------------+--------------------+
+| | 1 | deploy STCv stack on the target | 2 STCv VM will be |
+| | | cloud with affinity attribute | established on the |
+| | | according to requirements. | cloud |
+| +------+----------------------------------+--------------------+
+| | 2 | run rfc2544 throughput test with | test result report |
+| | | different packet size | will be produced in|
+| | | | QTIP container |
+| +------+----------------------------------+--------------------+
+| | 3 | destory STCv stack | STCv stack |
+| | | different packet size | destoried |
++--------------+------+----------------------------------+--------------------+
+|test verdict | find the test result report in QTIP container running |
+| | directory |
++--------------+--------------------------------------------------------------+
+
++-----------------------------------------------------------------------------+
+|Network throughput |
++==============+==============================================================+
+|test case id | qtip_latency |
++--------------+--------------------------------------------------------------+
+|metric | rfc2544 lantency |
++--------------+--------------------------------------------------------------+
+|test purpose | get the latency value of the pathway on same host or accross |
+| | hosts |
++--------------+--------------------------------------------------------------+
+|configuration | None |
++--------------+--------------------------------------------------------------+
+|test tool | Spirent Test Center Virtual |
++--------------+--------------------------------------------------------------+
+|references | RFC2544 |
++--------------+--------------------------------------------------------------+
+|applicability | 1. test the switch latency on same host or accross hosts |
+| | 2. test the switch latency for different packet sizes |
++--------------+--------------------------------------------------------------+
+|pre-test | 1. deploy STC license server and LabServer on public network |
+|conditions | and verify it can operate correctlly |
+| | 2. upload STC virtual image and create STCv flavor on the |
+| | deployed cloud environment |
++--------------+------+----------------------------------+--------------------+
+|test sequence | step | description | result |
+| +------+----------------------------------+--------------------+
+| | 1 | deploy STCv stack on the target | 2 STCv VM will be |
+| | | cloud with affinity attribute | established on the |
+| | | according to requirements. | cloud |
+| +------+----------------------------------+--------------------+
+| | 2 | run rfc2544 latency test with | test result report |
+| | | different packet size | will be produced in|
+| | | | QTIP container |
+| +------+----------------------------------+--------------------+
+| | 3 | destroy STCv stack | STCv stack |
+| | | | destried |
++--------------+------+----------------------------------+--------------------+
+|test verdict | find the test result report in QTIP container running |
+| | directory |
++--------------+--------------------------------------------------------------+
+
++-----------------------------------------------------------------------------+
+|Network Latency |
++==============+==============================================================+
+|test case id | e.g. qtip_throughput |
++--------------+--------------------------------------------------------------+
+|metric | what will be measured, e.g. latency |
++--------------+--------------------------------------------------------------+
+|test purpose | describe what is the purpose of the test case |
++--------------+--------------------------------------------------------------+
+|configuration | what .yaml file to use, state SLA if applicable, state |
+| | test duration, list and describe the scenario options used in|
+| | this TC and also list the options using default values. |
++--------------+--------------------------------------------------------------+
+|test tool | e.g. ping |
++--------------+--------------------------------------------------------------+
+|references | RFC2544 |
++--------------+--------------------------------------------------------------+
+|applicability | describe variations of the test case which can be |
+| | performend, e.g. run the test for different packet sizes |
++--------------+--------------------------------------------------------------+
+|pre-test | describe configuration in the tool(s) used to perform |
+|conditions | the measurements (e.g. fio, pktgen), POD-specific |
+| | configuration required to enable running the test |
++--------------+------+----------------------------------+--------------------+
+|test sequence | step | description | result |
+| +------+----------------------------------+--------------------+
+| | 1 | use this to describe tests that | what happens in |
+| | | require several steps e.g. | this step |
+| | | step 1 collect logs | e.g. logs collected|
+| +------+----------------------------------+--------------------+
+| | 2 | remove interface | interface down |
+| +------+----------------------------------+--------------------+
+| | N | what is done in step N | what happens |
++--------------+------+----------------------------------+--------------------+
+|test verdict | expected behavior, or SLA, pass/fail criteria |
++--------------+--------------------------------------------------------------+
diff --git a/docs/testing/user/userguide/web.rst b/docs/testing/user/userguide/web.rst
new file mode 100644
index 00000000..79f180d9
--- /dev/null
+++ b/docs/testing/user/userguide/web.rst
@@ -0,0 +1,70 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. http://creativecommons.org/licenses/by/4.0
+
+
+**********************
+Web Portal User Manual
+**********************
+
+QTIP consists of different tools(metrics) to benchmark the NFVI. These metrics
+fall under different NFVI subsystems(QPI's) such as compute, storage and network.
+QTIP benchmarking tasks are built upon `Ansible`_ playbooks and roles.
+QTIP web portal is a platform to expose QTIP as a benchmarking service hosted on a central host.
+
+
+Running
+=======
+
+After setting up the web portal as instructed in config guide, cd into the `web` directory.
+
+and run.
+
+::
+
+ python manage.py runserver 0.0.0.0
+
+
+You can access the portal by logging onto `<host>:8000/bench/login/`
+
+If you want to use port 80, you may need sudo permission.
+
+::
+
+ sudo python manage.py runserver 0.0.0.0:80
+
+To Deploy on `wsgi`_, Use the Django `deployment tutorial`_
+
+
+Features
+========
+
+After logging in You'll be redirect to QTIP-Web Dashboard. You'll see following menus on left.
+
+ * Repos
+ * Run Benchmarks
+ * Tasks
+
+Repo
+----
+
+ Repos are links to qtip `workspaces`_. This menu list all the aded repos. Links to new repos
+ can be added here.
+
+Run Benchmarks
+--------------
+
+ To run a benchmark, select the corresponding repo and run. QTIP Benchmarking service will clone
+ the workspace and run the benchmarks. Inventories used are predefined in the workspace repo in the `/hosts/` config file.
+
+Tasks
+-----
+
+ All running or completed benchmark jobs can be seen in Tasks menu with their status.
+
+
+*New users can be added by Admin on the Django Admin app by logging into `/admin/'.*
+
+.. _Ansible: https://www.ansible.com/
+.. _wsgi: https://wsgi.readthedocs.io/en/latest/what.html
+.. _deployment tutorial: https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/
+.. _workspaces: https://github.com/opnfv/qtip/blob/master/docs/testing/developer/devguide/ansible.rst#create-workspace
diff --git a/legacy/DO-NOT-DELETE b/legacy/DO-NOT-DELETE
new file mode 100644
index 00000000..fdecaad1
--- /dev/null
+++ b/legacy/DO-NOT-DELETE
@@ -0,0 +1,2 @@
+The legacy code is no longer maintained. But they should be kept until we finish
+migration to new architecture.
diff --git a/legacy/__init__.py b/legacy/__init__.py
new file mode 100644
index 00000000..48893ae6
--- /dev/null
+++ b/legacy/__init__.py
@@ -0,0 +1,8 @@
+##############################################################################
+# Copyright (c) 2017 ZTE Corporation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
diff --git a/legacy/assets/perftest/common/git_proxy_pbook.yaml b/legacy/assets/perftest/common/git_proxy_pbook.yaml
new file mode 100644
index 00000000..e190162b
--- /dev/null
+++ b/legacy/assets/perftest/common/git_proxy_pbook.yaml
@@ -0,0 +1,19 @@
+##############################################################################
+# Copyright (c) 2017 ZTE Corporation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+#git
+- name: set git proxy(http)
+ shell: "git config --global http.proxy {{ http_proxy }}"
+ when: http_proxy is defined
+ ignore_errors: yes
+
+- name: set git proxy(https)
+ shell: "git config --global https.proxy {{https_proxy}}"
+ when: https_proxy is defined
+ ignore_errors: yes
+
diff --git a/legacy/assets/perftest/common/sys_proxy_pbook.yaml b/legacy/assets/perftest/common/sys_proxy_pbook.yaml
new file mode 100644
index 00000000..543285e3
--- /dev/null
+++ b/legacy/assets/perftest/common/sys_proxy_pbook.yaml
@@ -0,0 +1,61 @@
+##############################################################################
+# Copyright (c) 2017 ZTE Corporation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+#env
+- name: insert shell proxy http
+ lineinfile: dest=/etc/profile.d/proxy.sh state=present create=yes owner=root group=root mode=0644 regexp="export http_proxy={{ http_proxy }}"
+ insertafter=EOF line="export http_proxy={{ http_proxy }}"
+ when: http_proxy is defined
+ ignore_errors: yes
+
+- name: insert shell proxy https
+ lineinfile: dest=/etc/profile.d/proxy.sh state=present create=yes owner=root group=root mode=0644 regexp="export https_proxy={{ https_proxy }}"
+ insertafter=EOF line="export https_proxy={{ https_proxy }}"
+ when: https_proxy is defined
+ ignore_errors: yes
+
+- name: insert no proxy
+ lineinfile: dest=/etc/profile.d/proxy.sh state=present create=yes owner=root group=root mode=0644 regexp="{{ no_proxy }}"
+ insertafter=EOF line="export no_proxy={{ no_proxy }}"
+ when: no_proxy is defined
+ ignore_errors: yes
+
+#wget
+- name: insert wget proxy(http)
+ lineinfile: dest=/etc/wgetrc state=present regexp="http_proxy={{ http_proxy }}"
+ insertafter="^#http_proxy" line="http_proxy={{ http_proxy }}"
+ when: http_proxy is defined
+ ignore_errors: yes
+
+- name: insert wget proxy(https)
+ lineinfile: dest=/etc/wgetrc state=present regexp="https_proxy={{ https_proxy }}"
+ insertafter="^#https_proxy" line="https_proxy={{ https_proxy }}"
+ when: https_proxy is defined
+ ignore_errors: yes
+
+#yum
+- name: insert yum proxy(http)
+ lineinfile: dest=/etc/yum.conf state=present regexp="proxy={{ http_proxy }}"
+ insertafter=EOF line="proxy={{ http_proxy }}"
+ when: ansible_os_family == "RedHat" and http_proxy is defined
+ ignore_errors: yes
+
+#apt
+
+- name: insert apt proxy(http)
+ lineinfile: dest=/etc/apt/apt.conf state=present create=yes regexp="Acquire::http::Proxy \"{{ http_proxy }}\";"
+ insertafter=EOF line="Acquire::http::Proxy \"{{ http_proxy }}\";"
+ when: ansible_os_family == "Debian" and http_proxy is defined
+ ignore_errors: yes
+
+- name: insert apt proxy(https)
+ lineinfile: dest=/etc/apt/apt.conf state=present create=yes regexp="Acquire::https::Proxy \"{{ https_proxy }}\";"
+ insertafter=EOF line="Acquire::https::Proxy \"{{ https_proxy }}\";"
+ when: ansible_os_family == "Debian" and https_proxy is defined
+ ignore_errors: yes
+
diff --git a/legacy/assets/perftest/etc/fio_test_job b/legacy/assets/perftest/etc/fio_test_job
new file mode 100644
index 00000000..6817abca
--- /dev/null
+++ b/legacy/assets/perftest/etc/fio_test_job
@@ -0,0 +1,13 @@
+[global]
+
+runtime= 600
+ioengine=libaio
+iodepth=2
+direct=1
+bs=4k
+rw=randrw
+
+[job1]
+size=5G
+
+
diff --git a/legacy/assets/perftest/etc/info_collect.py b/legacy/assets/perftest/etc/info_collect.py
new file mode 100644
index 00000000..3dbe55c2
--- /dev/null
+++ b/legacy/assets/perftest/etc/info_collect.py
@@ -0,0 +1,94 @@
+##############################################################################
+# Copyright (c) 2017 ZTE Corporation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+import os
+import pickle
+import json
+import sys
+
+os.system('inxi -b -c0 -n > $PWD/est_2')
+est_ob = open("est_2", "r+")
+est_ob2 = open("est_1", "w+")
+in_string = est_ob.read().replace('\n', ' ')
+cpu_idle = float(os.popen("""top -bn1 | grep "Cpu(s)" | awk '{print $8}'""").read().rstrip())
+cpu_usage = 100 - cpu_idle
+est_ob2.write(in_string)
+est_ob.close()
+est_ob2.close()
+
+inxi_host = os.popen("""cat $PWD/est_1 | grep -o -P '(?<=Host:).*(?=Kernel)' """).read().lstrip().rstrip()
+inxi_mem = os.popen("""cat $PWD/est_1 | grep -o -P '(?<=Memory:).*(?=MB)' """).read().lstrip().rstrip() + "MB"
+inxi_cpu = os.popen("""cat $PWD/est_1 | grep -o -P '(?<=CPU).*(?=speed)' | cut -f2 -d':'""").read().lstrip().rstrip()
+inxi_distro = os.popen(""" cat $PWD/est_1 | grep -o -P '(?<=Distro:).*(?=Machine:)' """).read().rstrip().lstrip()
+inxi_kernel = os.popen(""" cat $PWD/est_1 | grep -o -P '(?<=Kernel:).*(?=Console:)' """).read().rstrip().lstrip()
+inxi_HD = os.popen(""" cat $PWD/est_1 | grep -o -P '(?<=HDD Total Size:).*(?=Info:)' """).read().rstrip().lstrip()
+inxi_product = os.popen(""" cat $PWD/est_1 | grep -o -P '(?<=product:).*(?=Mobo:)' """).read().rstrip().lstrip()
+
+info_dict = {'hostname': inxi_host,
+ 'product': inxi_product,
+ 'os': inxi_distro,
+ 'kernel': inxi_kernel,
+ 'cpu': inxi_cpu,
+ 'cpu_usage': '{0}%'.format(str(round(cpu_usage, 3))),
+ 'memory_usage': inxi_mem,
+ 'disk_usage': inxi_HD}
+network_flag = str(sys.argv[1]).rstrip()
+
+if (network_flag == 'n'):
+
+ info_dict['network_interfaces'] = {}
+ tem_2 = """ cat $PWD/est_1 | grep -o -P '(?<=Network:).*(?=Info:)'"""
+ print os.system(tem_2 + ' > Hello')
+ i = int(os.popen(tem_2 + " | grep -o 'Card' | wc -l ").read())
+ print i
+
+ for x in range(1, i + 1):
+ tem = """ cat $PWD/est_1 | grep -o -P '(?<=Card-""" + str(x) + """:).*(?=Card-""" + str(x + 1) + """)'"""
+ if i == 1:
+ tem = """ cat $PWD/est_1 | grep -o -P '(?<=Network:).*(?=Info:)'"""
+ inxi_card_1 = ((os.popen(tem + " | grep -o -P '(?<=Card:).*(?=Drives:)'|sed 's/ *driver:.*//'").read().rstrip().lstrip()))
+ print inxi_card_1
+ info_dict['network_interfaces']['interface_' + str(x)] = {}
+ info_dict['network_interfaces']['interface_' + str(x)]['network_card'] = inxi_card_1
+ inxi_card_2 = ((os.popen(tem + "| grep -o -P '(?<=Card:).*(?=Drives:)'|sed -e 's/^.*IF: //'").read())).rstrip().lstrip()
+ info_dict['network_interfaces']['interface_' + str(x)]['interface_info'] = inxi_card_2
+ elif x < (i):
+ print "two"
+ inxi_card_1 = ((os.popen(tem + "| sed 's/ *driver:.*//'").read().rstrip().lstrip()))
+ info_dict['network_interfaces']['interface_' + str(x)] = {}
+ info_dict['network_interfaces']['interface_' + str(x)]['network_Card'] = inxi_card_1
+ inxi_card_2 = ((os.popen(tem + "|sed -e 's/^.*IF: //'").read())).rstrip().lstrip()
+ info_dict['network_interfaces']['interface_' + str(x)]['interface_info'] = inxi_card_2
+ elif x == i:
+ print "Three"
+ info_dict['network_interfaces']['interface_' + str(x)] = {}
+ inxi_card_1 = ((os.popen(""" cat $PWD/est_1 | grep -o -P '(?<=Card-""" + str(x) + """:).*(?=Drives:)'| sed 's/ *driver:.*//' """).read().rstrip().lstrip()))
+ info_dict['network_interfaces']['interface_' + str(x)]['network_Card'] = inxi_card_1
+ inxi_card_2 = ((os.popen(""" cat $PWD/est_1 | grep -o -P '(?<=Card-""" + str(x) + """:).*(?=Drives:)'| sed -e 's/^.*IF: //' """).read().rstrip().lstrip()))
+ info_dict['network_interfaces']['interface_' + str(x)]['interface_info'] = inxi_card_2
+ else:
+ print "No network cards"
+ os.system("bwm-ng -o plain -c 1 | grep -v '=' | grep -v 'iface' | grep -v '-' > bwm_dump")
+ n_interface = int(os.popen(" cat bwm_dump | grep -v 'total' | wc -l ").read().rstrip())
+ interface = {}
+ for x in range(1, n_interface):
+ interface_name = os.popen(" cat bwm_dump | awk 'NR==" + str(x) + "' | awk '{print $1}' ").read().rstrip().replace(':', '')
+ interface[str(interface_name)] = {}
+ interface[str(interface_name)]['Rx (KB/s)'] = os.popen(" cat bwm_dump | awk 'NR==" + str(x) + "' | awk '{print $2}' ").read().rstrip()
+ interface[str(interface_name)]['Tx (KB/s)'] = os.popen(" cat bwm_dump | awk 'NR==" + str(x) + "' | awk '{print $4}' ").read().rstrip()
+ interface[str(interface_name)]['Total (KB/s)'] = os.popen(" cat bwm_dump | awk 'NR== " + str(x) + "' | awk '{print $6}' ").read().rstrip()
+
+ info_dict['interface_io'] = interface
+
+print info_dict
+
+with open('./sys_info_temp', 'w+')as out_info:
+ pickle.dump(info_dict, out_info)
+
+with open('temp', 'w+') as result_json:
+ json.dump(info_dict, result_json, indent=4, sort_keys=True)
diff --git a/legacy/assets/perftest/fio.yaml b/legacy/assets/perftest/fio.yaml
new file mode 100644
index 00000000..e6d1072d
--- /dev/null
+++ b/legacy/assets/perftest/fio.yaml
@@ -0,0 +1,120 @@
+##############################################################################
+# Copyright (c) 2017 ZTE Corporation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+ - hosts: localhost
+ connection: local
+ gather_facts: no
+
+ tasks:
+ - name: making fio directory
+ file: path={{Dest_dir}}/fio state=directory
+
+ - name: making temporary fio directory
+ file: path={{Dest_dir}}/fio/fio_temp state=directory
+
+
+ - hosts: "{{role}}"
+ become: yes
+ remote_user: "{{username}}"
+
+ tasks:
+ - name: checking home directory
+ shell: echo $HOME
+ register: home_dir
+
+ - name: cleaning fio directory
+ file: path={{home_dir.stdout}}/fio state=absent
+
+ - name: cleaning previous results
+ file: path={{home_dir.stdout}}/qtip_result state=absent
+
+ - name: making fio temporary directory
+ file: path={{home_dir.stdout}}/fio state=directory
+
+ - name: making results temporary directory
+ file: path={{home_dir.stdout}}/qtip_result state=directory
+
+ - include: ./common/sys_proxy_pbook.yaml
+
+ - include: ./common/sys_info_pbook.yaml
+ vars:
+ network: false
+
+ - name: Installing fio dependencies when CentOS
+ shell: sudo yum install wget gcc libaio-devel -y
+ when: ansible_os_family == "RedHat"
+
+ - name: Installing fio dependencies when Ubuntu
+ shell: sudo apt-get install wget gcc libaio-dev -y
+ when: ansible_os_family == "Debian"
+
+ - name: Fetching fio
+ shell: cd $HOME/fio/ && wget http://freecode.com/urls/3aa21b8c106cab742bf1f20d60629e3f -O fio.tar.gz
+
+ - name: Untar fio
+ shell: cd $HOME/fio/ && sudo tar -zxvf fio.tar.gz
+
+ - name: configure
+ shell: cd $HOME/fio/fio-2.1.10 && sudo ./configure && sudo make
+
+ - name: Fetching fio job
+ copy: src=./etc/fio_test_job dest={{home_dir.stdout}}/fio/fio-2.1.10/
+
+ - name: Benchmarking block storage through fio
+ shell: cd $HOME/fio/fio-2.1.10 && sudo ./fio --output-format=json --output=$HOME/qtip_result/fio_result.json fio_test_job
+
+ - name: Fetching result transformation script
+ copy: src={{workingdir}}/qtip/utils/transform/fio_transform.py dest={{home_dir.stdout}}/qtip_result
+
+ - name: Transforming result
+ shell: cd $HOME/qtip_result && sudo python fio_transform.py
+
+ - name: copy report formation script
+ copy: src={{workingdir}}/qtip/utils/transform/final_report.py dest={{home_dir.stdout}}/qtip_result
+
+ - name: consolidating report
+ shell: cd $HOME/qtip_result && sudo python final_report.py FIO {{fname}}
+
+ - name: registering files
+ shell: (cd $HOME/qtip_result/; find . -maxdepth 1 -name "*.json") | cut -d'/' -f2
+ register: files_to_copy
+
+ - name: copy results
+ fetch: src={{home_dir.stdout}}/qtip_result/{{item}} dest={{Dest_dir}}/fio/fio_temp
+ with_items: "{{files_to_copy.stdout_lines}}"
+
+ - name: registering log files
+ shell: (cd $HOME/qtip_result/; find . -maxdepth 1 -name "*.log") | cut -d'/' -f2
+ register: copy_log_results
+
+ - name: copying log results
+ fetch: src={{home_dir.stdout}}/qtip_result/{{item}} dest={{Dest_dir}}/fio/fio_temp
+ with_items: "{{copy_log_results.stdout_lines}}"
+
+ - name: cleaning fio
+ file: path={{home_dir.stdout}}/fio state=absent
+
+ - name: cleaning_qtip_result
+ file: path={{home_dir.stdout}}/qtip_result state=absent
+
+ - hosts: localhost
+ connection: local
+ gather_facts: no
+
+ tasks:
+ - name: extracting_json
+ shell: (find {{Dest_dir}}/fio/fio_temp/ -name "*.json" | xargs cp -t {{Dest_dir}}/fio/)
+
+ - name: making_logs_folder
+ file: path={{Dest_dir}}/fio/logs state=directory
+
+ - name: extracting_log
+ shell: (find {{Dest_dir}}/fio/fio_temp/ -name "*.log" | xargs cp -t {{Dest_dir}}/fio/logs)
+
+ - name: removing fio_log
+ file: path={{Dest_dir}}/fio/fio_temp state=absent
diff --git a/legacy/assets/perftest/iperf.yaml b/legacy/assets/perftest/iperf.yaml
new file mode 100644
index 00000000..6654c556
--- /dev/null
+++ b/legacy/assets/perftest/iperf.yaml
@@ -0,0 +1,170 @@
+##############################################################################
+# Copyright (c) 2017 ZTE Corporation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+ - hosts: localhost
+ connection: local
+ gather_facts: no
+
+ tasks:
+ - name: making Iperf directory
+ file: path={{Dest_dir}}/iperf state=directory
+
+ - name: making temporary iperf directory
+ file: path={{Dest_dir}}/iperf/iperf_temp state=directory
+
+
+ - hosts: "{{role}}"
+ become: yes
+ remote_user: "{{username}}"
+
+ tasks:
+ - name: Rolename
+ set_fact:
+ rolename: "{{role}}"
+ when: role is defined
+
+ - name: installertype
+ set_fact:
+ installertype: "{{installer}}"
+
+ - name: Get Hostname
+ shell: echo $HOSTNAME
+ register: hostID
+
+ - name: echo
+ shell: echo index_var
+
+ - name: checking home directory
+ shell: echo $HOME
+ register: home_dir
+
+ - name: cleaning iperf directory
+ file: path={{home_dir.stdout}}/iperf state=absent
+
+ - name: cleaning previous results
+ file: path={{home_dir.stdout}}/qtip_result state=absent
+
+ - name: making Iperf temporary directory
+ file: path={{home_dir.stdout}}/iperf state=directory
+
+ - name: making results temporary directory
+ file: path={{home_dir.stdout}}/qtip_result state=directory
+
+ - include: ./common/sys_proxy_pbook.yaml
+
+ - include: ./common/sys_info_pbook.yaml
+ vars:
+ network: true
+
+ - name: Installing Epel-release when CentOS
+ shell: sudo yum install epel-release -y
+ when: ansible_os_family == "RedHat"
+
+ - name: Allow iperf server port in iptables input rules
+ shell: iptables -A INPUT -p tcp --dport {{iperf_port}} -j ACCEPT
+ vars:
+ iperf_port: 5201
+ ignore_errors: yes
+ when: rolename == "1-server" and installertype == 'fuel'
+
+ - name: Installing IPERF when Ubuntu
+ shell: sudo apt-get install iperf3 -y
+ when: ansible_os_family == "Debian"
+
+ - name: Installing Iperf3
+ shell: sudo yum install iperf3 -y
+ when: ansible_os_family == "RedHat"
+
+ - name: Running iperf on server
+ shell: iperf3 -s
+ async: 400
+ poll: 0
+ when: rolename == "1-server"
+
+ - name: Running Iperf on Host
+ shell: iperf3 --time {{duration}} -b 0 G -c {{ip1}} -J -O10 >> {{home_dir.stdout}}/qtip_result/iperf_raw.json
+ ignore_errors: yes
+ with_items:
+ - "{{ip1}}"
+ when: rolename == "2-host" and "{{privateip1}}" == "NONE"
+
+ - name: Running Iperf on Host
+ shell: iperf3 --time {{duration}} -b 0 G -c {{privateip1}} -J -O10 >> {{home_dir.stdout}}/qtip_result/iperf_raw.json
+ ignore_errors: yes
+ with_items:
+ - "{{ip1}}"
+ when: rolename == "2-host" and "{{privateip1}}" != "NONE"
+
+ - name: Fetching result transformation script
+ copy: src={{workingdir}}/qtip/utils/transform/iperf_transform.py dest={{home_dir.stdout}}/qtip_result
+ - name: Transforming result
+
+ shell: cd $HOME/qtip_result && sudo python iperf_transform.py
+ when: rolename =="2-host" and "{{ip2}}" == ''
+
+ - name: copy report formation script
+ copy: src={{workingdir}}/qtip/utils/transform/final_report.py dest={{home_dir.stdout}}/qtip_result
+ when: rolename =="2-host" and "{{ip2}}" == ''
+
+ - name: consolidating report
+ shell: cd $HOME/qtip_result && sudo python final_report.py IPERF {{fname}}
+ when: rolename =="2-host" and "{{ip2}}" == ''
+
+ - name: Files to Copy
+ shell: (cd $HOME/qtip_result/; find . -maxdepth 1 -name "*.json") | cut -d'/' -f2
+ register: files_to_copy
+ when: rolename =="2-host" and "{{ip2}}" == ''
+
+ - name: copy results
+ fetch: src={{home_dir.stdout}}/qtip_result/{{item}} dest={{Dest_dir}}/iperf/iperf_temp
+ with_items: "{{files_to_copy.stdout_lines}}"
+ when: rolename =="2-host" and "{{ip2}}" == ''
+
+ - name: registering log files
+ shell: (cd $HOME/qtip_result/; find . -maxdepth 1 -name "*.log") | cut -d'/' -f2
+ register: copy_log_results
+ when: rolename =="2-host" and "{{ip2}}" == ''
+
+ - name: copying log results
+ fetch: src={{home_dir.stdout}}/qtip_result/{{item}} dest={{Dest_dir}}/iperf/iperf_temp
+ with_items: "{{copy_log_results.stdout_lines}}"
+ when: rolename =="2-host" and "{{ip2}}" == ''
+
+ - name: cleaning iperf directory
+ file: path={{home_dir.stdout}}/iperf state=absent
+
+ - name: cleaning previous results
+ file: path={{home_dir.stdout}}/qtip_result state=absent
+
+ - hosts: localhost
+ connection: local
+ gather_facts: no
+
+ tasks:
+ - name: Rolename
+ set_fact:
+ rolename: "{{role}}"
+ when: role is defined
+
+ - name: extracting_json
+ shell: (find {{Dest_dir}}/iperf/iperf_temp/ -name "*.json" | xargs cp -t {{Dest_dir}}/iperf/)
+ when: rolename == "2-host"
+
+ - name: making_logs_folder
+ file: path={{Dest_dir}}/iperf/logs state=directory
+
+ - name: extracting_log
+ shell: ( find {{Dest_dir}}/iperf/iperf_temp/ -name "*.log" | xargs cp -t {{Dest_dir}}/iperf/logs)
+ when: rolename == "2-host"
+
+ - name: removing iperf_raw file
+ file: path={{Dest_dir}}/iperf/iperf_raw.json state=absent
+ when: rolename == "2-host"
+
+ - name: removing iperf_temp
+ file: path={{Dest_dir}}/iperf/iperf_temp state=absent
diff --git a/legacy/assets/perftest/summary b/legacy/assets/perftest/summary
new file mode 100644
index 00000000..5891408c
--- /dev/null
+++ b/legacy/assets/perftest/summary
@@ -0,0 +1,23 @@
+---
+
+ test_cases:
+ - name: fio
+ description: Storage performance benchmark
+
+ - name: iperf
+ description: Measures the network throughput
+
+ - name: dpi
+ description: Traffic classification rate provides a measure for CPU performance
+
+ - name: ssl
+ description: CPU performance benchmark
+
+ - name: dhrystone
+ description: Evaluate CPU's integer operation performance
+
+ - name: whetstone
+ description: Evaluate CPU's floating point performance
+
+ - name: ramspeed
+ description: Measures the memory performance of a machine
diff --git a/legacy/assets/testplan/default/network/iperf_bm.yaml b/legacy/assets/testplan/default/network/iperf_bm.yaml
new file mode 100644
index 00000000..3b10a383
--- /dev/null
+++ b/legacy/assets/testplan/default/network/iperf_bm.yaml
@@ -0,0 +1,58 @@
+##############################################################################
+# Copyright (c) 2017 ZTE Corporation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+Scenario:
+ benchmark: iperf
+ topology: Client and Server on different baremetal Compute nodes
+ server: machine_1
+ client: machine_2
+ benchmark_details:
+ duration: 20
+ protocol: tcp
+ bandwidthGbps: 10
+
+Context:
+ Host_Machines:
+ machine_1:
+ ip:
+ pw:
+ role: 1-server
+ machine_2:
+ ip:
+ pw:
+ role: 2-host
+
+ Virtual_Machines:
+
+Test_Description:
+ Test_category: "network"
+ Benchmark: "iperf"
+ Overview: >
+ '''This test will run the IPERF benchmark on virutalmachine_1 and virtualmachine_2. On the\n
+ same compute node
+ if you wish to add a host machine add the following information under the Host_Machine tag
+ virtualmachine_1:
+ availability_zone: compute1
+ OS_image: QTIP_CentOS
+ public_network: 'net04_ext'
+ role: 1-server
+ flavor: m1.large
+
+ virtualmachine_2:
+ availability_zone: compute2
+ OS_image: QTIP_CentOS
+ public_network: 'net04_ext'
+ role: 2-host
+ flavor: m1.large
+
+ machine_1:
+ ip:
+ pw:
+ role:
+ '''
+
diff --git a/legacy/assets/testplan/default/network/iperf_vm.yaml b/legacy/assets/testplan/default/network/iperf_vm.yaml
new file mode 100644
index 00000000..e42dc0bb
--- /dev/null
+++ b/legacy/assets/testplan/default/network/iperf_vm.yaml
@@ -0,0 +1,51 @@
+##############################################################################
+# Copyright (c) 2017 ZTE Corporation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+Scenario:
+ benchmark: iperf
+ topology: Client and Server on ONE compute
+ server : virtualmachine_1
+ client: virtualmachine_2
+ description: 'Leave the bandwidth as 0 to throttle maximum traffic'
+ benchmark_details:
+ duration: 20
+ protocol: tcp
+ bandwidthGbps: 0
+
+Context:
+ Host_Machines:
+
+ Virtual_Machines:
+ virtualmachine_1:
+ availability_zone: compute1
+ OS_image: QTIP_CentOS
+ public_network: 'net04_ext'
+ role: 1-server
+ flavor: m1.large
+
+ virtualmachine_2:
+ availability_zone: compute1
+ OS_image: QTIP_CentOS
+ public_network: 'net04_ext'
+ role: 2-host
+ flavor: m1.large
+
+Test_Description:
+ Test_category: "network"
+ Benchmark: "iperf"
+ Overview: >
+ '''This test will run the IPERF benchmark on virutalmachine_1 and virtualmachine_2. On the\n
+ same compute node
+ if you wish to add a host machine add the following information under the Host_Machine tag
+
+ machine_1:
+ ip:
+ pw:
+ role:
+ '''
+
diff --git a/legacy/assets/testplan/default/network/iperf_vm_2.yaml b/legacy/assets/testplan/default/network/iperf_vm_2.yaml
new file mode 100644
index 00000000..8a1d1a0d
--- /dev/null
+++ b/legacy/assets/testplan/default/network/iperf_vm_2.yaml
@@ -0,0 +1,52 @@
+##############################################################################
+# Copyright (c) 2017 ZTE Corporation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+Scenario:
+ benchmark: iperf
+ topology: Client and Server on two different compute nodes
+ server : virtualmachine_1
+ client: virtualmachine_2
+ description: 'Leave the bandwidth as 0 to throttle maximum traffic'
+ benchmark_details:
+ duration: 20
+ protocol: tcp
+ bandwidthGbps: 0
+
+Context:
+ Host_Machines:
+
+
+ Virtual_Machines:
+ virtualmachine_1:
+ availability_zone: compute1
+ OS_image: QTIP_CentOS
+ public_network: 'net04_ext'
+ role: 1-server
+ flavor: m1.large
+
+ virtualmachine_2:
+ availability_zone: compute2
+ OS_image: QTIP_CentOS
+ public_network: 'net04_ext'
+ role: 2-host
+ flavor: m1.large
+
+Test_Description:
+ Test_category: "network"
+ Benchmark: "iperf"
+ Overview: >
+ '''This test will run the IPERF benchmark on virutalmachine_1 and virtualmachine_2. On the\n
+ same compute node
+ if you wish to add a host machine add the following information under the Host_Machine tag
+
+ machine_1:
+ ip:
+ pw:
+ role:
+ '''
+
diff --git a/legacy/assets/testplan/default/storage/fio_bm.yaml b/legacy/assets/testplan/default/storage/fio_bm.yaml
new file mode 100644
index 00000000..d226b1af
--- /dev/null
+++ b/legacy/assets/testplan/default/storage/fio_bm.yaml
@@ -0,0 +1,47 @@
+##############################################################################
+# Copyright (c) 2017 ZTE Corporation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+Scenario:
+ benchmark: fio
+ host: machine_1, machine_2
+ server:
+
+Context:
+ Host_Machines:
+ machine_1:
+ ip:
+ pw:
+ role: host
+ machine_2:
+ ip:
+ pw:
+ role: host
+
+
+ Virtual_Machines:
+
+
+Test_Description:
+ Test_category: "Storage"
+ Benchmark: "FIO"
+ Overview: >
+ '''This test will run the FIO benchmark in parallel on host machines "machine_1" and "machine_2".\n
+ The fio job specifications can be found in qtip/benchmarks/fio_jobs/test_job.
+ The job conists of an fio load of:
+ 1.50% rand read 50% rand write
+ 2.Asynch engine
+ 3.Direct IO.
+ 4.Queing depth of 2
+
+ if you wish to add another machine add the following information under the Host_Machines tag
+ machine_3:
+ ip: 172.18.0.16
+ pw: Op3nStack
+ role: host
+ '''
+
diff --git a/legacy/assets/testplan/default/storage/fio_vm.yaml b/legacy/assets/testplan/default/storage/fio_vm.yaml
new file mode 100644
index 00000000..b1cf3142
--- /dev/null
+++ b/legacy/assets/testplan/default/storage/fio_vm.yaml
@@ -0,0 +1,52 @@
+##############################################################################
+# Copyright (c) 2017 ZTE Corporation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+Scenario:
+ benchmark: fio
+ host: virtualmachine_1, virtualmachine_2
+ server:
+
+Context:
+ Host_Machines:
+
+ Virtual_Machines:
+ virtualmachine_1:
+ availability_zone: compute1
+ public_network: 'net04_ext'
+ OS_image: QTIP_CentOS
+ flavor: m1.large
+ role: host
+ virtualmachine_2:
+ availability_zone: compute2
+ public_network: 'net04_ext'
+ OS_image: QTIP_CentOS
+ flavor: m1.large
+ role: host
+
+Test_Description:
+ Test_category: "Storage"
+ Benchmark: "FIO"
+ Overview: >
+ '''This test will run the FIO benchmark in parallel on virtualmachine_1 and virtualmachine_2.\n
+ The fio job specifications can be found in qtip/benchmarks/fio_jobs/test_job.
+ The job conists of an fio load of:
+ 1.50% rand read 50% rand write
+ 2.Asynch engine
+ 3.Direct IO.
+ 4.Queing depth of 2
+
+ if you wish to add a virtual machine add the following information under the Virtual_Machine tag
+
+ virtualmachine_3:
+ availability_zone:
+ public_network:
+ OS_image:
+ flavor:
+ role:
+ '''
+
diff --git a/legacy/docs/_01-compute.rst b/legacy/docs/_01-compute.rst
new file mode 100644
index 00000000..56be5488
--- /dev/null
+++ b/legacy/docs/_01-compute.rst
@@ -0,0 +1,104 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. http://creativecommons.org/licenses/by/4.0
+.. (c) 2015 Dell Inc.
+.. (c) 2016 ZTE Corp.
+
+
+Compute Suite
+=============
+
+Introduction
+------------
+
+The QTIP testing suite aims to benchmark the compute components of an OPNFV platform.
+Such components include, the CPU performance, the memory performance.
+Additionally virtual computing performance provided by the Hypervisor (KVM) installed as part of OPNFV platforms would be benchmarked too.
+
+The test suite consists of both synthetic and application specific benchmarks to test compute components.
+
+All the compute benchmarks could be run in 2 scenarios:
+
+1. On Baremetal Machines provisioned by an OPNFV installer (Host machines)
+2. On Virtual Machines brought up through OpenStack on an OPNFV platform
+
+Note: The Compute benchmank suite constains relatively old benchmarks such as dhrystone and whetstone. The suite would be updated for better benchmarks such as Linbench for the OPNFV C release.
+
+Benchmarks
+----------
+
+The benchmarks include:
+
+Dhrystone 2.1
+^^^^^^^^^^^^^^^^
+
+Dhrystone is a synthetic benchmark for measuring CPU performance. It uses integer calculations to evaluate CPU capabilities.
+Both Single CPU performance is measured along multi-cpu performance.
+
+
+Dhrystone, however, is a dated benchmark and has some short comings.
+Written in C, it is a small program that doesn't test the CPU memory subsystem.
+Additionally, dhrystone results could be modified by optimizing the compiler and insome cases hardware configuration.
+
+References: http://www.eembc.org/techlit/datasheets/dhrystone_wp.pdf
+
+Whetstone
+^^^^^^^^^^^^
+
+Whetstone is a synthetic benchmark to measure CPU floating point operation performance.
+Both Single CPU performance is measured along multi-cpu performance.
+
+Like Dhrystone, Whetstone is a dated benchmark and has short comings.
+
+References:
+
+http://www.netlib.org/benchmark/whetstone.c
+
+OpenSSL Speed
+^^^^^^^^^^^^^^^^
+
+OpenSSL Speed can be used to benchmark compute performance of a machine. In QTIP, two OpenSSL Speed benchmarks are incorporated:
+1. RSA signatunes/sec signed by a machine
+2. AES 128-bit encryption throughput for a machine for cipher block sizes
+
+References:
+
+https://www.openssl.org/docs/manmaster/apps/speed.html
+
+RAMSpeed
+^^^^^^^^
+
+RAMSpeed is used to measure a machine's memory perfomace.
+The problem(array)size is large enough to ensure Cache Misses so that the main machine memory is used.
+INTmem and FLOATmem benchmarks are executed in 4 different scenarios:
+
+a. Copy: a(i)=b(i)
+b. Add: a(i)=b(i)+c(i)
+c. Scale: a(i)=b(i)*d
+d. Tniad: a(i)=b(i)+c(i)*d
+
+INTmem uses integers in these four benchmarks whereas FLOATmem uses floating points for these benchmarks.
+
+References:
+
+http://alasir.com/software/ramspeed/
+
+https://www.ibm.com/developerworks/community/wikis/home?lang=en#!/wiki/W51a7ffcf4dfd_4b40_9d82_446ebc23c550/page/Untangling+memory+access+measurements
+
+DPI
+^^^
+
+nDPI is a modified variant of OpenDPI, Open source Deep packet Inspection, that is maintained by ntop.
+An example application called *pcapreader* has been developed and is available for use along nDPI.
+
+A sample .pcap file is passed to the *pcapreader* application.
+nDPI classifies traffic in the pcap file into different categories based on string matching.
+The *pcapreader* application provides a throughput number for the rate at which traffic was classified, indicating a machine's computational performance.
+The results are run 10 times and an average is taken for the obtained number.
+
+*nDPI may provide non consistent results and was added to Brahmaputra for experimental purposes*
+
+References:
+
+http://www.ntop.org/products/deep-packet-inspection/ndpi/
+
+http://www.ntop.org/wp-content/uploads/2013/12/nDPI_QuickStartGuide.pdf
diff --git a/legacy/docs/_02-network.rst b/legacy/docs/_02-network.rst
new file mode 100644
index 00000000..00fe5b0a
--- /dev/null
+++ b/legacy/docs/_02-network.rst
@@ -0,0 +1,61 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. http://creativecommons.org/licenses/by/4.0
+.. (c) 2015 Dell Inc.
+.. (c) 2016 ZTE Corp.
+
+
+Network Suite
+=============
+
+QTIP uses IPerf3 as the main tool for testing the network throughput.
+There are three tests that are run through the QTIP framework.
+
+**1. Network throughput between two compute nodes**
+
+**2. Network Throughput between two VMs on the same compute node**
+
+**3. Network Throughput between two VMs on different compute nodes**
+
+
+Network throughput between two compute nodes
+-----------------------------------------------
+
+For the throughput between two compute nodes, Iperf3 is installed on the compute nodes comprising the systems-under-test.
+One of the compute nodes is used as a server and the other as a client.
+The client pushes traffic to the server for a duration specified by the user in the configuration file for Iperf3.
+
+
+These files can be found in the "benchmarks/testplan/{POD}/network/" directory.
+The bandwidth is limited by the physical link layer speed connecting the two compute nodes.
+The result file includes the b/s bandwidth and the CPU usage for both the client and server.
+
+Network throughput between two VMs on the same compute node
+--------------------------------------------------------------
+
+QTIP framework sets up a stack with a private network, security groups, routers and attaches two VMs to this network.
+Iperf3 is installed on the VMs and one is assigned the role of client while the other VM serves as a server.
+Traffic is pushed over the QTIP private network between the two VMs.
+A closer look is needed to see how the traffic actually flows between the VMs in this configuration to understand what is happening to the packet as it traverses the OpenStack virtual network.
+
+The packet originates from VM1 and its sent to the Linux bridge via a tap interface where the security groups are written.
+Afterwards the packet is forwarded to the Integration bridge (br-int) via a patch port.
+Since VM2 is also connected to the Integration bridge in a similar manner as VM1, the packet gets forwarded to the linux bridge connecting VM2.
+After the Linux bridge the packet is sent to VM2 and is received by the Iperf3 server.
+Since no physical link is involved in this topology, only the OVS (Integration bridge) (br-int) is being benchmarked.
+
+
+Network throughput between two VMs on different compute nodes
+--------------------------------------------------------------
+
+
+As in case 2, QTIP framework sets up a stack with a private network, security groups, routers, and two VMs which are attached to the created network. However, the two VMs are spawned up on different compute nodes.
+
+Since the VMs are spawned on different nodes, the traffic involves additional paths.
+
+The traffic packet leaves the client VM and makes its way to the Integration Bridge (br-int) as in the previous case through a linux bridge and a patch port.
+The integration bridge (br-int) forwards the packet to the the tunneling bridge (br-tun) where the packet is encapsulated based on the tunneling protocol used (GRE/VxLAN).
+The packet then moves onto the physical link through the ethernet bridge (br-eth).
+
+On the receiving compute node, the packet arrives at ethernet bridge(br-eth) through the physical link.
+This packet then moves to the tunneling bridge (br-tun) where the packet is decapsulated.
+The packet then moves onto the internal bridge (br-int) and finally moves through a patch port into the linux bridge and eventually to the VM where it is received by the Iperf server application.
diff --git a/legacy/docs/_03-storage.rst b/legacy/docs/_03-storage.rst
new file mode 100644
index 00000000..b1490432
--- /dev/null
+++ b/legacy/docs/_03-storage.rst
@@ -0,0 +1,31 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. http://creativecommons.org/licenses/by/4.0
+.. (c) 2015 Dell Inc.
+.. (c) 2016 ZTE Corp.
+
+
+Storage Suite
+=============
+
+The QTIP benchmark suite aims to evaluate storage components within an OPNFV platform.
+For Brahamaputra release, FIO would evaluate File System performance for the host machine.
+It will also test the I/O performance provided by the hypervisor(KVM) when Storage benchmarks are run inside VMs.
+
+QTIP storage test cases consist of:
+
+**1. FIO Job to benchmark baremetal file system performance**
+
+**2. FIO Job to bechmark virtual machine file system performance**
+
+**Note: For Brahmaputra release, only the Ephemeral Storage is being tested. For C release persistent block and object storage would be tested.**
+
+The FIO Job would consist of:
+
+1. A file size of 5GB
+2. Random Read 50%, Random Write 50%
+3. Direct I/O
+4. Asynch I/O Engine
+5. I/O Queue depth of 2
+6. Block size :4K
+
+For this Job, I/O per second would be measured along mean I/O latency to provide storage performance numbers.
diff --git a/legacy/docs/annex.rst b/legacy/docs/annex.rst
new file mode 100644
index 00000000..e8bf5555
--- /dev/null
+++ b/legacy/docs/annex.rst
@@ -0,0 +1,18 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. http://creativecommons.org/licenses/by/4.0
+.. (c) 2016 ZTE Corp.
+
+
+*****
+Annex
+*****
+
+.. toctree::
+ :maxdepth: 2
+
+ _testcase_description.rst
+
+Downloads
+=========
+
+- :download:`Sample configuration <../download/sample_config.yaml>`
diff --git a/legacy/docs/apidocs/qtip_restful_api.rst b/legacy/docs/apidocs/qtip_restful_api.rst
new file mode 100644
index 00000000..7e48b95b
--- /dev/null
+++ b/legacy/docs/apidocs/qtip_restful_api.rst
@@ -0,0 +1,10 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. http://creativecommons.org/licenses/by/4.0
+.. (c) 2015 Dell Inc.
+.. (c) 2016 ZTE Corp.
+
+****************
+QTIP restful api
+****************
+
+You can get all the QTIP restful api by http://restful_api.qtip.openzero.net/api/spec.html.
diff --git a/legacy/docs/benchmark-suites.rst b/legacy/docs/benchmark-suites.rst
new file mode 100644
index 00000000..84d1c647
--- /dev/null
+++ b/legacy/docs/benchmark-suites.rst
@@ -0,0 +1,15 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. http://creativecommons.org/licenses/by/4.0
+.. (c) 2016 ZTE Corp.
+
+
+****************
+Benchmark Suites
+****************
+
+.. toctree::
+ :maxdepth: 2
+
+ _01-compute.rst
+ _02-network.rst
+ _03-storage.rst
diff --git a/legacy/docs/download/sample_config.yaml b/legacy/docs/download/sample_config.yaml
new file mode 100644
index 00000000..72c16bf4
--- /dev/null
+++ b/legacy/docs/download/sample_config.yaml
@@ -0,0 +1,58 @@
+##############################################################################
+# Copyright (c) 2016 ZTE Corporation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+ ## This is a sample Config.yaml file
+
+#Scenario would define the test case scenario.
+#The benchmark key contains the benchmark to run such as dhrystone,whetstone,dpi,ssh etc.
+# Host and server list the different machines on which the benchmark would run.
+# On machines listed within hosts, the benchmarks would run in parallel.
+# On machines listed within server, the benchmarks would run when the benechmarks running on hosts have been completed.
+# This has been used to control the folow of the testcase. For example, running the testcases on a vm vs hostmachin, we would like to run the test case serially. It should run first on the host machine and then on the vm. This testcase flow control could be used for other testcases to be developed such as those for networking.
+Scenario:
+ benchmark: dhrystone
+ host: machine_1, machine_2, virtualmachine_1
+ server:
+
+
+#Context would define the environment on which to run:
+#Host Machine keys would contain Host_Machines/ Baremetal machines to run the benchmarks on
+#e.g in Host Machine , machine_1 and machine_2 are the bare metal machines. For each baremetal machine its IP(which should be reachable from the location on which you run QTIP), passwords and its role(host or server). If your installer is 'fuel' or 'compass' and you left baremetal machine IP empty,qtip will get compute node ip from installer automaticly.
+Context:
+ Host_Machines:
+ machine_1:
+ ip: 172.18.0.16
+ pw: Op3nStack
+ role: host
+ Virtual_Machines:
+ virtualmachine_1:
+ availability_zone: nova
+ public_network: 'net04_ext'
+ OS_image: QTIP_CentOS
+ flavor: m1.large
+ role: server
+#Proxy_Environment key was optional.If all the Machines could access the public network, no need to define Proxy_Environment.Could be used later for getting http,https proxy infos which would be setted on all the Host_Manchines and Virtual_Machines.
+ Proxy_Environment:
+ http_proxy: http://10.20.0.1:8118
+ https_proxy: http://10.20.0.1:8118
+ no_proxy: localhost,127.0.0.1,10.20.*,192.168.*
+
+# A general description of the testcase. Could be used later for reports.
+Test_Description:
+ Test_category: "Compute"
+ Benchmark: "dhrystone"
+ Overview: >
+ ''' This test will run the dhrystone benchmark in serial on machine_1 and machine_2.\n
+ if you wish to add a virtual machine add the following information under the Virtual_Machine tag
+
+ virtualmachine_1:
+ availability_zone:
+ public_network:
+ OS_image:
+ flavor:
+ role: '''
diff --git a/legacy/docs/index.rst b/legacy/docs/index.rst
new file mode 100644
index 00000000..241a2680
--- /dev/null
+++ b/legacy/docs/index.rst
@@ -0,0 +1,13 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. http://creativecommons.org/licenses/by/4.0
+.. (c) 2015 Dell Inc.
+.. (c) 2016 ZTE Corp.
+
+################
+QTIP Configguide
+################
+
+.. toctree::
+ :maxdepth: 2
+
+ ./qtip_restful_api.rst
diff --git a/legacy/docs/introduction.rst b/legacy/docs/introduction.rst
new file mode 100644
index 00000000..3147f0aa
--- /dev/null
+++ b/legacy/docs/introduction.rst
@@ -0,0 +1,381 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. http://creativecommons.org/licenses/by/4.0
+.. (c) 2015 Dell Inc.
+.. (c) 2016 ZTE Corp.
+
+
+************
+Introduction
+************
+
+This guide will serve as a first step to familiarize the user with how to
+run QTIP the first time when the user pull QTIP image on to their host machine.
+In order to install and config QTIP please follow the instructions in the
+configuration.rst located in docs/configguide/configuration.rst.
+
+QTIP Directory structure
+========================
+
+The QTIP directory has been sectioned off into multiple folders to facilitate
+ segmenting information into relevant categories. The folders that concern
+ the end user are `benchmarks/testplan/` and `benchmarks/suite/`.
+
+**testplan/:**
+
+This folder is used to store all the config files which are used to setup the
+environment prior to a test. This folder is further divided into opnfv pods
+which run QTIP. Inside each pod there are folders which contain the config files
+segmented based on test cases. Namely, these include, `Compute`, `Network` and
+`Storage`. The default folder is there for the end user who is interested in
+testing their infrastructure which is installed by fuel or compass but aren't
+part of a opnfv pod,and for opnfv CI.
+
+The structure of the directory for the user appears as follows
+::
+
+ testplan/default/compute
+ testplan/default/network
+ testplan/default/storage
+
+The benchmarks that are part of the QTIP framework are listed under these
+folders. An example of the compute folder is shown below.
+Their naming convention is <BENCHMARK>_<VM/BM>.yaml
+::
+
+ dhrystone_bm.yaml
+ dhrystone_vm.yaml
+ whetstone_vm.yaml
+ whetstone_bm.yaml
+ ssl_vm.yaml
+ ssl_bm.yaml
+ ramspeed_vm.yaml
+ ramspeed_bm.yaml
+ dpi_vm.yaml
+ dpi_bm.yaml
+
+The above listed files are used to configure the environment. The VM/BM tag
+distinguishes between a test to be run on the Virtual Machine or the compute
+node itself, respectively.
+
+
+**benchmarks/suite/:**
+
+This folder contains three files, namely `compute`, `network` and `storage`.
+These files list the benchmarks are to be run by the QTIP framework. Sample
+compute test file is shown below
+::
+
+ {
+ "bm": [
+ "dhrystone_bm.yaml",
+ "whetstone_bm.yaml",
+ "ramspeed_bm.yaml",
+ "dpi_bm.yaml",
+ "ssl_bm.yaml"
+ ],
+ "vm": [
+ "dhrystone_vm.yaml",
+ "whetstone_vm.yaml",
+ "ramspeed_vm.yaml",
+ "dpi_vm.yaml",
+ "ssl_vm.yaml"
+ ]
+ }
+
+The compute file will now run all the benchmarks listed above one after
+another on the environment.
+
+Preparing a config file for test:
+---------------------------------
+
+We will be using dhrystone as a example to list out the changes that the
+user will need to do in order to run the benchmark.
+
+Dhrystone on Compute Nodes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+QTIP framework can run benchmarks on the actual compute nodes as well. In
+order to run dhrystone on the compute nodes we will be editing the
+dhrystone_bm.yaml file.
+
+::
+
+ Scenario:
+ benchmark: dhrystone
+ host: machine_1, machine_2
+ server:
+
+The `Scenario` field is used by to specify the name of the benchmark to
+run as done by `benchmark: dhrystone`. The `host` and `server` tag are
+not used for the compute benchmarks but are included here to help the
+user `IF` they wish to control the execution. By default both machine_1
+and machine_2 will have dhrystone run on them in parallel but the user
+can change this so that machine_1 run dhrystone before machine_2. This
+will be elaborated in the `Context` tag.
+
+::
+
+ Context:
+ Host_Machines:
+ machine_1:
+ ip: 10.20.0.6
+ pw:
+ role: host
+ machine_2:
+ ip: 10.20.0.5
+ pw:
+ role: host
+
+ Virtual_Machines:
+
+The `Context` tag helps the user list the number of compute nodes they want
+ to run dhrystone on. The user can list all the compute nodes under the
+ `Host_Machines` tag. All the machines under test must be listed under the
+ `Host_Machines` and naming it incrementally higher. The `ip:` tag is used
+ to specify the IP of the particular compute node.The `ip:` tag can be left
+ blank when installer type is 'fuel',because QTIP will get ip
+ from installer. The `pw:` tag can be left blank because QTIP uses its own
+ key for ssh. In order to run dhrystone on one compute node at a time the user
+ needs to edit the `role:` tag. `role: host` for machine_1 and `role: server`
+ for machine_2 will allow for dhrystone to be run on machine_1 and then run
+ on machine_2.
+
+::
+
+
+ Test_Description:
+ Test_category: "Compute"
+ Benchmark: "dhrystone"
+ Overview: >
+ ''' This test will run the dhrystone benchmark in parallel on
+ machine_1 and machine_2.
+
+The above field is purely for a description purpose to explain to the user
+the working of the test and is not fed to the framework.
+
+Sample dhrystone_bm.yaml file:
+------------------------------
+::
+
+ Scenario:
+ benchmark: dhrystone
+ host: machine_1, machine_2
+ server:
+
+ Context:
+ Host_Machines:
+ machine_1:
+ ip: 10.20.0.6
+ pw:
+ role: host
+ machine_2:
+ ip: 10.20.0.5
+ pw:
+ role: host
+
+ Virtual_Machines:
+
+
+ Test_Description:
+ Test_category: "Compute"
+ Benchmark: "dhrystone"
+ Overview: >
+ ''' This test will run the dhrystone benchmark in parallel on
+ machine_1 and machine_2.\n
+
+Dhrystone on Virtual Machine:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+To run dhrystone on the VMs we will be editing dhrystone_vm.yaml file.
+Snippets on the file are given below.
+
+::
+
+ Scenario:
+ benchmark: dhrystone
+ host: virtualmachine_1, virtualmachine_2
+ server:
+
+
+The `Scenario` field is used by to specify the name of the benchmark to
+run as done by `benchmark: dhrystone`. The `host` and `server` tag are
+not used for the compute benchmarks but are included here to help the
+user `IF` they wish to control the execution. By default both
+virtualmachine_1 and virtualmachine_2 will have dhrystone run on them
+in parallel but the user can change this so that virtualmachine_1 run
+dhrystone before virtualmachine_2. This will be elaborated in the
+`Context` tag.
+::
+
+ Context:
+ Host_Machines:
+
+ Virtual_Machines:
+ virtualmachine_1:
+ availability_zone: compute1
+ public_network: 'net04_ext'
+ OS_image: QTIP_CentOS
+ flavor: m1.large
+ role: host
+ virtualmachine_2:
+ availability_zone: compute2
+ public_network: 'net04_ext'
+ OS_image: QTIP_CentOS
+ flavor: m1.large
+ role: host
+
+The `Context` tag helps the user list the number of VMs and their
+characteristic. The user can list all the VMs they want to bring up
+under the `Virtual_Machines:` tag. In the above example we will be
+bringing up two VMs. One on Compute1 and the other on Compute2. The
+user can change this as desired `NOTE: Please ensure you have the
+necessary compute nodes before listing under the 'availability_zone:'
+tag`. The rest of the options do not need to be modified by the user.
+
+Running dhrystone sequentially (Optional):
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In order to run dhrystone on one VM at a time the user needs to edit
+the `role:` tag. `role: host` for virtualmachine_1 and `role: server`
+for virtualmachine_2 will allow for dhrystone to be run on
+virtualmachine_1 and then run on virtualmachine_2.
+
+::
+
+ Test_Description:
+ Test_category: "Compute"
+ Benchmark: "dhrystone"
+ Overview:
+ This test will run the dhrystone benchmark in parallel on
+ virtualmachine_1 and virtualmachine_2
+
+The above field is purely for a decription purpose to explain to
+the user the working of the test and is not fed to the framework.
+
+Running dhrystone with proxy (Optional):
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In order to run the dhrystone on the hosts or vms which can only access the
+public network by proxy, the user needs to add the `Proxy_Environment` info
+in `Context` tag.
+
+::
+
+ Context:
+ Host_Machines:
+ machine_1:
+ ip: 10.20.0.29
+ pw:
+ role: host
+ machine_2:
+ ip: 10.20.0.30
+ pw:
+ role: host
+
+ Virtual_Machines:
+
+ Proxy_Environment:
+ http_proxy: http://10.20.0.1:8118
+ https_proxy: http://10.20.0.1:8118
+ no_proxy: localhost,127.0.0.1,10.20.*,192.168.*
+
+Sample dhrystone_vm.yaml file:
+------------------------------
+::
+
+ Scenario:
+ benchmark: dhrystone
+ host: virtualmachine_1, virtualmachine_2
+ server:
+
+ Context:
+ Host_Machines:
+
+ Virtual_Machines:
+ virtualmachine_1:
+ availability_zone: compute1
+ public_network: 'net04_ext'
+ OS_image: QTIP_CentOS
+ flavor: m1.large
+ role: host
+ virtualmachine_2:
+ availability_zone: compute2
+ public_network: 'net04_ext'
+ OS_image: QTIP_CentOS
+ flavor: m1.large
+ role: host
+
+ Test_Description:
+ Test_category: "Compute"
+ Benchmark: "dhrystone"
+ Overview: >
+ This test will run the dhrystone benchmark in parallel on
+ machine_1 and machine_2.\n
+
+Commands to run the Framework:
+------------------------------
+
+In order to start QTIP on the default lab please use the following commands (asssuming your installer
+is 'fuel' or 'compass', you use the config files in the benchmarks/testplan/default/ directory and listed the
+intended suite in the benchmarks/suite/<RELEVANT-SUITE-FILE>):
+
+First step is to export the necessary information to the environment and generate QTIP key pair.
+Please follow the instructions in the configuration.rst.
+
+Secondary step download the QTIP image and upload it to the Cloud.QTIP will use this image
+to create VM when test VM performance.
+::
+
+ source docker/prepare_qtip_image.sh
+
+Running QTIP on the using `default` as the pod name and for the `compute` suite by cli.
+::
+
+ python qtip.py -l default -f compute
+
+Running QTIP on the using 'default' as the pod name and for the 'compute' suite 'bm' type by restful api.
+::
+
+ curl --trace-ascii debug.txt -X POST -d '{ "installer_ip": "10.20.6.2","installer_type":"fuel", "suite_name":"compute", "type": "BM"}' -H "Content-Type: application/json" http://127.0.0.1:5000/api/v1.0/jobs
+
+Running QTIP on the using 'default' as the pod name and for the 'compute' suite 'vm' type by restful api.
+::
+
+ curl --trace-ascii debug.txt -X POST -d '{ "installer_ip": "10.20.6.2","installer_type":"fuel", "suite_name":"compute", "type": "VM"}' -H "Content-Type: application/json" http://127.0.0.1:5000/api/v1.0/jobs
+
+Running QTIP on the using `default` as the pod name and for the `network` suite by cli.
+::
+
+ python qtip.py -l default -f network
+
+Running QTIP on the using 'default' as the pod name and for the 'network' suite 'bm' type by restful api.
+::
+
+ curl --trace-ascii debug.txt -X POST -d '{ "installer_ip": "10.20.6.2","installer_type":"fuel", "suite_name":"network", "type": "BM"}' -H "Content-Type: application/json" http://127.0.0.1:5000/api/v1.0/jobs
+
+Running QTIP on the using `default` as the pod name and for the `storage` suite by cli.
+::
+
+ python qtip.py -l default -f network
+
+Running QTIP on the using 'default' as the pod name and for the 'storage' suite 'bm' type by restful api.
+::
+
+ curl --trace-ascii debug.txt -X POST -d '{ "installer_ip": "10.20.6.2","installer_type":"fuel", "suite_name":"storage", "type": "BM"}' -H "Content-Type: application/json" http://127.0.0.1:5000/api/v1.0/jobs
+
+Get running QTIP job status by restful api
+::
+
+ curl --trace-ascii debug.txt -X GET http://127.0.0.1:5000/api/v1.0/jobs/job-id
+ For example:
+ curl --trace-ascii debug.txt -X GET http://127.0.0.1:5000/api/v1.0/jobs/5b71f035-3fd6-425c-9cc7-86acd3a04214
+
+Stop running QTIP job by restful api.The job will finish the current benchmark test and stop.
+::
+
+ curl --trace-ascii debug.txt -X DELTET http://127.0.0.1:5000/api/v1.0/jobs/job-id
+ For example:
+ curl --trace-ascii debug.txt -X DELETE http://127.0.0.1:5000/api/v1.0/jobs/5b71f035-3fd6-425c-9cc7-86acd3a04214q
+
+Results:
+--------
+In QTIP container, QTIP generates results in the `/home/opnfv/qtip/results/` directory are listed down under the particularly benchmark name. So all the results for dhrystone would be listed and time stamped.
diff --git a/legacy/docs/overview/index.rst b/legacy/docs/overview/index.rst
new file mode 100644
index 00000000..9a387360
--- /dev/null
+++ b/legacy/docs/overview/index.rst
@@ -0,0 +1,14 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. http://creativecommons.org/licenses/by/4.0
+.. (c) 2016 ZTE Corp.
+
+
+
+#####################
+QTIP Project Overview
+#####################
+
+.. toctree::
+ :maxdepth: 2
+
+ ./overview.rst
diff --git a/legacy/docs/overview/overview.rst b/legacy/docs/overview/overview.rst
new file mode 100644
index 00000000..4fd42356
--- /dev/null
+++ b/legacy/docs/overview/overview.rst
@@ -0,0 +1,21 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. http://creativecommons.org/licenses/by/4.0
+.. (c) 2015 Dell Inc.
+.. (c) 2016 ZTE Corp.
+
+********
+Overview
+********
+
+.. _QTIP: https://wiki.opnfv.org/platform_performance_benchmarking
+
+QTIP_ is an OPNFV Project.
+
+QTIP aims to benchmark OPNFV platforms through a "Bottom up" approach, testing
+bare-metal components first.
+
+The overall problem this project tries to solve is the general
+characterization of an OPNFV platform. It will focus on general performance
+questions that are common to the platform itself, or applicable to multiple
+OPNFV use cases. QTIP will provide the capability to quantify a platform's
+performance behavior in a standardized, rigorous, and open way.
diff --git a/legacy/driver/playbook/bwn_ng.yaml b/legacy/driver/playbook/bwn_ng.yaml
new file mode 100644
index 00000000..c52cb14e
--- /dev/null
+++ b/legacy/driver/playbook/bwn_ng.yaml
@@ -0,0 +1,25 @@
+##############################################################################
+# Copyright (c) 2017 ZTE Corporation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+- name: Install bwm-ng when CentOS
+ yum:
+ name: bwm-ng
+ state: present
+ when: ansible_os_family == "RedHat"
+
+- name: Install bwm-ng when Ubuntu
+ apt:
+ name: bwm-ng
+ state: present
+ update_cache: yes
+ when: ansible_os_family == "Debian"
+
+- name: Run bwm-ng
+ shell: bwm-ng -o plain -c 1 > bwm-dump.log
+ args:
+ chdir: '{{ dest_path }}' \ No newline at end of file
diff --git a/legacy/driver/playbook/top.yaml b/legacy/driver/playbook/top.yaml
new file mode 100644
index 00000000..dfa0aff2
--- /dev/null
+++ b/legacy/driver/playbook/top.yaml
@@ -0,0 +1,12 @@
+##############################################################################
+# Copyright (c) 2017 ZTE Corporation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+- name: Collect cpu usage
+ shell: top -bn1 > top.log
+ args:
+ chdir: '{{ dest_path }}'
diff --git a/legacy/scripts/__init__.py b/legacy/scripts/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/legacy/scripts/__init__.py
diff --git a/legacy/scripts/ref_results/__init__.py b/legacy/scripts/ref_results/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/legacy/scripts/ref_results/__init__.py
diff --git a/legacy/scripts/ref_results/compute_benchmarks_indices.py b/legacy/scripts/ref_results/compute_benchmarks_indices.py
new file mode 100644
index 00000000..936b58df
--- /dev/null
+++ b/legacy/scripts/ref_results/compute_benchmarks_indices.py
@@ -0,0 +1,168 @@
+##############################################################################
+# Copyright (c) 2017 ZTE Corporation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+from index_calculation import generic_index as get_index
+from index_calculation import get_reference
+from result_accum import result_concat as concat
+
+
+def dpi_index():
+ dpi_dict = concat('results/dpi/')
+ dpi_bm_ref = get_reference('compute', 'dpi_bm')
+ dpi_bm_index = get_index(dpi_dict, 'dpi_bm', dpi_bm_ref, 'details', 'bps')
+
+ dpi_vm_ref = get_reference('compute', 'dpi_vm')
+ dpi_vm_index = get_index(dpi_dict, 'dpi_vm', dpi_vm_ref, 'details', 'bps')
+ dpi_index = (dpi_bm_index + dpi_vm_index) / 2
+ dpi_dict_i = {}
+ dpi_dict_i['index'] = dpi_index
+ dpi_dict_i['results'] = dpi_dict
+ return dpi_dict_i
+
+
+def dhrystone_index():
+
+ dhrystone_dict = concat('results/dhrystone/')
+ dhrystone_single_bm_ref = get_reference('compute', 'dhrystone_bm', 'single_cpu')
+ dhrystone_single_bm_index = get_index(dhrystone_dict, 'dhrystone_bm', dhrystone_single_bm_ref, 'details', 'single', 'score')
+
+ dhrystone_multi_bm_ref = get_reference('compute', 'dhrystone_bm', 'multi_cpu')
+ dhrystone_multi_bm_index = get_index(dhrystone_dict, 'dhrystone_bm', dhrystone_multi_bm_ref, 'details', 'multi', 'score')
+
+ dhrystone_bm_index = (dhrystone_single_bm_index + dhrystone_multi_bm_index) / 2
+
+ dhrystone_single_vm_ref = get_reference('compute', 'dhrystone_vm', 'single_cpu')
+ dhrystone_single_vm_index = get_index(dhrystone_dict, 'dhrystone_vm', dhrystone_single_vm_ref, 'details', 'single', 'score')
+
+ dhrystone_multi_vm_ref = get_reference('compute', 'dhrystone_vm', 'multi_cpu')
+ dhrystone_multi_vm_index = get_index(dhrystone_dict, 'dhrystone_vm', dhrystone_multi_vm_ref, 'details', 'multi', 'score')
+
+ dhrystone_vm_index = (dhrystone_single_vm_index + dhrystone_multi_vm_index) / 2
+
+ dhrystone_index = (dhrystone_bm_index + dhrystone_vm_index) / 2
+ dhrystone_dict_i = {}
+ dhrystone_dict_i['index'] = dhrystone_index
+ dhrystone_dict_i['results'] = dhrystone_dict
+ return dhrystone_dict_i
+
+
+def whetstone_index():
+
+ whetstone_dict = concat('results/whetstone/')
+ whetstone_single_bm_ref = get_reference('compute', 'whetstone_bm', 'single_cpu')
+ whetstone_single_bm_index = get_index(whetstone_dict, 'whetstone_bm', whetstone_single_bm_ref, 'details', 'single', 'score')
+
+ whetstone_multi_bm_ref = get_reference('compute', 'whetstone_bm', 'multi_cpu')
+ whetstone_multi_bm_index = get_index(whetstone_dict, 'whetstone_bm', whetstone_multi_bm_ref, 'details', 'multi', 'score')
+
+ whetstone_bm_index = (whetstone_single_bm_index + whetstone_multi_bm_index) / 2
+
+ whetstone_single_vm_ref = get_reference('compute', 'whetstone_vm', 'single_cpu')
+ whetstone_single_vm_index = get_index(whetstone_dict, 'whetstone_vm', whetstone_single_vm_ref, 'details', 'single', 'score')
+
+ whetstone_multi_vm_ref = get_reference('compute', 'whetstone_vm', 'multi_cpu')
+ whetstone_multi_vm_index = get_index(whetstone_dict, 'whetstone_vm', whetstone_multi_vm_ref, 'details', 'multi', 'score')
+
+ whetstone_vm_index = (whetstone_single_vm_index + whetstone_multi_vm_index) / 2
+
+ whetstone_index = (whetstone_bm_index + whetstone_vm_index) / 2
+ whetstone_dict_i = {}
+ whetstone_dict_i['index'] = whetstone_index
+ whetstone_dict_i['results'] = whetstone_dict
+ return whetstone_dict_i
+
+
+def ramspeed_index():
+
+ ramspeed_dict = concat('results/ramspeed/')
+ ramspeed_int_bm_ref = get_reference('compute', 'ramspeed_bm', 'INTmem', 'Average (MB/s)')
+ ramspeed_int_bm_index = get_index(ramspeed_dict, 'ramspeed_bm', ramspeed_int_bm_ref, 'details', 'int_bandwidth', 'average')
+
+ ramspeed_float_bm_ref = get_reference('compute', 'ramspeed_bm', 'FLOATmem', 'Average (MB/s)')
+ ramspeed_float_bm_index = get_index(ramspeed_dict, 'ramspeed_bm', ramspeed_float_bm_ref, 'details', 'float_bandwidth', 'average')
+
+ ramspeed_bm_index = (ramspeed_int_bm_index + ramspeed_float_bm_index) / 2
+
+ ramspeed_int_vm_ref = get_reference('compute', 'ramspeed_vm', 'INTmem', 'Average (MB/s)')
+ ramspeed_int_vm_index = get_index(ramspeed_dict, 'ramspeed_vm', ramspeed_int_vm_ref, 'details', 'int_bandwidth', 'average')
+
+ ramspeed_float_vm_ref = get_reference('compute', 'ramspeed_vm', 'FLOATmem', 'Average (MB/s)')
+ ramspeed_float_vm_index = get_index(ramspeed_dict, 'ramspeed_vm', ramspeed_float_vm_ref, 'details', 'float_bandwidth', 'average')
+
+ ramspeed_vm_index = (ramspeed_int_vm_index + ramspeed_float_vm_index) / 2
+
+ ramspeed_index = (ramspeed_vm_index + ramspeed_bm_index) / 2
+
+ ramspeed_dict_i = {}
+ ramspeed_dict_i['index'] = ramspeed_index
+ ramspeed_dict_i['results'] = ramspeed_dict
+ return ramspeed_dict_i
+
+
+def ssl_index():
+
+ ssl_dict = concat('results/ssl/')
+
+ ssl_RSA512b_bm_ref = get_reference('compute', 'ssl_bm', 'RSA', '512b')
+ ssl_RSA1024b_bm_ref = get_reference('compute', 'ssl_bm', 'RSA', '1024b')
+ ssl_RSA2048b_bm_ref = get_reference('compute', 'ssl_bm', 'RSA', '2048b')
+ ssl_RSA4096b_bm_ref = get_reference('compute', 'ssl_bm', 'RSA', '4096b')
+
+ ssl_AES16B_bm_ref = get_reference('compute', 'ssl_bm', 'AES', '16B')
+ ssl_AES64B_bm_ref = get_reference('compute', 'ssl_bm', 'AES', '64B')
+ ssl_AES256B_bm_ref = get_reference('compute', 'ssl_bm', 'AES', '256B')
+ ssl_AES1024B_bm_ref = get_reference('compute', 'ssl_bm', 'AES', '1024B')
+ ssl_AES8192B_bm_ref = get_reference('compute', 'ssl_bm', 'AES', '8192B')
+
+ ssl_RSA512b_bm_index = get_index(ssl_dict, "ssl_bm", ssl_RSA512b_bm_ref, 'details', 'rsa_sig', '512_bits')
+ ssl_RSA1024b_bm_index = get_index(ssl_dict, "ssl_bm", ssl_RSA1024b_bm_ref, 'details', 'rsa_sig', '1024_bits')
+ ssl_RSA2048b_bm_index = get_index(ssl_dict, "ssl_bm", ssl_RSA2048b_bm_ref, 'details', 'rsa_sig', '2048_bits')
+ ssl_RSA4096b_bm_index = get_index(ssl_dict, "ssl_bm", ssl_RSA4096b_bm_ref, 'details', 'rsa_sig', '4096_bits')
+ ssl_RSA_bm_index = (ssl_RSA512b_bm_index + ssl_RSA1024b_bm_index + ssl_RSA2048b_bm_index + ssl_RSA4096b_bm_index) / 4
+
+ ssl_AES16B_bm_index = get_index(ssl_dict, "ssl_bm", ssl_AES16B_bm_ref, 'details', 'aes_128_cbc', '16B_block')
+ ssl_AES64B_bm_index = get_index(ssl_dict, "ssl_bm", ssl_AES64B_bm_ref, 'details', 'aes_128_cbc', '64B_block')
+ ssl_AES256B_bm_index = get_index(ssl_dict, "ssl_bm", ssl_AES256B_bm_ref, 'details', 'aes_128_cbc', '256B_block')
+ ssl_AES1024B_bm_index = get_index(ssl_dict, "ssl_bm", ssl_AES1024B_bm_ref, 'details', 'aes_128_cbc', '1024B_block')
+ ssl_AES8192B_bm_index = get_index(ssl_dict, "ssl_bm", ssl_AES8192B_bm_ref, 'details', 'aes_128_cbc', '8192B_block')
+ ssl_AES_bm_index = (ssl_AES16B_bm_index + ssl_AES64B_bm_index + ssl_AES256B_bm_index + ssl_AES1024B_bm_index + ssl_AES8192B_bm_index) / 5
+
+ ssl_bm_index = (ssl_RSA_bm_index + ssl_AES_bm_index) / 2
+
+ ssl_RSA512b_vm_ref = get_reference('compute', 'ssl_vm', 'RSA', '512b')
+ ssl_RSA1024b_vm_ref = get_reference('compute', 'ssl_vm', 'RSA', '1024b')
+ ssl_RSA2048b_vm_ref = get_reference('compute', 'ssl_vm', 'RSA', '2048b')
+ ssl_RSA4096b_vm_ref = get_reference('compute', 'ssl_vm', 'RSA', '4096b')
+
+ ssl_AES16B_vm_ref = get_reference('compute', 'ssl_vm', 'AES', '16B')
+ ssl_AES64B_vm_ref = get_reference('compute', 'ssl_vm', 'AES', '64B')
+ ssl_AES256B_vm_ref = get_reference('compute', 'ssl_vm', 'AES', '256B')
+ ssl_AES1024B_vm_ref = get_reference('compute', 'ssl_vm', 'AES', '1024B')
+ ssl_AES8192B_vm_ref = get_reference('compute', 'ssl_vm', 'AES', '8192B')
+
+ ssl_RSA512b_vm_index = get_index(ssl_dict, "ssl_vm", ssl_RSA512b_vm_ref, 'details', 'rsa_sig', '512_bits')
+ ssl_RSA1024b_vm_index = get_index(ssl_dict, "ssl_vm", ssl_RSA1024b_vm_ref, 'details', 'rsa_sig', '1024_bits')
+ ssl_RSA2048b_vm_index = get_index(ssl_dict, "ssl_vm", ssl_RSA2048b_vm_ref, 'details', 'rsa_sig', '2048_bits')
+ ssl_RSA4096b_vm_index = get_index(ssl_dict, "ssl_vm", ssl_RSA4096b_vm_ref, 'details', 'rsa_sig', '4096_bits')
+ ssl_RSA_vm_index = (ssl_RSA512b_vm_index + ssl_RSA1024b_vm_index + ssl_RSA2048b_vm_index + ssl_RSA4096b_vm_index) / 4
+
+ ssl_AES16B_vm_index = get_index(ssl_dict, "ssl_vm", ssl_AES16B_vm_ref, 'details', 'aes_128_cbc', '16B_block')
+ ssl_AES64B_vm_index = get_index(ssl_dict, "ssl_vm", ssl_AES64B_vm_ref, 'details', 'aes_128_cbc', '64B_block')
+ ssl_AES256B_vm_index = get_index(ssl_dict, "ssl_vm", ssl_AES256B_vm_ref, 'details', 'aes_128_cbc', '256B_block')
+ ssl_AES1024B_vm_index = get_index(ssl_dict, "ssl_vm", ssl_AES1024B_vm_ref, 'details', 'aes_128_cbc', '1024B_block')
+ ssl_AES8192B_vm_index = get_index(ssl_dict, "ssl_vm", ssl_AES8192B_vm_ref, 'details', 'aes_128_cbc', '8192B_block')
+ ssl_AES_vm_index = (ssl_AES16B_vm_index + ssl_AES64B_vm_index + ssl_AES256B_vm_index + ssl_AES1024B_vm_index + ssl_AES8192B_vm_index) / 5
+
+ ssl_vm_index = (ssl_RSA_vm_index + ssl_AES_vm_index) / 2
+
+ ssl_index = (ssl_bm_index + ssl_vm_index) / 2
+
+ ssl_dict_i = {}
+ ssl_dict_i['index'] = ssl_index
+ ssl_dict_i['results'] = ssl_dict
+ return ssl_dict_i
diff --git a/legacy/scripts/ref_results/index_calculation.py b/legacy/scripts/ref_results/index_calculation.py
new file mode 100644
index 00000000..14c2d4d2
--- /dev/null
+++ b/legacy/scripts/ref_results/index_calculation.py
@@ -0,0 +1,49 @@
+##############################################################################
+# Copyright (c) 2017 ZTE Corporation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+import json
+
+
+def compute_index(total_measured, ref_result, count):
+ try:
+ average = float(total_measured / count)
+
+ except ZeroDivisionError:
+ average = 0
+ index = average / ref_result
+ return index
+
+
+def get_reference(*args):
+
+ with open('scripts/ref_results/reference.json') as reference_file:
+ reference_djson = json.load(reference_file)
+ for arg in args:
+ ref_n = reference_djson.get(str(arg))
+ reference_djson = reference_djson.get(str(arg))
+ return ref_n
+
+
+def generic_index(dict_gen, testcase, reference_num, *args):
+ c = len(args)
+ count = 0
+ total = 0
+ result = 0
+ for k, v in dict_gen.iteritems():
+ dict_temp = dict_gen[k]
+ if dict_gen[k]['name'] == '{0}.yaml'.format(testcase):
+ count = count + 1
+ for arg in args:
+ if arg == args[c - 1]:
+ try:
+ result = float(dict_temp.get(str(arg)))
+ except ValueError:
+ result = float(dict_temp.get(str(arg))[:-1]) * 1000
+ dict_temp = dict_temp.get(str(arg))
+ total = total + result
+ return compute_index(total, reference_num, count)
diff --git a/legacy/scripts/ref_results/network_benchmarks_indices.py b/legacy/scripts/ref_results/network_benchmarks_indices.py
new file mode 100644
index 00000000..67980ee9
--- /dev/null
+++ b/legacy/scripts/ref_results/network_benchmarks_indices.py
@@ -0,0 +1,28 @@
+##############################################################################
+# Copyright (c) 2017 ZTE Corporation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+from index_calculation import generic_index as get_index
+from index_calculation import get_reference
+from result_accum import result_concat as concat
+
+
+def iperf_index():
+ iperf_dict = concat('results/iperf/')
+ iperf_bm_ref = get_reference('network', 'iperf_bm', 'throughput received(b/s)')
+ iperf_bm_index = get_index(iperf_dict, 'iperf_bm', iperf_bm_ref, 'details', 'bandwidth', 'received_throughput')
+ iperf_vm_ref = get_reference('network', 'iperf_vm', 'throughput received(b/s)')
+ iperf_vm_index = get_index(iperf_dict, 'iperf_vm', iperf_vm_ref, 'details', 'bandwidth', 'received_throughput')
+
+ iperf_vm_2_ref = get_reference('network', 'iperf_vm_2', 'throughput received(b/s)')
+ iperf_vm_2_index = get_index(iperf_dict, 'iperf_vm_2', iperf_vm_2_ref, 'details', 'bandwidth', 'received_throughput')
+ iperf_index = float(iperf_bm_index + iperf_vm_index + iperf_vm_2_index) / 3
+ print iperf_index
+ iperf_dict_i = {}
+ iperf_dict_i['index'] = iperf_index
+ iperf_dict_i['results'] = iperf_dict
+ return iperf_dict_i
diff --git a/legacy/scripts/ref_results/reference.json b/legacy/scripts/ref_results/reference.json
new file mode 100644
index 00000000..cfcbfc3b
--- /dev/null
+++ b/legacy/scripts/ref_results/reference.json
@@ -0,0 +1,97 @@
+{
+ "compute": {
+ "dhrystone_bm": {
+ "multi_cpu": 103362.1,
+ "single_cpu": 3231.7
+ },
+ "dhrystone_vm": {
+ "multi_cpu": 10585.8,
+ "single_cpu": 2953.6
+ },
+ "dpi_bm": 8.12,
+ "dpi_vm": 22.12,
+ "ramspeed_bm": {
+ "FLOATmem": {
+ "Average (MB/s)": 9758.79
+ },
+ "INTmem": {
+ "Average (MB/s)": 12268.38
+ }
+ },
+ "ramspeed_vm": {
+ "FLOATmem": {
+ "Average (MB/s)": 9064.09
+ },
+ "INTmem": {
+ "Average (MB/s)": 12147.59
+ }
+ },
+ "ssl_bm": {
+ "AES": {
+ "1024B": 808861020,
+ "16B": 735490250,
+ "256B": 803323650,
+ "64B": 788429210,
+ "8192B": 807701160
+ },
+ "RSA": {
+ "1024b": 7931.44,
+ "2048b": 1544.3,
+ "4096b": 161.92,
+ "512b": 22148.9
+ }
+ },
+ "ssl_vm": {
+ "AES": {
+ "1024B": 808861020,
+ "16B": 735490250,
+ "256B": 803323650,
+ "64B": 788429210,
+ "8192B": 807701160
+ },
+ "RSA": {
+ "1024b": 7931.44,
+ "2048b": 1544.3,
+ "4096b": 161.92,
+ "512b": 22148.9
+ }
+ },
+ "whetstone_bm": {
+ "multi_cpu": 41483.3,
+ "single_cpu": 806.1
+ },
+ "whetstone_vm": {
+ "multi_cpu": 2950.6,
+ "single_cpu": 789.0
+ }
+ },
+ "network": {
+ "iperf_bm": {
+ "throughput received(b/s)": 944473000.0
+ },
+ "iperf_vm": {
+ "throughput received(b/s)": 14416700000.0
+ },
+ "iperf_vm_2": {
+ "throughput received(b/s)": 2461530000.0
+ }
+ },
+ "storage": {
+ "fio_bm": {
+ "read": {
+ "IOPS": 6693
+ },
+ "write": {
+ "IOPS": 6688
+ }
+ },
+ "fio_vm": {
+ "read": {
+ "IOPS": 2239
+ },
+ "write": {
+ "IOPS": 2237
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/legacy/scripts/ref_results/result_accum.py b/legacy/scripts/ref_results/result_accum.py
new file mode 100644
index 00000000..6eb169e8
--- /dev/null
+++ b/legacy/scripts/ref_results/result_accum.py
@@ -0,0 +1,39 @@
+##############################################################################
+# Copyright (c) 2017 ZTE Corporation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+import os
+import json
+
+
+def result_concat(targ_dir):
+ list_vm = []
+ list_bm = []
+ diction = {}
+
+ for file in os.listdir(targ_dir):
+ if file.endswith(".json"):
+ if file.startswith("instance"):
+ print str(file)
+ list_vm.append(file)
+ else:
+ list_bm.append(file)
+ l = len(list_bm)
+ k = len(list_vm)
+
+ for x in range(0, l):
+ file_t = list_bm[x]
+ with open(targ_dir + file_t) as result_file:
+ result_djson = json.load(result_file)
+ diction['Baremetal' + str(int(x + 1))] = result_djson
+
+ for x in range(0, k):
+ file_t = list_vm[x]
+ with open(targ_dir + file_t) as result_file:
+ result_djson = json.load(result_file)
+ diction['Virtual Machine ' + str(x + 1)] = result_djson
+ return diction
diff --git a/legacy/scripts/ref_results/storage_benchmarks_indices.py b/legacy/scripts/ref_results/storage_benchmarks_indices.py
new file mode 100644
index 00000000..e87fe36b
--- /dev/null
+++ b/legacy/scripts/ref_results/storage_benchmarks_indices.py
@@ -0,0 +1,37 @@
+##############################################################################
+# Copyright (c) 2017 ZTE Corporation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+from index_calculation import generic_index as get_index
+from index_calculation import get_reference
+from result_accum import result_concat as concat
+
+
+def fio_index():
+ fio_dict = concat('results/fio/')
+ fio_r_bm_ref = get_reference('storage', 'fio_bm', 'read', 'IOPS')
+ fio_r_bm_index = get_index(fio_dict, 'fio_bm', fio_r_bm_ref, 'details', 'job_0', 'read', 'io_ps')
+ fio_w_bm_ref = get_reference('storage', 'fio_bm', 'write', 'IOPS')
+ fio_w_bm_index = get_index(fio_dict, 'fio_bm', fio_w_bm_ref, 'details', 'job_0', 'write', 'io_ps')
+
+ fio_bm_index = (fio_r_bm_index + fio_w_bm_index) / 2
+
+ fio_r_vm_ref = get_reference('storage', 'fio_vm', 'read', 'IOPS')
+ fio_r_vm_index = get_index(fio_dict, 'fio_vm', fio_r_vm_ref, 'details', 'job_0', 'read', 'io_ps')
+
+ fio_w_vm_ref = get_reference('storage', 'fio_vm', 'write', 'IOPS')
+ fio_w_vm_index = get_index(fio_dict, 'fio_vm', fio_w_vm_ref, 'details', 'job_0', 'write', 'io_ps')
+
+ fio_vm_index = (fio_r_vm_index + fio_w_vm_index) / 2
+
+ fio_index = (fio_bm_index + fio_vm_index) / 2
+ print fio_index
+
+ fio_dict_i = {}
+ fio_dict_i['index'] = fio_index
+ fio_dict_i['results'] = fio_dict
+ return fio_dict_i
diff --git a/legacy/scripts/ref_results/suite_result.py b/legacy/scripts/ref_results/suite_result.py
new file mode 100644
index 00000000..66213391
--- /dev/null
+++ b/legacy/scripts/ref_results/suite_result.py
@@ -0,0 +1,58 @@
+##############################################################################
+# Copyright (c) 2016 ZTE Corp and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+import json
+import importlib
+import sys
+from qtip.utils import logger_utils
+from os.path import expanduser
+
+logger = logger_utils.QtipLogger('suite_result').get
+
+
+def get_benchmark_result(benchmark_name, suite_name):
+ benchmark_indices = importlib.import_module('scripts.ref_results'
+ '.{0}_benchmarks_indices'.format(suite_name))
+ methodToCall = getattr(benchmark_indices, '{0}_index'.format(benchmark_name))
+ return methodToCall()
+
+
+def get_suite_result(suite_name):
+ suite_dict = {}
+ suite_bench_list = {'compute': ['DPI', 'Dhrystone', 'Whetstone', 'SSL', 'RamSpeed'],
+ 'storage': ['FIO'],
+ 'network': ['IPERF']}
+ temp = 0
+ l = len(suite_bench_list[suite_name])
+ for benchmark in suite_bench_list[suite_name]:
+ try:
+ suite_dict[benchmark] = get_benchmark_result(benchmark.lower(), suite_name)
+ temp = temp + float(suite_dict[benchmark]['index'])
+ except OSError:
+ l = l - 1
+ pass
+
+ if l == 0:
+ logger.info("No {0} suite results found".format(suite_name))
+ return False
+ else:
+ suite_index = temp / l
+ suite_dict_f = {'index': suite_index,
+ 'suite_results': suite_dict}
+ result_path = expanduser('~') + '/qtip/results'
+ with open('{0}/{1}_result.json'.format(result_path, suite_name), 'w+') as result_json:
+ json.dump(suite_dict_f, result_json, indent=4, sort_keys=True)
+ return True
+
+
+def main():
+ get_suite_result(sys.argv[1])
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/ci/network/docker-compose.yaml b/tests/ci/network/docker-compose.yaml
new file mode 100644
index 00000000..d4f2c904
--- /dev/null
+++ b/tests/ci/network/docker-compose.yaml
@@ -0,0 +1,27 @@
+##############################################################################
+# Copyright (c) 2017 Dell EMC, ZTE and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+version: '2'
+services:
+
+ nettest:
+ container_name: nettest_qtip
+ image: opnfv/qtip-nettest:${DOCKER_TAG}
+ ports:
+ - 5001:5000
+ env_file: ${ENV_FILE}
+
+ qtip:
+ container_name: network_qtip
+ image: opnfv/qtip:${DOCKER_TAG}
+ env_file: ${ENV_FILE}
+ volumes:
+ - ${SSH_CREDENTIALS}:/root/.ssh
+ links:
+ - nettest