aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/database/v2/models.py2
-rw-r--r--api/resources/v1/env.py52
-rw-r--r--api/resources/v2/containers.py2
-rw-r--r--api/resources/v2/environments.py8
-rw-r--r--api/resources/v2/images.py10
-rw-r--r--api/resources/v2/openrcs.py8
-rw-r--r--api/resources/v2/pods.py8
-rw-r--r--api/resources/v2/projects.py8
-rw-r--r--api/resources/v2/tasks.py11
-rw-r--r--api/resources/v2/testcases.py10
-rw-r--r--api/resources/v2/testsuites.py8
-rw-r--r--api/urls.py3
-rw-r--r--api/utils/thread.py8
-rw-r--r--docker/Dockerfile1
-rwxr-xr-xdocker/nginx.sh31
-rwxr-xr-xdocker/supervisor.sh26
-rwxr-xr-xdocker/uwsgi.sh (renamed from api/api-prepare.sh)54
-rw-r--r--etc/yardstick/yardstick.conf.sample2
-rw-r--r--gui/Gruntfile.js492
-rw-r--r--gui/app/404.html152
-rw-r--r--gui/app/favicon.icobin0 -> 4286 bytes
-rw-r--r--gui/app/images/back.pngbin0 -> 1976 bytes
-rw-r--r--gui/app/images/checkno.pngbin0 -> 5849 bytes
-rw-r--r--gui/app/images/checkyes.pngbin0 -> 6423 bytes
-rw-r--r--gui/app/images/close.pngbin0 -> 1211 bytes
-rw-r--r--gui/app/images/loading.gifbin0 -> 130310 bytes
-rw-r--r--gui/app/images/loading2.gifbin0 -> 25405 bytes
-rw-r--r--gui/app/images/statusno.pngbin0 -> 3861 bytes
-rw-r--r--gui/app/images/statusyes.pngbin0 -> 2577 bytes
-rw-r--r--gui/app/images/url.json1
-rw-r--r--gui/app/images/yeoman.pngbin0 -> 13501 bytes
-rw-r--r--gui/app/index.html111
-rw-r--r--gui/app/robots.txt4
-rw-r--r--gui/app/scripts/app.js30
-rw-r--r--gui/app/scripts/controllers/container.controller.js182
-rw-r--r--gui/app/scripts/controllers/content.controller.js136
-rw-r--r--gui/app/scripts/controllers/detail.controller.js384
-rw-r--r--gui/app/scripts/controllers/image.controller.js166
-rw-r--r--gui/app/scripts/controllers/main.js725
-rw-r--r--gui/app/scripts/controllers/pod.controller.js179
-rw-r--r--gui/app/scripts/controllers/project.controller.js160
-rw-r--r--gui/app/scripts/controllers/projectDetail.controller.js690
-rw-r--r--gui/app/scripts/controllers/report.controller.js115
-rw-r--r--gui/app/scripts/controllers/suitecreate.controller.js104
-rw-r--r--gui/app/scripts/controllers/suitedetail.controller.js48
-rw-r--r--gui/app/scripts/controllers/task.controller.js175
-rw-r--r--gui/app/scripts/controllers/taskModify.controller.js533
-rw-r--r--gui/app/scripts/controllers/testcase.controller.js154
-rw-r--r--gui/app/scripts/controllers/testcasedetail.controller.js50
-rw-r--r--gui/app/scripts/controllers/testsuit.controller.js119
-rw-r--r--gui/app/scripts/factory/main.factory.js247
-rw-r--r--gui/app/scripts/router.config.js184
-rw-r--r--gui/app/styles/main.css208
-rw-r--r--gui/app/views/container.html134
-rw-r--r--gui/app/views/content.html0
-rw-r--r--gui/app/views/environmentDetail.html143
-rw-r--r--gui/app/views/environmentList.html155
-rw-r--r--gui/app/views/layout/footer.html5
-rw-r--r--gui/app/views/layout/header.html43
-rw-r--r--gui/app/views/layout/sideNav.html141
-rw-r--r--gui/app/views/layout/sideNav2.html108
-rw-r--r--gui/app/views/main.html174
-rw-r--r--gui/app/views/main2.html174
-rw-r--r--gui/app/views/modal/chooseContainer.html15
-rw-r--r--gui/app/views/modal/deleteConfirm.html19
-rw-r--r--gui/app/views/modal/environmentDialog.html330
-rw-r--r--gui/app/views/modal/projectCreate.html21
-rw-r--r--gui/app/views/modal/suiteName.html18
-rw-r--r--gui/app/views/modal/taskCreate.html134
-rw-r--r--gui/app/views/podupload.html136
-rw-r--r--gui/app/views/projectList.html57
-rw-r--r--gui/app/views/projectdetail.html97
-rw-r--r--gui/app/views/report.html56
-rw-r--r--gui/app/views/suite.html149
-rw-r--r--gui/app/views/suitedetail.html110
-rw-r--r--gui/app/views/taskList.html62
-rw-r--r--gui/app/views/taskmodify.html162
-rw-r--r--gui/app/views/testcasechoose.html48
-rw-r--r--gui/app/views/testcasedetail.html110
-rw-r--r--gui/app/views/testcaselist.html150
-rw-r--r--gui/app/views/uploadImage.html145
-rw-r--r--gui/bower.json45
-rwxr-xr-xgui/gui.sh8
-rw-r--r--gui/package.json43
-rw-r--r--gui/test/.jshintrc18
-rw-r--r--gui/test/karma.conf.js93
-rw-r--r--gui/test/spec/controllers/main.js23
-rwxr-xr-xinstall.sh5
-rw-r--r--samples/vnf_samples/nsut/ping/tc_ping_ovs_dpdk_context.yaml42
-rw-r--r--tests/ci/scp_storperf_files.sh20
-rwxr-xr-xtests/ci/yardstick-verify15
-rw-r--r--tests/opnfv/test_cases/opnfv_yardstick_tc008.yaml9
-rw-r--r--tests/unit/benchmark/contexts/nodes_duplicate_sample_new.yaml32
-rw-r--r--tests/unit/benchmark/contexts/nodes_duplicate_sample_ovs.yaml63
-rw-r--r--tests/unit/benchmark/contexts/nodes_sample_new.yaml96
-rw-r--r--tests/unit/benchmark/contexts/nodes_sample_new_sriov.yaml82
-rw-r--r--tests/unit/benchmark/contexts/nodes_sample_ovs.yaml104
-rw-r--r--tests/unit/benchmark/contexts/nodes_sample_ovsdpdk.yaml104
-rw-r--r--tests/unit/benchmark/contexts/ovs_sample_password.yaml104
-rw-r--r--tests/unit/benchmark/contexts/ovs_sample_ssh_key.yaml69
-rw-r--r--tests/unit/benchmark/contexts/ovs_sample_write_to_file.txt1
-rw-r--r--tests/unit/benchmark/contexts/sriov_sample_password.yaml52
-rw-r--r--tests/unit/benchmark/contexts/sriov_sample_ssh_key.yaml54
-rw-r--r--tests/unit/benchmark/contexts/sriov_sample_write_to_file.txt1
-rw-r--r--tests/unit/benchmark/contexts/test_ovsdpdk.py325
-rw-r--r--tests/unit/benchmark/contexts/test_sriov.py421
-rw-r--r--tests/unit/benchmark/contexts/test_standalone.py590
-rw-r--r--tests/unit/benchmark/core/test_task.py14
-rw-r--r--tests/unit/benchmark/scenarios/availability/test_util.py19
-rw-r--r--tests/unit/benchmark/scenarios/networking/test_pktgen.py534
-rw-r--r--yardstick/benchmark/contexts/model.py2
-rw-r--r--yardstick/benchmark/contexts/ovsdpdk.py369
-rw-r--r--yardstick/benchmark/contexts/sriov.py431
-rw-r--r--yardstick/benchmark/contexts/standalone.py90
-rw-r--r--yardstick/benchmark/core/plugin.py4
-rw-r--r--yardstick/benchmark/core/task.py23
-rw-r--r--yardstick/benchmark/scenarios/availability/actionplayers.py4
-rw-r--r--yardstick/benchmark/scenarios/availability/director.py7
-rw-r--r--yardstick/benchmark/scenarios/availability/operation/baseoperation.py1
-rw-r--r--yardstick/benchmark/scenarios/availability/operation/operation_general.py20
-rw-r--r--yardstick/benchmark/scenarios/availability/scenario_general.py4
-rw-r--r--yardstick/benchmark/scenarios/availability/util.py33
-rw-r--r--yardstick/benchmark/scenarios/networking/iperf3.py17
-rw-r--r--yardstick/benchmark/scenarios/networking/pktgen.py242
-rw-r--r--yardstick/benchmark/scenarios/networking/pktgen_benchmark.bash139
-rw-r--r--yardstick/common/constants.py2
-rw-r--r--yardstick/common/utils.py10
-rw-r--r--yardstick/dispatcher/base.py6
-rw-r--r--yardstick/network_services/vnf_generic/vnfdgen.py2
-rw-r--r--yardstick/orchestrator/heat.py8
130 files changed, 12684 insertions, 213 deletions
diff --git a/api/database/v2/models.py b/api/database/v2/models.py
index 64d49cc9d..1e85559cb 100644
--- a/api/database/v2/models.py
+++ b/api/database/v2/models.py
@@ -31,7 +31,7 @@ class V2Environment(Base):
class V2Openrc(Base):
- __tablename__ = 'V2_openrc'
+ __tablename__ = 'v2_openrc'
id = Column(Integer, primary_key=True)
uuid = Column(String(30))
name = Column(String(30))
diff --git a/api/resources/v1/env.py b/api/resources/v1/env.py
index 4632f15fe..8943db3d1 100644
--- a/api/resources/v1/env.py
+++ b/api/resources/v1/env.py
@@ -65,16 +65,20 @@ class V1Env(ApiResource):
client.pull(consts.GRAFANA_IMAGE, consts.GRAFANA_TAG)
LOG.info('Createing grafana container')
- self._create_grafana_container(client)
+ container = self._create_grafana_container(client)
LOG.info('Grafana container is created')
time.sleep(5)
+ container = client.inspect_container(container['Id'])
+ ip = container['NetworkSettings']['Networks']['bridge']['IPAddress']
+ LOG.debug('container ip is: %s', ip)
+
LOG.info('Creating data source for grafana')
- self._create_data_source()
+ self._create_data_source(ip)
LOG.info('Creating dashboard for grafana')
- self._create_dashboard()
+ self._create_dashboard(ip)
self._update_task_status(task_id)
LOG.info('Finished')
@@ -82,8 +86,8 @@ class V1Env(ApiResource):
self._update_task_error(task_id, str(e))
LOG.exception('Create grafana failed')
- def _create_dashboard(self):
- url = 'http://admin:admin@%s:3000/api/dashboards/db' % consts.GRAFANA_IP
+ def _create_dashboard(self, ip):
+ url = 'http://admin:admin@{}:{}/api/dashboards/db'.format(ip, consts.GRAFANA_PORT)
path = os.path.join(consts.REPOS_DIR, 'dashboard', '*dashboard.json')
for i in sorted(glob.iglob(path)):
@@ -95,13 +99,21 @@ class V1Env(ApiResource):
LOG.exception('Create dashboard %s failed', i)
raise
- def _create_data_source(self):
- url = 'http://admin:admin@%s:3000/api/datasources' % consts.GRAFANA_IP
+ def _create_data_source(self, ip):
+ url = 'http://admin:admin@{}:{}/api/datasources'.format(ip, consts.GRAFANA_PORT)
+ influx_conf = utils.parse_ini_file(consts.CONF_FILE)
+
+ try:
+ influx_url = influx_conf['dispatcher_influxdb']['target']
+ except KeyError:
+ LOG.exception('influxdb url not set in yardstick.conf')
+ raise
+
data = {
"name": "yardstick",
"type": "influxdb",
"access": "proxy",
- "url": "http://%s:8086" % consts.INFLUXDB_IP,
+ "url": influx_url,
"password": "root",
"user": "root",
"database": "yardstick",
@@ -117,8 +129,8 @@ class V1Env(ApiResource):
raise
def _create_grafana_container(self, client):
- ports = [3000]
- port_bindings = {k: k for k in ports}
+ ports = [consts.GRAFANA_PORT]
+ port_bindings = {consts.GRAFANA_PORT: consts.GRAFANA_MAPPING_PORT}
restart_policy = {"MaximumRetryCount": 0, "Name": "always"}
host_config = client.create_host_config(port_bindings=port_bindings,
restart_policy=restart_policy)
@@ -133,6 +145,7 @@ class V1Env(ApiResource):
host_config=host_config)
LOG.info('Starting container')
client.start(container)
+ return container
def _check_image_exist(self, client, t):
return any(t in a['RepoTags'][0]
@@ -152,9 +165,6 @@ class V1Env(ApiResource):
client = Client(base_url=consts.DOCKER_URL)
try:
- LOG.info('Changing output to influxdb')
- self._change_output_to_influxdb()
-
LOG.info('Checking if influxdb image exist')
if not self._check_image_exist(client, '%s:%s' %
(consts.INFLUXDB_IMAGE,
@@ -163,11 +173,18 @@ class V1Env(ApiResource):
client.pull(consts.INFLUXDB_IMAGE, tag=consts.INFLUXDB_TAG)
LOG.info('Createing influxdb container')
- self._create_influxdb_container(client)
+ container = self._create_influxdb_container(client)
LOG.info('Influxdb container is created')
time.sleep(5)
+ container = client.inspect_container(container['Id'])
+ ip = container['NetworkSettings']['Networks']['bridge']['IPAddress']
+ LOG.debug('container ip is: %s', ip)
+
+ LOG.info('Changing output to influxdb')
+ self._change_output_to_influxdb(ip)
+
LOG.info('Config influxdb')
self._config_influxdb()
@@ -180,7 +197,7 @@ class V1Env(ApiResource):
def _create_influxdb_container(self, client):
- ports = [8083, 8086]
+ ports = [consts.INFLUXDB_DASHBOARD_PORT, consts.INFLUXDB_PORT]
port_bindings = {k: k for k in ports}
restart_policy = {"MaximumRetryCount": 0, "Name": "always"}
host_config = client.create_host_config(port_bindings=port_bindings,
@@ -196,6 +213,7 @@ class V1Env(ApiResource):
host_config=host_config)
LOG.info('Starting container')
client.start(container)
+ return container
def _config_influxdb(self):
try:
@@ -208,7 +226,7 @@ class V1Env(ApiResource):
except Exception:
LOG.exception('Config influxdb failed')
- def _change_output_to_influxdb(self):
+ def _change_output_to_influxdb(self, ip):
utils.makedirs(consts.CONF_DIR)
parser = configparser.ConfigParser()
@@ -218,7 +236,7 @@ class V1Env(ApiResource):
LOG.info('Set dispatcher to influxdb')
parser.set('DEFAULT', 'dispatcher', 'influxdb')
parser.set('dispatcher_influxdb', 'target',
- 'http://%s:8086' % consts.INFLUXDB_IP)
+ 'http://{}:{}'.format(ip, consts.INFLUXDB_PORT))
LOG.info('Writing to %s', consts.CONF_FILE)
with open(consts.CONF_FILE, 'w') as f:
diff --git a/api/resources/v2/containers.py b/api/resources/v2/containers.py
index ce7130376..66dc94120 100644
--- a/api/resources/v2/containers.py
+++ b/api/resources/v2/containers.py
@@ -1,5 +1,5 @@
##############################################################################
-# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others.
+# Copyright (c) 2017 Huawei Technologies Co.,Ltd.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
diff --git a/api/resources/v2/environments.py b/api/resources/v2/environments.py
index e4679b0d6..f021a3c5a 100644
--- a/api/resources/v2/environments.py
+++ b/api/resources/v2/environments.py
@@ -1,3 +1,11 @@
+##############################################################################
+# Copyright (c) 2017 Huawei Technologies Co.,Ltd.
+#
+# 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 uuid
import logging
diff --git a/api/resources/v2/images.py b/api/resources/v2/images.py
index 701818493..8359e105b 100644
--- a/api/resources/v2/images.py
+++ b/api/resources/v2/images.py
@@ -1,3 +1,11 @@
+##############################################################################
+# Copyright (c) 2017 Huawei Technologies Co.,Ltd.
+#
+# 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 subprocess
import threading
@@ -29,6 +37,8 @@ class V2Images(ApiResource):
else:
images = [self.get_info(change_obj_to_dict(i)) for i in images_list]
status = 1 if all(i['status'] == 'ACTIVE' for i in images) else 0
+ if not images:
+ status = 0
return result_handler(consts.API_SUCCESS, {'status': status, 'images': images})
diff --git a/api/resources/v2/openrcs.py b/api/resources/v2/openrcs.py
index 5f3b9382f..cb506d0e8 100644
--- a/api/resources/v2/openrcs.py
+++ b/api/resources/v2/openrcs.py
@@ -1,3 +1,11 @@
+##############################################################################
+# Copyright (c) 2017 Huawei Technologies Co.,Ltd.
+#
+# 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 uuid
import logging
import re
diff --git a/api/resources/v2/pods.py b/api/resources/v2/pods.py
index ebc1312da..f2316d353 100644
--- a/api/resources/v2/pods.py
+++ b/api/resources/v2/pods.py
@@ -1,3 +1,11 @@
+##############################################################################
+# Copyright (c) 2017 Huawei Technologies Co.,Ltd.
+#
+# 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 uuid
import yaml
import logging
diff --git a/api/resources/v2/projects.py b/api/resources/v2/projects.py
index 376cf1a37..2ff61d0fe 100644
--- a/api/resources/v2/projects.py
+++ b/api/resources/v2/projects.py
@@ -1,3 +1,11 @@
+##############################################################################
+# Copyright (c) 2017 Huawei Technologies Co.,Ltd.
+#
+# 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 uuid
import logging
diff --git a/api/resources/v2/tasks.py b/api/resources/v2/tasks.py
index 9790d7640..885a190c6 100644
--- a/api/resources/v2/tasks.py
+++ b/api/resources/v2/tasks.py
@@ -1,3 +1,11 @@
+##############################################################################
+# Copyright (c) 2017 Huawei Technologies Co.,Ltd.
+#
+# 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 uuid
import logging
from datetime import datetime
@@ -106,7 +114,8 @@ class V2Task(ApiResource):
if project.tasks:
LOG.info('update tasks in project')
- new_task_list = project.tasks.split(',').remove(task_id)
+ new_task_list = project.tasks.split(',')
+ new_task_list.remove(task_id)
if new_task_list:
new_tasks = ','.join(new_task_list)
else:
diff --git a/api/resources/v2/testcases.py b/api/resources/v2/testcases.py
index 81b4aa88c..b47a8f6b7 100644
--- a/api/resources/v2/testcases.py
+++ b/api/resources/v2/testcases.py
@@ -1,3 +1,11 @@
+##############################################################################
+# Copyright (c) 2017 Huawei Technologies Co.,Ltd.
+#
+# 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 errno
import os
@@ -17,7 +25,7 @@ class V2Testcases(ApiResource):
def get(self):
param = Param({})
testcase_list = Testcase().list_all(param)
- return result_handler(consts.API_SUCCESS, testcase_list)
+ return result_handler(consts.API_SUCCESS, {'testcases': testcase_list})
def post(self):
return self._dispatch_post()
diff --git a/api/resources/v2/testsuites.py b/api/resources/v2/testsuites.py
index ee942eff9..56ad47375 100644
--- a/api/resources/v2/testsuites.py
+++ b/api/resources/v2/testsuites.py
@@ -1,3 +1,11 @@
+##############################################################################
+# Copyright (c) 2017 Huawei Technologies Co.,Ltd.
+#
+# 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 errno
import logging
diff --git a/api/urls.py b/api/urls.py
index 2211348f3..3fef91af8 100644
--- a/api/urls.py
+++ b/api/urls.py
@@ -26,15 +26,18 @@ urlpatterns = [
Url('/api/v2/yardstick/environments/action', 'v2_environments'),
Url('/api/v2/yardstick/environments/<environment_id>', 'v2_environment'),
+ Url('/api/v2/yardstick/openrcs', 'v2_openrcs'),
Url('/api/v2/yardstick/openrcs/action', 'v2_openrcs'),
Url('/api/v2/yardstick/openrcs/<openrc_id>', 'v2_openrc'),
+ Url('/api/v2/yardstick/pods', 'v2_pods'),
Url('/api/v2/yardstick/pods/action', 'v2_pods'),
Url('/api/v2/yardstick/pods/<pod_id>', 'v2_pod'),
Url('/api/v2/yardstick/images', 'v2_images'),
Url('/api/v2/yardstick/images/action', 'v2_images'),
+ Url('/api/v2/yardstick/containers', 'v2_containers'),
Url('/api/v2/yardstick/containers/action', 'v2_containers'),
Url('/api/v2/yardstick/containers/<container_id>', 'v2_container'),
diff --git a/api/utils/thread.py b/api/utils/thread.py
index 5f4ec7e94..20bd07a12 100644
--- a/api/utils/thread.py
+++ b/api/utils/thread.py
@@ -1,3 +1,11 @@
+##############################################################################
+# Copyright (c) 2017 Huawei Technologies Co.,Ltd.
+#
+# 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 threading
import os
import logging
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 2c4270a09..b48a550bf 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -37,6 +37,7 @@ RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/storperf ${ST
WORKDIR ${YARDSTICK_REPO_DIR}
RUN ${YARDSTICK_REPO_DIR}/install.sh
+RUN ${YARDSTICK_REPO_DIR}/docker/supervisor.sh
RUN echo "daemon off;" >> /etc/nginx/nginx.conf
diff --git a/docker/nginx.sh b/docker/nginx.sh
new file mode 100755
index 000000000..26937d134
--- /dev/null
+++ b/docker/nginx.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+##############################################################################
+# Copyright (c) 2017 Huawei Technologies Co.,Ltd 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
+##############################################################################
+
+# nginx config
+nginx_config='/etc/nginx/conf.d/yardstick.conf'
+
+if [[ ! -e "${nginx_config}" ]];then
+
+ cat << EOF > "${nginx_config}"
+server {
+ listen 5000;
+ server_name localhost;
+ index index.htm index.html;
+ location / {
+ include uwsgi_params;
+ uwsgi_pass unix:///var/run/yardstick.sock;
+ }
+
+ location /gui/ {
+ alias /etc/nginx/yardstick/gui/;
+ }
+}
+EOF
+fi
diff --git a/docker/supervisor.sh b/docker/supervisor.sh
new file mode 100755
index 000000000..b67de2212
--- /dev/null
+++ b/docker/supervisor.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+##############################################################################
+# Copyright (c) 2017 Huawei Technologies Co.,Ltd 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
+##############################################################################
+
+# nginx service start when boot
+supervisor_config='/etc/supervisor/conf.d/yardstick.conf'
+
+if [[ ! -e "${supervisor_config}" ]];then
+ cat << EOF > "${supervisor_config}"
+[supervisord]
+nodaemon = true
+
+[program:nginx]
+command = service nginx restart
+
+[program:yardstick_uwsgi]
+directory = /etc/yardstick
+command = uwsgi -i yardstick.ini
+EOF
+fi
diff --git a/api/api-prepare.sh b/docker/uwsgi.sh
index 7632d9da9..cf4612332 100755
--- a/api/api-prepare.sh
+++ b/docker/uwsgi.sh
@@ -1,6 +1,6 @@
#!/bin/bash
##############################################################################
-# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others.
+# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -12,6 +12,13 @@
# generate uwsgi config file
mkdir -p /etc/yardstick
+
+# create api log directory
+mkdir -p /var/log/yardstick
+
+# create yardstick.sock for communicating
+touch /var/run/yardstick.sock
+
uwsgi_config='/etc/yardstick/yardstick.ini'
if [[ ! -e "${uwsgi_config}" ]];then
@@ -37,48 +44,3 @@ EOF
echo "virtualenv = ${YARDSTICK_VENV}" >> "${uwsgi_config}"
fi
fi
-
-# nginx config
-nginx_config='/etc/nginx/conf.d/yardstick.conf'
-
-if [[ ! -e "${nginx_config}" ]];then
-
- cat << EOF > "${nginx_config}"
-server {
- listen 5000;
- server_name localhost;
- index index.htm index.html;
- location / {
- include uwsgi_params;
- uwsgi_pass unix:///var/run/yardstick.sock;
- }
-}
-EOF
-fi
-
-# nginx service start when boot
-supervisor_config='/etc/supervisor/conf.d/yardstick.conf'
-
-if [[ ! -e "${supervisor_config}" ]];then
- cat << EOF > "${supervisor_config}"
-[supervisord]
-nodaemon = true
-
-[program:yardstick_nginx]
-user = root
-command = service nginx restart
-autorestart = true
-
-[program:yardstick_uwsgi]
-user = root
-directory = /etc/yardstick
-command = uwsgi -i yardstick.ini
-autorestart = true
-EOF
-fi
-
-# create api log directory
-mkdir -p /var/log/yardstick
-
-# create yardstick.sock for communicating
-touch /var/run/yardstick.sock
diff --git a/etc/yardstick/yardstick.conf.sample b/etc/yardstick/yardstick.conf.sample
index 572051186..1157b9d62 100644
--- a/etc/yardstick/yardstick.conf.sample
+++ b/etc/yardstick/yardstick.conf.sample
@@ -9,7 +9,7 @@
[DEFAULT]
debug = False
-dispatcher = http
+dispatcher = http # setup multiple dipatcher with comma deperted e.g. file,http
[dispatcher_http]
timeout = 5
diff --git a/gui/Gruntfile.js b/gui/Gruntfile.js
new file mode 100644
index 000000000..171d65add
--- /dev/null
+++ b/gui/Gruntfile.js
@@ -0,0 +1,492 @@
+// Generated on 2017-05-31 using generator-angular 0.15.1
+'use strict';
+
+// # Globbing
+// for performance reasons we're only matching one level down:
+// 'test/spec/{,*/}*.js'
+// use this if you want to recursively match all subfolders:
+// 'test/spec/**/*.js'
+
+module.exports = function(grunt) {
+
+ // Time how long tasks take. Can help when optimizing build times
+ require('time-grunt')(grunt);
+
+ // Automatically load required Grunt tasks
+ require('jit-grunt')(grunt, {
+ useminPrepare: 'grunt-usemin',
+ ngtemplates: 'grunt-angular-templates',
+ cdnify: 'grunt-google-cdn'
+ });
+
+ // Configurable paths for the application
+ var appConfig = {
+ app: require('./bower.json').appPath || 'app',
+ dist: 'dist'
+ };
+
+ // Define the configuration for all the tasks
+ grunt.initConfig({
+
+ // Project settings
+ yeoman: appConfig,
+
+ // Watches files for changes and runs tasks based on the changed files
+ watch: {
+ bower: {
+ files: ['bower.json'],
+ tasks: ['wiredep']
+ },
+ js: {
+ files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
+ tasks: ['newer:jshint:all', 'newer:jscs:all'],
+ options: {
+ livereload: '<%= connect.options.livereload %>'
+ }
+ },
+ jsTest: {
+ files: ['test/spec/{,*/}*.js'],
+ tasks: ['newer:jshint:test', 'newer:jscs:test', 'karma']
+ },
+ styles: {
+ files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
+ tasks: ['newer:copy:styles', 'postcss']
+ },
+ gruntfile: {
+ files: ['Gruntfile.js']
+ },
+ livereload: {
+ options: {
+ livereload: '<%= connect.options.livereload %>'
+ },
+ files: [
+ '<%= yeoman.app %>/{,*/}*.html',
+ '.tmp/styles/{,*/}*.css',
+ '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
+ ]
+ }
+ },
+
+ // The actual grunt server settings
+ connect: {
+ options: {
+ port: 9099,
+ // Change this to '0.0.0.0' to access the server from outside.
+ hostname: 'localhost',
+ livereload: 35745
+ },
+ livereload: {
+ options: {
+ open: true,
+ middleware: function(connect) {
+ return [
+ connect.static('.tmp'),
+ connect().use(
+ '/bower_components',
+ connect.static('./bower_components')
+ ),
+ connect().use(
+ '/app/styles',
+ connect.static('./app/styles')
+ ),
+ connect.static(appConfig.app)
+ ];
+ }
+ }
+ },
+ test: {
+ options: {
+ port: 9001,
+ middleware: function(connect) {
+ return [
+ connect.static('.tmp'),
+ connect.static('test'),
+ connect().use(
+ '/bower_components',
+ connect.static('./bower_components')
+ ),
+ connect.static(appConfig.app)
+ ];
+ }
+ }
+ },
+ dist: {
+ options: {
+ open: true,
+ base: '<%= yeoman.dist %>'
+ }
+ }
+ },
+
+ // Make sure there are no obvious mistakes
+ jshint: {
+ options: {
+ jshintrc: '.jshintrc',
+ reporter: require('jshint-stylish')
+ },
+ all: {
+ src: [
+ 'Gruntfile.js',
+ '<%= yeoman.app %>/scripts/{,*/}*.js'
+ ]
+ },
+ test: {
+ options: {
+ jshintrc: 'test/.jshintrc'
+ },
+ src: ['test/spec/{,*/}*.js']
+ }
+ },
+
+ // Make sure code styles are up to par
+ jscs: {
+ options: {
+ config: '.jscsrc',
+ verbose: true
+ },
+ all: {
+ src: [
+ 'Gruntfile.js',
+ '<%= yeoman.app %>/scripts/{,*/}*.js'
+ ]
+ },
+ test: {
+ src: ['test/spec/{,*/}*.js']
+ }
+ },
+
+ // Empties folders to start fresh
+ clean: {
+ dist: {
+ files: [{
+ dot: true,
+ src: [
+ '.tmp',
+ '<%= yeoman.dist %>/{,*/}*',
+ '!<%= yeoman.dist %>/.git{,*/}*'
+ ]
+ }]
+ },
+ server: '.tmp'
+ },
+
+ // Add vendor prefixed styles
+ postcss: {
+ options: {
+ processors: [
+ require('autoprefixer-core')({ browsers: ['last 1 version'] })
+ ]
+ },
+ server: {
+ options: {
+ map: true
+ },
+ files: [{
+ expand: true,
+ cwd: '.tmp/styles/',
+ src: '{,*/}*.css',
+ dest: '.tmp/styles/'
+ }]
+ },
+ dist: {
+ files: [{
+ expand: true,
+ cwd: '.tmp/styles/',
+ src: '{,*/}*.css',
+ dest: '.tmp/styles/'
+ }]
+ }
+ },
+
+ // Automatically inject Bower components into the app
+ wiredep: {
+ app: {
+ src: ['<%= yeoman.app %>/index.html'],
+ ignorePath: /\.\.\//
+ },
+ test: {
+ devDependencies: true,
+ src: '<%= karma.unit.configFile %>',
+ ignorePath: /\.\.\//,
+ fileTypes: {
+ js: {
+ block: /(([\s\t]*)\/{2}\s*?bower:\s*?(\S*))(\n|\r|.)*?(\/{2}\s*endbower)/gi,
+ detect: {
+ js: /'(.*\.js)'/gi
+ },
+ replace: {
+ js: '\'{{filePath}}\','
+ }
+ }
+ }
+ }
+ },
+
+ // Renames files for browser caching purposes
+ filerev: {
+ dist: {
+ src: [
+ '<%= yeoman.dist %>/scripts/{,*/}*.js',
+ '<%= yeoman.dist %>/styles/{,*/}*.css',
+ '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
+ '<%= yeoman.dist %>/styles/fonts/*'
+ ]
+ }
+ },
+
+ // Reads HTML for usemin blocks to enable smart builds that automatically
+ // concat, minify and revision files. Creates configurations in memory so
+ // additional tasks can operate on them
+ useminPrepare: {
+ html: '<%= yeoman.app %>/index.html',
+ options: {
+ dest: '<%= yeoman.dist %>',
+ flow: {
+ html: {
+ steps: {
+ js: ['concat', 'uglifyjs'],
+ css: ['cssmin']
+ },
+ post: {}
+ }
+ }
+ }
+ },
+
+ // Performs rewrites based on filerev and the useminPrepare configuration
+ usemin: {
+ html: ['<%= yeoman.dist %>/{,*/}*.html'],
+ css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
+ js: ['<%= yeoman.dist %>/scripts/{,*/}*.js'],
+ options: {
+ assetsDirs: [
+ '<%= yeoman.dist %>',
+ '<%= yeoman.dist %>/images',
+ '<%= yeoman.dist %>/styles'
+ ],
+ patterns: {
+ js: [
+ [/(images\/[^''""]*\.(png|jpg|jpeg|gif|webp|svg))/g, 'Replacing references to images']
+ ]
+ }
+ }
+ },
+
+ // The following *-min tasks will produce minified files in the dist folder
+ // By default, your `index.html`'s <!-- Usemin block --> will take care of
+ // minification. These next options are pre-configured if you do not wish
+ // to use the Usemin blocks.
+ // cssmin: {
+ // dist: {
+ // files: {
+ // '<%= yeoman.dist %>/styles/main.css': [
+ // '.tmp/styles/{,*/}*.css'
+ // ]
+ // }
+ // }
+ // },
+ // uglify: {
+ // dist: {
+ // files: {
+ // '<%= yeoman.dist %>/scripts/scripts.js': [
+ // '<%= yeoman.dist %>/scripts/scripts.js'
+ // ]
+ // }
+ // }
+ // },
+ // concat: {
+ // dist: {}
+ // },
+
+ imagemin: {
+ dist: {
+ files: [{
+ expand: true,
+ cwd: '<%= yeoman.app %>/images',
+ src: '{,*/}*.{png,jpg,jpeg,gif}',
+ dest: '<%= yeoman.dist %>/images'
+ }]
+ }
+ },
+
+ svgmin: {
+ dist: {
+ files: [{
+ expand: true,
+ cwd: '<%= yeoman.app %>/images',
+ src: '{,*/}*.svg',
+ dest: '<%= yeoman.dist %>/images'
+ }]
+ }
+ },
+
+ htmlmin: {
+ dist: {
+ options: {
+ collapseWhitespace: true,
+ conservativeCollapse: true,
+ collapseBooleanAttributes: true,
+ removeCommentsFromCDATA: true
+ },
+ files: [{
+ expand: true,
+ cwd: '<%= yeoman.dist %>',
+ src: ['*.html'],
+ dest: '<%= yeoman.dist %>'
+ }]
+ }
+ },
+
+ ngtemplates: {
+ dist: {
+ options: {
+ module: 'yardStickGui2App',
+ htmlmin: '<%= htmlmin.dist.options %>',
+ usemin: 'scripts/scripts.js'
+ },
+ cwd: '<%= yeoman.app %>',
+ src: 'views/{,*/}*.html',
+ dest: '.tmp/templateCache.js'
+ }
+ },
+
+ // ng-annotate tries to make the code safe for minification automatically
+ // by using the Angular long form for dependency injection.
+ ngAnnotate: {
+ dist: {
+ files: [{
+ expand: true,
+ cwd: '.tmp/concat/scripts',
+ src: '*.js',
+ dest: '.tmp/concat/scripts'
+ }]
+ }
+ },
+
+ // Replace Google CDN references
+ cdnify: {
+ dist: {
+ html: ['<%= yeoman.dist %>/*.html']
+ }
+ },
+
+ // Copies remaining files to places other tasks can use
+ copy: {
+ dist: {
+ files: [{
+ expand: true,
+ dot: true,
+ cwd: '<%= yeoman.app %>',
+ dest: '<%= yeoman.dist %>',
+ src: [
+ '*.{ico,png,txt}',
+ '*.html',
+ 'images/{,*/}*.{webp}',
+ 'styles/fonts/{,*/}*.*'
+ ]
+ }, {
+ expand: true,
+ cwd: '.tmp/images',
+ dest: '<%= yeoman.dist %>/images',
+ src: ['generated/*']
+ }, {
+ expand: true,
+ cwd: 'bower_components/bootstrap/dist',
+ src: 'fonts/*',
+ dest: '<%= yeoman.dist %>'
+ },
+ {
+ expand: true,
+ cwd: 'bower_components/components-font-awesome',
+ src: 'fonts/*',
+ dest: '<%=yeoman.dist%>'
+ }
+ ]
+ },
+ styles: {
+ expand: true,
+ cwd: '<%= yeoman.app %>/styles',
+ dest: '.tmp/styles/',
+ src: '{,*/}*.css'
+ }
+ },
+
+ // Run some tasks in parallel to speed up the build process
+ concurrent: {
+ server: [
+ 'copy:styles'
+ ],
+ test: [
+ 'copy:styles'
+ ],
+ dist: [
+ 'copy:styles',
+ 'imagemin',
+ 'svgmin'
+ ]
+ },
+
+ // Test settings
+ karma: {
+ unit: {
+ configFile: 'test/karma.conf.js',
+ singleRun: true
+ }
+ }
+ });
+
+
+ grunt.registerTask('serve', 'Compile then start a connect web server', function(target) {
+ if (target === 'dist') {
+ return grunt.task.run(['build', 'connect:dist:keepalive']);
+ }
+
+ grunt.task.run([
+ 'clean:server',
+ 'wiredep',
+ 'concurrent:server',
+ 'postcss:server',
+ 'connect:livereload',
+ 'watch'
+ ]);
+ });
+
+ grunt.registerTask('server', 'DEPRECATED TASK. Use the "serve" task instead', function(target) {
+ grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
+ grunt.task.run(['serve:' + target]);
+ });
+
+ grunt.registerTask('test', [
+ 'clean:server',
+ 'wiredep',
+ 'concurrent:test',
+ 'postcss',
+ 'connect:test',
+ 'karma'
+ ]);
+
+ grunt.registerTask('build', [
+ 'clean:dist',
+ 'wiredep',
+ 'useminPrepare',
+ 'concurrent:dist',
+ 'postcss',
+ 'ngtemplates',
+ 'concat',
+ 'ngAnnotate',
+ 'copy:dist',
+ // 'cdnify',
+ 'cssmin',
+ 'uglify',
+ 'filerev',
+ 'usemin',
+ 'htmlmin'
+ ]);
+
+ grunt.registerTask('default', [
+ 'newer:jshint',
+ 'newer:jscs',
+ 'test',
+ 'build'
+ ]);
+};
diff --git a/gui/app/404.html b/gui/app/404.html
new file mode 100644
index 000000000..899828a3c
--- /dev/null
+++ b/gui/app/404.html
@@ -0,0 +1,152 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Page Not Found :(</title>
+ <style>
+ ::-moz-selection {
+ background: #b3d4fc;
+ text-shadow: none;
+ }
+
+ ::selection {
+ background: #b3d4fc;
+ text-shadow: none;
+ }
+
+ html {
+ padding: 30px 10px;
+ font-size: 20px;
+ line-height: 1.4;
+ color: #737373;
+ background: #f0f0f0;
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+ }
+
+ html,
+ input {
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ }
+
+ body {
+ max-width: 500px;
+ padding: 30px 20px 50px;
+ border: 1px solid #b3b3b3;
+ border-radius: 4px;
+ margin: 0 auto;
+ box-shadow: 0 1px 10px #a7a7a7, inset 0 1px 0 #fff;
+ background: #fcfcfc;
+ }
+
+ h1 {
+ margin: 0 10px;
+ font-size: 50px;
+ text-align: center;
+ }
+
+ h1 span {
+ color: #bbb;
+ }
+
+ h3 {
+ margin: 1.5em 0 0.5em;
+ }
+
+ p {
+ margin: 1em 0;
+ }
+
+ ul {
+ padding: 0 0 0 40px;
+ margin: 1em 0;
+ }
+
+ .container {
+ max-width: 380px;
+ margin: 0 auto;
+ }
+
+ /* google search */
+
+ #goog-fixurl ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ }
+
+ #goog-fixurl form {
+ margin: 0;
+ }
+
+ #goog-wm-qt,
+ #goog-wm-sb {
+ border: 1px solid #bbb;
+ font-size: 16px;
+ line-height: normal;
+ vertical-align: top;
+ color: #444;
+ border-radius: 2px;
+ }
+
+ #goog-wm-qt {
+ width: 220px;
+ height: 20px;
+ padding: 5px;
+ margin: 5px 10px 0 0;
+ box-shadow: inset 0 1px 1px #ccc;
+ }
+
+ #goog-wm-sb {
+ display: inline-block;
+ height: 32px;
+ padding: 0 10px;
+ margin: 5px 0 0;
+ white-space: nowrap;
+ cursor: pointer;
+ background-color: #f5f5f5;
+ background-image: -webkit-linear-gradient(rgba(255,255,255,0), #f1f1f1);
+ background-image: -moz-linear-gradient(rgba(255,255,255,0), #f1f1f1);
+ background-image: -ms-linear-gradient(rgba(255,255,255,0), #f1f1f1);
+ background-image: -o-linear-gradient(rgba(255,255,255,0), #f1f1f1);
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ }
+
+ #goog-wm-sb:hover,
+ #goog-wm-sb:focus {
+ border-color: #aaa;
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
+ background-color: #f8f8f8;
+ }
+
+ #goog-wm-qt:hover,
+ #goog-wm-qt:focus {
+ border-color: #105cb6;
+ outline: 0;
+ color: #222;
+ }
+
+ input::-moz-focus-inner {
+ padding: 0;
+ border: 0;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="container">
+ <h1>Not found <span>:(</span></h1>
+ <p>Sorry, but the page you were trying to view does not exist.</p>
+ <p>It looks like this was the result of either:</p>
+ <ul>
+ <li>a mistyped address</li>
+ <li>an out-of-date link</li>
+ </ul>
+ <script>
+ var GOOG_FIXURL_LANG = (navigator.language || '').slice(0,2),GOOG_FIXURL_SITE = location.host;
+ </script>
+ <script src="//linkhelp.clients.google.com/tbproxy/lh/wm/fixurl.js"></script>
+ </div>
+ </body>
+</html>
diff --git a/gui/app/favicon.ico b/gui/app/favicon.ico
new file mode 100644
index 000000000..652790530
--- /dev/null
+++ b/gui/app/favicon.ico
Binary files differ
diff --git a/gui/app/images/back.png b/gui/app/images/back.png
new file mode 100644
index 000000000..917c86edd
--- /dev/null
+++ b/gui/app/images/back.png
Binary files differ
diff --git a/gui/app/images/checkno.png b/gui/app/images/checkno.png
new file mode 100644
index 000000000..7c6841930
--- /dev/null
+++ b/gui/app/images/checkno.png
Binary files differ
diff --git a/gui/app/images/checkyes.png b/gui/app/images/checkyes.png
new file mode 100644
index 000000000..ef6028310
--- /dev/null
+++ b/gui/app/images/checkyes.png
Binary files differ
diff --git a/gui/app/images/close.png b/gui/app/images/close.png
new file mode 100644
index 000000000..0d2c14252
--- /dev/null
+++ b/gui/app/images/close.png
Binary files differ
diff --git a/gui/app/images/loading.gif b/gui/app/images/loading.gif
new file mode 100644
index 000000000..b04dd11bc
--- /dev/null
+++ b/gui/app/images/loading.gif
Binary files differ
diff --git a/gui/app/images/loading2.gif b/gui/app/images/loading2.gif
new file mode 100644
index 000000000..9d1534468
--- /dev/null
+++ b/gui/app/images/loading2.gif
Binary files differ
diff --git a/gui/app/images/statusno.png b/gui/app/images/statusno.png
new file mode 100644
index 000000000..ace4a454d
--- /dev/null
+++ b/gui/app/images/statusno.png
Binary files differ
diff --git a/gui/app/images/statusyes.png b/gui/app/images/statusyes.png
new file mode 100644
index 000000000..d88a99ee8
--- /dev/null
+++ b/gui/app/images/statusyes.png
Binary files differ
diff --git a/gui/app/images/url.json b/gui/app/images/url.json
new file mode 100644
index 000000000..f16c4e0ac
--- /dev/null
+++ b/gui/app/images/url.json
@@ -0,0 +1 @@
+{"url": "192.168.23.2:1948"} \ No newline at end of file
diff --git a/gui/app/images/yeoman.png b/gui/app/images/yeoman.png
new file mode 100644
index 000000000..92497addf
--- /dev/null
+++ b/gui/app/images/yeoman.png
Binary files differ
diff --git a/gui/app/index.html b/gui/app/index.html
new file mode 100644
index 000000000..5592656cc
--- /dev/null
+++ b/gui/app/index.html
@@ -0,0 +1,111 @@
+<!doctype html>
+<html>
+
+<head>
+ <meta charset="utf-8">
+ <title></title>
+ <meta name="description" content="">
+ <meta name="viewport" content="width=device-width">
+ <!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
+ <!-- build:css(.) styles/vendor.css -->
+ <!-- bower:css -->
+ <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css" />
+ <link rel="stylesheet" href="bower_components/angular-wizard/dist/angular-wizard.min.css" />
+ <link rel="stylesheet" href="bower_components/AngularJS-Toaster/toaster.css" />
+ <link rel="stylesheet" href="bower_components/ng-dialog/css/ngDialog.css" />
+ <link rel="stylesheet" href="bower_components/ng-dialog/css/ngDialog-theme-default.css" />
+ <link rel="stylesheet" href="bower_components/components-font-awesome/css/font-awesome.css" />
+ <link rel="stylesheet" href="bower_components/v-accordion/dist/v-accordion.css" />
+ <link rel="stylesheet" href="bower_components/angular-loading/angular-loading.css" />
+ <!-- endbower -->
+ <!-- endbuild -->
+ <!-- build:css(.tmp) styles/main.css -->
+ <link rel="stylesheet" href="styles/main.css">
+
+
+ <!-- endbuild -->
+</head>
+
+<script>
+// read file
+
+
+</script>
+
+<body ng-app="yardStickGui2App">
+ <!--[if lte IE 8]>
+ <p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
+ <![endif]-->
+
+
+
+
+ <div ui-view></div>
+
+
+
+
+ <!-- Google Analytics: change UA-XXXXX-X to be your site's ID -->
+ <!--<script>
+ ! function(A, n, g, u, l, a, r) {
+ A.GoogleAnalyticsObject = l, A[l] = A[l] || function() {
+ (A[l].q = A[l].q || []).push(arguments)
+ }, A[l].l = +new Date, a = n.createElement(g),
+ r = n.getElementsByTagName(g)[0], a.src = u, r.parentNode.insertBefore(a, r)
+ }(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
+
+ ga('create', 'UA-XXXXX-X');
+ ga('send', 'pageview');
+ </script>-->
+
+ <!-- build:js(.) scripts/vendor.js -->
+ <!-- bower:js -->
+ <script src="bower_components/jquery/dist/jquery.js"></script>
+ <script src="bower_components/angular/angular.js"></script>
+ <script src="bower_components/bootstrap/dist/js/bootstrap.js"></script>
+ <script src="bower_components/angular-strap/dist/angular-strap.js"></script>
+ <script src="bower_components/angular-strap/dist/angular-strap.tpl.js"></script>
+ <script src="bower_components/angular-ui-router/release/angular-ui-router.js"></script>
+ <script src="bower_components/angular-animate/angular-animate.js"></script>
+ <script src="bower_components/angular-breadcrumb/release/angular-breadcrumb.js"></script>
+ <script src="bower_components/angular-wizard/dist/angular-wizard.min.js"></script>
+ <script src="bower_components/angular-resource/angular-resource.js"></script>
+ <script src="bower_components/ng-file-upload/ng-file-upload.js"></script>
+ <script src="bower_components/AngularJS-Toaster/toaster.js"></script>
+ <script src="bower_components/ng-dialog/js/ngDialog.js"></script>
+ <script src="bower_components/angularUtils-pagination/dirPagination.js"></script>
+ <script src="bower_components/ngstorage/ngStorage.js"></script>
+ <script src="bower_components/v-accordion/dist/v-accordion.js"></script>
+ <script src="bower_components/spin.js/spin.js"></script>
+ <script src="bower_components/angular-loading/angular-loading.js"></script>
+ <script src="bower_components/spin.js/spin.js"></script>
+ <script src="bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script>
+ <script src="bower_components/angular-sanitize/angular-sanitize.js"></script>
+ <!-- endbower -->
+ <!-- endbuild -->
+
+ <!-- build:js({.tmp,app}) scripts/scripts.js -->
+ <script src="scripts/app.js"></script>
+ <script src="scripts/router.config.js"></script>
+ <script src="scripts/controllers/main.js"></script>
+ <script src="scripts/factory/main.factory.js"></script>
+ <script src="scripts/controllers/content.controller.js"></script>
+ <script src="scripts/controllers/detail.controller.js"></script>
+ <script src="scripts/controllers/image.controller.js"></script>
+ <script src="scripts/controllers/pod.controller.js"></script>
+ <script src="scripts/controllers/container.controller.js"></script>
+ <script src="scripts/controllers/testcase.controller.js"></script>
+ <script src="scripts/controllers/testcasedetail.controller.js"></script>
+ <script src="scripts/controllers/testsuit.controller.js"></script>
+ <script src="scripts/controllers/suitedetail.controller.js"></script>
+ <script src="scripts/controllers/suitecreate.controller.js"></script>
+ <script src="scripts/controllers/task.controller.js"></script>
+ <script src="scripts/controllers/report.controller.js"></script>
+ <script src="scripts/controllers/project.controller.js"></script>
+ <script src="scripts/controllers/projectDetail.controller.js"></script>
+ <script src="scripts/controllers/taskModify.controller.js"></script>
+
+ <!-- endbuild -->
+</body>
+
+</html>
diff --git a/gui/app/robots.txt b/gui/app/robots.txt
new file mode 100644
index 000000000..4d521f952
--- /dev/null
+++ b/gui/app/robots.txt
@@ -0,0 +1,4 @@
+# robotstxt.org
+
+User-agent: *
+Disallow:
diff --git a/gui/app/scripts/app.js b/gui/app/scripts/app.js
new file mode 100644
index 000000000..ecb642c95
--- /dev/null
+++ b/gui/app/scripts/app.js
@@ -0,0 +1,30 @@
+'use strict';
+
+/**
+ * @ngdoc overview
+ * @name yardStickGui2App
+ * @description
+ * # yardStickGui2App
+ *
+ * Main module of the application.
+ */
+angular
+ .module('yardStickGui2App', [
+ 'ui.router',
+ 'ngAnimate',
+ 'ngSanitize',
+ 'mgcrea.ngStrap',
+ 'ncy-angular-breadcrumb',
+ 'mgo-angular-wizard',
+ 'ngResource',
+ 'ngFileUpload',
+ 'toaster',
+ 'ngDialog',
+ 'angularUtils.directives.dirPagination',
+ 'ngStorage',
+ 'vAccordion',
+ 'darthwade.dwLoading',
+ 'ui.bootstrap'
+
+
+ ]);
diff --git a/gui/app/scripts/controllers/container.controller.js b/gui/app/scripts/controllers/container.controller.js
new file mode 100644
index 000000000..6c2ccd8ff
--- /dev/null
+++ b/gui/app/scripts/controllers/container.controller.js
@@ -0,0 +1,182 @@
+'use strict';
+
+angular.module('yardStickGui2App')
+ .controller('ContainerController', ['$scope', '$state', '$stateParams', 'mainFactory', 'Upload', 'toaster', 'ngDialog',
+ function($scope, $state, $stateParams, mainFactory, Upload, toaster, ngDialog) {
+
+
+ init();
+ $scope.showloading = false;
+
+ $scope.displayContainerInfo = [];
+ $scope.containerList = [{ value: 'create_influxdb', name: "InfluxDB" }, { value: 'create_grafana', name: "Grafana" }]
+
+ function init() {
+
+
+ $scope.uuid = $stateParams.uuid;
+ $scope.createContainer = createContainer;
+ $scope.openChooseContainnerDialog = openChooseContainnerDialog;
+
+
+ getItemIdDetail();
+
+ }
+
+ function getItemIdDetail() {
+ $scope.displayContainerInfo = [];
+ mainFactory.ItemDetail().get({
+ 'envId': $scope.uuid
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.envName = response.result.environment.name;
+ $scope.containerId = response.result.environment.container_id;
+ if ($scope.containerId != null) {
+
+ var keysArray = Object.keys($scope.containerId);
+ for (var k in $scope.containerId) {
+ getConDetail($scope.containerId[k]);
+ }
+ } else {
+ $scope.podData = null;
+ }
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ function getConDetail(id) {
+ mainFactory.containerDetail().get({
+ 'containerId': id
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ // $scope.podData = response.result;
+ response.result.container['id'] = id;
+ $scope.displayContainerInfo.push(response.result.container);
+
+ }
+
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+
+ }
+
+ function createContainer() {
+
+ $scope.showloading = true;
+ mainFactory.runAcontainer().post({
+ 'action': $scope.selectContainer.value,
+ 'args': {
+ 'environment_id': $scope.uuid,
+ }
+ }).$promise.then(function(response) {
+ $scope.showloading = false;
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'create container success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ setTimeout(function() {
+ getItemIdDetail();
+ }, 10000);
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'Wrong',
+ body: response.error_msg,
+ timeout: 3000
+ });
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+
+ })
+ }
+
+ function openChooseContainnerDialog() {
+ ngDialog.open({
+ template: 'views/modal/chooseContainer.html',
+ scope: $scope,
+ className: 'ngdialog-theme-default',
+ width: 500,
+ showClose: true,
+ closeByDocument: false
+ })
+ }
+
+ function chooseResult(name) {
+ $scope.selectContainer = name;
+ }
+ $scope.goBack = function goBack() {
+ $state.go('app2.projectList');
+ }
+
+ $scope.openDeleteEnv = function openDeleteEnv(id, name) {
+ $scope.deleteName = name;
+ $scope.deleteId = id;
+ ngDialog.open({
+ template: 'views/modal/deleteConfirm.html',
+ scope: $scope,
+ className: 'ngdialog-theme-default',
+ width: 500,
+ showClose: true,
+ closeByDocument: false
+ })
+
+ }
+
+ $scope.deleteContainer = function deleteContainer() {
+ mainFactory.deleteContainer().delete({ 'containerId': $scope.deleteId }).$promise.then(function(response) {
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'delete container success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ ngDialog.close();
+ getItemIdDetail();
+
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'Wrong',
+ body: response.error_msg,
+ timeout: 3000
+ });
+ }
+
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+
+
+ }
+ ]);
diff --git a/gui/app/scripts/controllers/content.controller.js b/gui/app/scripts/controllers/content.controller.js
new file mode 100644
index 000000000..d2bc19eea
--- /dev/null
+++ b/gui/app/scripts/controllers/content.controller.js
@@ -0,0 +1,136 @@
+'use strict';
+
+angular.module('yardStickGui2App')
+ .controller('ContentController', ['$scope', '$state', '$stateParams', 'mainFactory', 'Upload', 'toaster', '$location', '$localStorage',
+ function($scope, $state, $stateParams, mainFactory, Upload, toaster, $location, $localStorage) {
+
+
+
+
+ init();
+ $scope.showEnvironment = false;
+ $scope.counldGoDetail = false;
+ $scope.activeStatus = 0;
+
+ $scope.$watch(function() {
+ return location.hash
+ }, function(newvalue, oldvalue) {
+ if (location.hash.indexOf('project') > -1) {
+ $scope.projectShow = true;
+ $scope.taskShow = false;
+ $scope.reportShow = false;
+ } else if (location.hash.indexOf('task') > -1) {
+ $scope.taskShow = true;
+ $scope.projectShow = true;
+ } else if (location.hash.indexOf('report') > -1) {
+ $scope.reportShow = true;
+ $scope.taskShow = true;
+ $scope.projectShow = true;
+ }
+
+ })
+
+
+ function init() {
+
+
+ $scope.showEnvironments = showEnvironments;
+ $scope.showSteps = $location.path().indexOf('project');
+ $scope.test = test;
+ $scope.gotoUploadPage = gotoUploadPage;
+ $scope.gotoOpenrcPage = gotoOpenrcPage;
+ $scope.gotoPodPage = gotoPodPage;
+ $scope.gotoContainerPage = gotoContainerPage;
+ $scope.gotoTestcase = gotoTestcase;
+ $scope.gotoEnviron = gotoEnviron;
+ $scope.gotoSuite = gotoSuite;
+ $scope.gotoProject = gotoProject;
+ $scope.gotoTask = gotoTask;
+ $scope.gotoReport = gotoReport;
+ $scope.stepsStatus = $localStorage.stepsStatus;
+ $scope.goBack = goBack;
+
+
+ }
+
+
+
+ function showEnvironments() {
+ $scope.showEnvironment = true;
+ }
+
+ function test() {
+ alert('test');
+ }
+
+ function gotoOpenrcPage() {
+ $scope.path = $location.path();
+ $scope.uuid = $scope.path.split('/').pop();
+ $state.go('app.environmentDetail', { uuid: $scope.uuid })
+ }
+
+ function gotoUploadPage() {
+ $scope.path = $location.path();
+ $scope.uuid = $scope.path.split('/').pop();
+ $state.go('app.uploadImage', { uuid: $scope.uuid });
+ }
+
+ function gotoPodPage() {
+ $scope.path = $location.path();
+ $scope.uuid = $scope.path.split('/').pop();
+ $state.go('app.podUpload', { uuid: $scope.uuid });
+ }
+
+ function gotoContainerPage() {
+ $scope.path = $location.path();
+ $scope.uuid = $scope.path.split('/').pop();
+ $state.go('app.container', { uuid: $scope.uuid });
+ }
+
+ function gotoTestcase() {
+ $state.go('app2.testcase');
+ }
+
+ function gotoEnviron() {
+ if ($location.path().indexOf('env') > -1 || $location.path().indexOf('environment') > -1) {
+ $scope.counldGoDetail = true;
+ }
+ $state.go('app2.environment');
+ }
+
+ function gotoSuite() {
+ $state.go('app2.testsuite');
+ }
+
+ function gotoProject() {
+ $state.go('app2.projectList');
+ }
+
+ function gotoTask() {
+ $state.go('app2.tasklist');
+ }
+
+ function gotoReport() {
+ $state.go('app2.report');
+ }
+
+ function goBack() {
+ if ($location.path().indexOf('main/environment')) {
+ return;
+ } else if ($location.path().indexOf('main/envDetail/') || $location.path().indexOf('main/imageDetail/') ||
+ $location.path().indexOf('main/podupload/') || $location.path().indexOf('main/container/')) {
+ $state.go('app2.environment');
+ return;
+ } else {
+ window.history.back();
+ }
+
+ }
+
+
+
+
+
+
+ }
+ ]); \ No newline at end of file
diff --git a/gui/app/scripts/controllers/detail.controller.js b/gui/app/scripts/controllers/detail.controller.js
new file mode 100644
index 000000000..3e2eaa100
--- /dev/null
+++ b/gui/app/scripts/controllers/detail.controller.js
@@ -0,0 +1,384 @@
+'use strict';
+
+angular.module('yardStickGui2App')
+ .controller('DetailController', ['$scope', '$state', '$stateParams', 'mainFactory', 'Upload', 'toaster', '$location', 'ngDialog',
+ function($scope, $state, $stateParams, mainFactory, Upload, toaster, $location, ngDialog) {
+
+
+
+
+ init();
+ $scope.showEnvironment = false;
+ $scope.envInfo = [];
+
+ function init() {
+ $scope.showEnvironments = showEnvironments;
+ // $scope.openrcID = $stateParams.uuid;
+ $scope.deleteEnvItem = deleteEnvItem;
+ $scope.addInfo = addInfo;
+ $scope.submitOpenRcFile = submitOpenRcFile;
+ $scope.uploadFiles = uploadFiles;
+ $scope.addEnvironment = addEnvironment;
+
+ $scope.uuid = $stateParams.uuid;
+ $scope.openrcID = $stateParams.opercId;
+ $scope.imageID = $stateParams.imageId;
+ $scope.podID = $stateParams.podId;
+ $scope.containerId = $stateParams.containerId;
+ $scope.ifNew = $stateParams.ifNew;
+
+
+ getItemIdDetail();
+ }
+
+
+
+ function showEnvironments() {
+ $scope.showEnvironment = true;
+ }
+
+
+ function deleteEnvItem(index) {
+ $scope.envInfo.splice(index, 1);
+ }
+
+ function addInfo() {
+ var tempKey = null;
+ var tempValue = null;
+ var temp = {
+ name: tempKey,
+ value: tempValue
+ }
+ $scope.envInfo.push(temp);
+
+ }
+
+ function submitOpenRcFile() {
+ $scope.showloading = true;
+
+ var postData = {};
+ postData['action'] = 'update_openrc';
+ rebuildEnvInfo();
+ postData['args'] = {};
+ postData['args']['openrc'] = $scope.postEnvInfo;
+ postData['args']['environment_id'] = $scope.uuid;
+
+
+ mainFactory.postEnvironmentVariable().post(postData).$promise.then(function(response) {
+ $scope.showloading = false;
+
+ if (response.status == 1) {
+
+ $scope.openrcInfo = response.result;
+ toaster.pop({
+ type: 'success',
+ title: 'create success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ $scope.showEnvrionment = true;
+ getItemIdDetail();
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'faile',
+ body: response.error_msg,
+ timeout: 3000
+ });
+ }
+
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ //reconstruc EnvInfo
+ function rebuildEnvInfo() {
+ $scope.postEnvInfo = {};
+ for (var i = 0; i < $scope.envInfo.length; i++) {
+ $scope.postEnvInfo[$scope.envInfo[i].name] = $scope.envInfo[i].value;
+ }
+
+ }
+
+ //buildtoEnvInfo
+ function buildToEnvInfo(object) {
+ var tempKeyArray = Object.keys(object);
+
+ for (var i = 0; i < tempKeyArray.length; i++) {
+ var tempkey = tempKeyArray[i];
+ var tempValue = object[tempKeyArray[i]];
+ var temp = {
+ name: tempkey,
+ value: tempValue
+ };
+ $scope.envInfo.push(temp);
+ }
+ }
+
+ function uploadFiles($file, $invalidFiles) {
+ $scope.openrcInfo = {};
+ $scope.loadingOPENrc = true;
+
+ $scope.displayOpenrcFile = $file;
+ timeConstruct($scope.displayOpenrcFile.lastModified);
+ Upload.upload({
+ url: Base_URL + '/api/v2/yardstick/openrcs',
+ data: { file: $file, 'environment_id': $scope.uuid, 'action': 'upload_openrc' }
+ }).then(function(response) {
+
+ $scope.loadingOPENrc = false;
+ if (response.data.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'upload success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ $scope.openrcInfo = response.data.result;
+ getItemIdDetail();
+
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'faile',
+ body: response.error_msg,
+ timeout: 3000
+ });
+ }
+
+ }, function(error) {
+ $scope.uploadfile = null;
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ function timeConstruct(array) {
+ var date = new Date(1398250549490);
+ var Y = date.getFullYear() + '-';
+ var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
+ var D = date.getDate() + ' ';
+ var h = date.getHours() + ':';
+ var m = date.getMinutes() + ':';
+ var s = date.getSeconds();
+ $scope.filelastModified = Y + M + D + h + m + s;
+
+ }
+
+ function addEnvironment() {
+ mainFactory.addEnvName().post({
+ 'action': 'create_environment',
+ args: {
+ 'name': $scope.baseElementInfo.name
+ }
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'create name success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ $scope.uuid = response.result.uuid;
+ var path = $location.path();
+ path = path + $scope.uuid;
+ $location.url(path);
+ getItemIdDetail();
+ }
+
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ function getItemIdDetail() {
+
+ mainFactory.ItemDetail().get({
+ 'envId': $scope.uuid
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.baseElementInfo = response.result.environment;
+
+
+ if ($scope.ifNew != 'true') {
+ $scope.baseElementInfo = response.result.environment;
+ if ($scope.baseElementInfo.openrc_id != null) {
+ getOpenrcDetail($scope.baseElementInfo.openrc_id);
+ }
+ }
+
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: response.error_msg,
+ timeout: 3000
+ });
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+
+ })
+ }
+ //getopenRcid
+ function getOpenrcDetail(openrcId) {
+ mainFactory.getEnvironmentDetail().get({
+ 'openrc_id': openrcId
+ }).$promise.then(function(response) {
+ $scope.openrcInfo = response.result;
+ buildToEnvInfo($scope.openrcInfo.openrc)
+ }, function(response) {
+
+ })
+ }
+
+
+ //getImgDetail
+ function getImageDetail() {
+ mainFactory.ImageDetail().get({
+ 'image_id': $scope.baseElementInfo.image_id
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.imageDetail = response.result.image;
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ //getPodDetail
+ function getPodDetail() {
+ mainFactory.podDeatil().get({
+ 'podId': $scope.baseElementInfo.pod_id
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.podDetail = response.result.pod;
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+ //getContainerDetail
+ function getPodDetail(containerId) {
+ mainFactory.containerDetail().get({
+ 'containerId': containerId
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.podDetail = response.result.pod;
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: response.error_msg,
+ timeout: 3000
+ });
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+ $scope.goBack = function goBack() {
+ window.history.back();
+ }
+
+ $scope.goNext = function goNext() {
+ $scope.path = $location.path();
+ $scope.uuid = $scope.path.split('/').pop();
+ $state.go('app.uploadImage', { uuid: $scope.uuid });
+ }
+
+ $scope.openDeleteEnv = function openDeleteEnv(id, name) {
+ $scope.deleteName = name;
+ $scope.deleteId = id;
+ ngDialog.open({
+ template: 'views/modal/deleteConfirm.html',
+ scope: $scope,
+ className: 'ngdialog-theme-default',
+ width: 500,
+ showClose: true,
+ closeByDocument: false
+ })
+
+ }
+
+ $scope.deleteOpenRc = function deleteOpenRc() {
+ mainFactory.deleteOpenrc().delete({ 'openrc': $scope.baseElementInfo.openrc_id }).$promise.then(function(response) {
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'delete openrc success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ ngDialog.close();
+ getItemIdDetail();
+ $scope.openrcInfo = null;
+ $scope.envInfo = [];
+ $scope.displayOpenrcFile = null;
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'Wrong',
+ body: response.result,
+ timeout: 3000
+ });
+ }
+
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+
+
+
+
+
+
+
+
+ }
+
+
+ ]);
diff --git a/gui/app/scripts/controllers/image.controller.js b/gui/app/scripts/controllers/image.controller.js
new file mode 100644
index 000000000..53acff405
--- /dev/null
+++ b/gui/app/scripts/controllers/image.controller.js
@@ -0,0 +1,166 @@
+'use strict';
+
+angular.module('yardStickGui2App')
+ .controller('ImageController', ['$scope', '$state', '$stateParams', 'mainFactory', 'Upload', 'toaster', '$location', '$interval',
+ function($scope, $state, $stateParams, mainFactory, Upload, toaster, $location, $interval) {
+
+
+ init();
+ $scope.showloading = false;
+ $scope.ifshowStatus = 0;
+
+ function init() {
+
+
+ $scope.uuid = $stateParams.uuid;
+ $scope.uploadImage = uploadImage;
+ getItemIdDetail();
+ getImageListSimple();
+ }
+
+ function getItemIdDetail() {
+ mainFactory.ItemDetail().get({
+ 'envId': $stateParams.uuid
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.baseElementInfo = response.result.environment;
+
+
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: response.error_msg,
+ timeout: 3000
+ });
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ function getImageListSimple() {
+
+ mainFactory.ImageList().get({}).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.imageListData = response.result.images;
+ // $scope.imageStatus = response.result.status;
+
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'get data failed',
+ body: 'please retry',
+ timeout: 3000
+ });
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'get data failed',
+ body: 'please retry',
+ timeout: 3000
+ });
+ })
+ }
+
+
+ function getImageList() {
+ if ($scope.intervalImgae != undefined) {
+ $interval.cancel($scope.intervalImgae);
+ }
+ mainFactory.ImageList().get({}).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.imageListData = response.result.images;
+ $scope.imageStatus = response.result.status;
+
+ if ($scope.imageStatus == 0) {
+ $scope.intervalImgae = $interval(function() {
+ getImageList();
+ }, 5000);
+ } else if ($scope.intervalImgae != undefined) {
+ $interval.cancel($scope.intervalImgae);
+ }
+
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'get data failed',
+ body: 'please retry',
+ timeout: 3000
+ });
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'get data failed',
+ body: 'please retry',
+ timeout: 3000
+ });
+ })
+ }
+
+ function uploadImage() {
+ $scope.imageStatus = 0;
+ $interval.cancel($scope.intervalImgae);
+ $scope.ifshowStatus = 1;
+ $scope.showloading = true;
+ mainFactory.uploadImage().post({
+ 'action': 'load_image',
+ 'args': {
+ 'environment_id': $scope.uuid
+
+ }
+ }).$promise.then(function(response) {
+ $scope.showloading = false;
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'create success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ setTimeout(function() {
+ getImageList();
+ }, 10000);
+
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'failed',
+ body: 'something wrong',
+ timeout: 3000
+ });
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'failed',
+ body: 'something wrong',
+ timeout: 3000
+ });
+ })
+ }
+
+ $scope.goBack = function goBack() {
+ $state.go('app2.projectList');
+ }
+
+ $scope.goNext = function goNext() {
+ $scope.path = $location.path();
+ $scope.uuid = $scope.path.split('/').pop();
+ $state.go('app.podUpload', { uuid: $scope.uuid });
+ }
+
+
+
+
+
+ }
+ ]);
diff --git a/gui/app/scripts/controllers/main.js b/gui/app/scripts/controllers/main.js
new file mode 100644
index 000000000..e3e880e62
--- /dev/null
+++ b/gui/app/scripts/controllers/main.js
@@ -0,0 +1,725 @@
+'use strict';
+
+angular.module('yardStickGui2App')
+ .controller('MainCtrl', ['$scope', '$state', '$stateParams', 'mainFactory', 'Upload', 'toaster', 'ngDialog', '$localStorage', '$loading', '$interval',
+ function($scope, $state, $stateParams, mainFactory, Upload, toaster, ngDialog, $localStorage, $loading, $interval) {
+
+
+ init();
+ $scope.project = 0;
+ $scope.showloading = false;
+ $scope.showEnvrionment = false;
+ $scope.loadingOPENrc = false;
+ $scope.uuidEnv = null;
+ $scope.showPod = null;
+ $scope.showImage = null;
+ $scope.showContainer = null;
+ $scope.showNextOpenRc = null;
+ $scope.showNextPod = null;
+ $scope.displayContainerInfo = [];
+ $scope.containerList = [{ value: 'create_influxdb', name: "InfluxDB" }, { value: 'create_grafana', name: "Grafana" }]
+ $scope.items = [
+ 'The first choice!',
+ 'And another choice for you.',
+ 'but wait! A third!'
+ ];
+ $scope.$on('$destroy', function() {
+ $interval.cancel($scope.intervalImgae)
+ });
+ $scope.showImageStatus = 0;
+
+
+
+
+
+
+ function init() {
+
+
+ $scope.gotoProject = gotoProject;
+ $scope.gotoEnvironment = gotoEnvironment;
+ $scope.gotoTask = gotoTask;
+ $scope.gotoExcute = gotoExcute;
+ $scope.gotoReport = gotoReport;
+ $scope.deleteEnvItem = deleteEnvItem;
+ $scope.addInfo = addInfo;
+ $scope.submitOpenRcFile = submitOpenRcFile;
+ $scope.uploadFilesPod = uploadFilesPod;
+ $scope.uploadFiles = uploadFiles;
+ $scope.showEnvriomentStatus = showEnvriomentStatus;
+ $scope.openEnvironmentDialog = openEnvironmentDialog;
+ $scope.getEnvironmentList = getEnvironmentList;
+ $scope.gotoDetail = gotoDetail;
+ $scope.addEnvironment = addEnvironment;
+ $scope.createContainer = createContainer;
+ $scope.chooseResult = chooseResult;
+
+ getEnvironmentList();
+ // getImageList();
+
+ }
+
+ function gotoProject() {
+ $scope.project = 1;
+ }
+
+ function gotoEnvironment() {
+ $scope.project = 0;
+ }
+
+ function gotoTask() {
+ $scope.project = 2;
+ }
+
+ function gotoExcute() {
+ $scope.project = 3;
+
+ }
+
+ function gotoReport() {
+ $scope.project = 4;
+ }
+ $scope.skipPod = function skipPod() {
+ $scope.showContainer = 1;
+
+ }
+ $scope.skipContainer = function skipContainer() {
+ getEnvironmentList();
+ ngDialog.close();
+ }
+
+ $scope.goToImage = function goToImage() {
+ getImageListSimple();
+ $scope.showImage = 1;
+ }
+ $scope.goToPod = function goToPod() {
+ $scope.showPod = 1;
+ }
+ $scope.goToPodPrev = function goToPodPrev() {
+ $scope.showImage = null;
+
+ }
+ $scope.skipPodPrev = function skipPodPrev() {
+ $scope.showImage = 1;
+ $scope.showPod = null;
+
+ }
+ $scope.skipContainerPrev = function skipContainerPrev() {
+ $scope.showPod = 1;
+ $scope.showContainer = null;
+ }
+
+ $scope.envInfo = [
+ { name: 'OS_USERNAME', value: '' },
+ { name: 'OS_PASSWORD', value: '' },
+ { name: 'OS_TENANT_NAME', value: '' },
+ { name: 'EXTERNAL_NETWORK', value: '' }
+ ];
+
+
+ function deleteEnvItem(index) {
+ $scope.envInfo.splice(index, 1);
+ }
+
+ function addInfo() {
+ var tempKey = null;
+ var tempValue = null;
+ var temp = {
+ name: tempKey,
+ value: tempValue
+ }
+ $scope.envInfo.push(temp);
+
+ }
+
+ function submitOpenRcFile() {
+ $scope.showloading = true;
+
+ var postData = {};
+ postData['action'] = 'update_openrc';
+ rebuildEnvInfo();
+ postData['args'] = {};
+ postData.args["openrc"] = $scope.postEnvInfo;
+ postData.args['environment_id'] = $scope.uuidEnv;
+ mainFactory.postEnvironmentVariable().post(postData).$promise.then(function(response) {
+ $scope.showloading = false;
+
+ if (response.status == 1) {
+
+ $scope.openrcInfo = response.result;
+ toaster.pop({
+ type: 'success',
+ title: 'create success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ $scope.showEnvrionment = true;
+ // $scope.showImage = response.status;
+ $scope.showNextOpenRc = 1;
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: response.error_msg,
+ timeout: 3000
+ });
+ }
+
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+
+ })
+ }
+
+ function uploadFiles($file, $invalidFiles) {
+ $scope.openrcInfo = {};
+ $scope.loadingOPENrc = true;
+ $scope.displayOpenrcFile = $file;
+ timeConstruct($scope.displayOpenrcFile.lastModified);
+ Upload.upload({
+ url: Base_URL + '/api/v2/yardstick/openrcs',
+ data: { file: $file, 'environment_id': $scope.uuidEnv, 'action': 'upload_openrc' }
+ }).then(function(response) {
+
+ $scope.loadingOPENrc = false;
+ if (response.data.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'upload success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ $scope.openrcInfo = response.data.result;
+
+ getItemIdDetailforOpenrc();
+ $scope.showNextOpenRc = 1;
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: response.error_msg,
+ timeout: 3000
+ });
+ }
+
+ }, function(error) {
+ $scope.uploadfile = null;
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ //reconstruc EnvInfo
+ function rebuildEnvInfo() {
+ $scope.postEnvInfo = {};
+ for (var i = 0; i < $scope.envInfo.length; i++) {
+ $scope.postEnvInfo[$scope.envInfo[i].name] = $scope.envInfo[i].value;
+ }
+
+ }
+ function uploadFilesPod($file, $invalidFiles) {
+ $scope.loadingOPENrc = true;
+
+ $scope.displayPodFile = $file;
+ timeConstruct($scope.displayPodFile.lastModified);
+ Upload.upload({
+ url: Base_URL + '/api/v2/yardstick/pods',
+ data: { file: $file, 'environment_id': $scope.uuidEnv, 'action': 'upload_pod_file' }
+ }).then(function(response) {
+
+ $scope.loadingOPENrc = false;
+ if (response.data.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'upload success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+
+ $scope.podData = response.data.result;
+
+
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: response.error_msg,
+ timeout: 3000
+ });
+ }
+
+ }, function(error) {
+ $scope.uploadfile = null;
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ function timeConstruct(array) {
+ var date = new Date(1398250549490);
+ var Y = date.getFullYear() + '-';
+ var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
+ var D = date.getDate() + ' ';
+ var h = date.getHours() + ':';
+ var m = date.getMinutes() + ':';
+ var s = date.getSeconds();
+ $scope.filelastModified = Y + M + D + h + m + s;
+
+ }
+
+ //display environment
+ function showEnvriomentStatus() {
+ $scope.showEnvironment = true;
+ }
+
+ //open Environment dialog
+ function openEnvironmentDialog() {
+ $scope.showEnvrionment = false;
+ $scope.loadingOPENrc = false;
+ $scope.uuidEnv = null;
+ $scope.showPod = null;
+ $scope.showImage = null;
+ $scope.showContainer = null;
+ $scope.showNextOpenRc = null;
+ $scope.showNextPod = null;
+ $scope.displayContainerInfo = [];
+
+ $scope.displayPodFile = null;
+ $scope.name = null;
+ $scope.openrcInfo = null;
+ $scope.envInfo = [
+ { name: 'OS_USERNAME', value: '' },
+ { name: 'OS_PASSWORD', value: '' },
+ { name: 'OS_TENANT_NAME', value: '' },
+ { name: 'EXTERNAL_NETWORK', value: '' }
+ ];
+ $scope.displayOpenrcFile = null;
+ $scope.podData = null;
+ $scope.displayContainerInfo = null;
+ ngDialog.open({
+ preCloseCallback: function(value) {
+ getEnvironmentList();
+ // getImageList();
+ },
+ template: 'views/modal/environmentDialog.html',
+ scope: $scope,
+ className: 'ngdialog-theme-default',
+ width: 950,
+ showClose: true,
+ closeByDocument: false
+ })
+ }
+
+ function getEnvironmentList() {
+ $loading.start('key');
+
+ mainFactory.getEnvironmentList().get().$promise.then(function(response) {
+ $scope.environmentList = response.result.environments;
+ $loading.finish('key');
+
+ }, function(error) {
+ $loading.finish('key');
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+
+ })
+ }
+
+ //go to detail page
+ function gotoDetail(ifNew, uuid) {
+
+ $state.go('app.environmentDetail', { uuid: uuid, ifNew: ifNew });
+ }
+
+
+ function addEnvironment(name) {
+ mainFactory.addEnvName().post({
+ 'action': 'create_environment',
+ args: {
+ 'name': name
+ }
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'create name success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ $scope.uuidEnv = response.result.uuid;
+ $scope.name = name;
+
+ }
+
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+
+
+ $scope.goBack = function goBack() {
+ $state.go('app2.projectList');
+ }
+ $scope.displayContainerInfo = [];
+
+ function createContainer(selectContainer) {
+
+ $scope.showloading = true;
+ mainFactory.runAcontainer().post({
+ 'action': selectContainer.value,
+ 'args': {
+ 'environment_id': $scope.uuidEnv,
+ }
+ }).$promise.then(function(response) {
+ $scope.showloading = false;
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'create container success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+
+ setTimeout(function() {
+ getItemIdDetail();
+ }, 10000);
+ $scope.ifskipOrClose = 1;
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'Wrong',
+ body: response.result,
+ timeout: 3000
+ });
+ }
+
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ function getConDetail(id) {
+ mainFactory.containerDetail().get({
+ 'containerId': id
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ // $scope.podData = response.result;
+ $scope.displayContainerInfo.push(response.result.container);
+
+ }
+
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+
+ }
+
+ function chooseResult(name) {
+ $scope.selectContainer = name;
+ }
+
+ function getItemIdDetail() {
+ $scope.displayContainerInfo = [];
+ mainFactory.ItemDetail().get({
+ 'envId': $scope.uuidEnv
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.envName = response.result.environment.name;
+ $scope.containerId = response.result.environment.container_id;
+ if ($scope.containerId != null) {
+
+ var keysArray = Object.keys($scope.containerId);
+ for (var k in $scope.containerId) {
+ getConDetail($scope.containerId[k]);
+
+ }
+
+
+ } else {
+ $scope.podData = null;
+ }
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ $scope.uploadImage = function uploadImage() {
+ $scope.imageStatus = 0;
+ $scope.showImageStatus = 1;
+ $scope.showloading = true;
+ mainFactory.uploadImage().post({
+ 'action': 'load_image',
+ 'args': {
+ 'environment_id': $scope.uuid
+
+ }
+ }).$promise.then(function(response) {
+ $scope.showloading = false;
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'create success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ setTimeout(function() {
+ getImageList();
+ }, 10000);
+ $scope.showNextPod = 1;
+
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'failed',
+ body: 'something wrong',
+ timeout: 3000
+ });
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'failed',
+ body: 'something wrong',
+ timeout: 3000
+ });
+ })
+ }
+
+ function getImageList() {
+ if ($scope.intervalImgae != undefined) {
+ $interval.cancel($scope.intervalImgae);
+ }
+ mainFactory.ImageList().get({}).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.imageListData = response.result.images;
+ $scope.imageStatus = response.result.status;
+
+ if ($scope.imageStatus == 0) {
+ $scope.intervalImgae = $interval(function() {
+ getImageList();
+ }, 5000);
+ } else if ($scope.intervalImgae != undefined) {
+ $interval.cancel($scope.intervalImgae);
+ }
+
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'get data failed',
+ body: 'please retry',
+ timeout: 3000
+ });
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'get data failed',
+ body: 'please retry',
+ timeout: 3000
+ });
+ })
+ }
+
+ function getImageListSimple() {
+
+ mainFactory.ImageList().get({}).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.imageListData = response.result.images;
+ $scope.imageStatus = response.result.status;
+
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'get data failed',
+ body: 'please retry',
+ timeout: 3000
+ });
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'get data failed',
+ body: 'please retry',
+ timeout: 3000
+ });
+ })
+ }
+
+ $scope.openDeleteEnv = function openDeleteEnv(id, name) {
+ $scope.deleteName = name;
+ $scope.deleteId = id;
+ ngDialog.open({
+ template: 'views/modal/deleteConfirm.html',
+ scope: $scope,
+ className: 'ngdialog-theme-default',
+ width: 500,
+ showClose: true,
+ closeByDocument: false
+ })
+
+ }
+
+ $scope.deleteEnv = function deleteEnv() {
+ mainFactory.deleteEnv().delete({ 'env_id': $scope.deleteId }).$promise.then(function(response) {
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'delete environment success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ ngDialog.close();
+ getEnvironmentList();
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'Wrong',
+ body: response.result,
+ timeout: 3000
+ });
+ }
+
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+
+
+
+
+
+ function getItemIdDetailforOpenrc() {
+
+ mainFactory.ItemDetail().get({
+ 'envId': $scope.uuidEnv
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.baseElementInfo = response.result.environment;
+
+
+ if ($scope.ifNew != 'true') {
+ $scope.baseElementInfo = response.result.environment;
+ if ($scope.baseElementInfo.openrc_id != null) {
+ getOpenrcDetailForOpenrc($scope.baseElementInfo.openrc_id);
+ }
+ }
+
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: response.error_msg,
+ timeout: 3000
+ });
+
+ }
+ }, function(error) {
+
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+
+ })
+ }
+
+
+
+ //getopenRcid
+ function getOpenrcDetailForOpenrc(openrcId) {
+ mainFactory.getEnvironmentDetail().get({
+ 'openrc_id': openrcId
+ }).$promise.then(function(response) {
+ $scope.openrcInfo = response.result;
+ buildToEnvInfoOpenrc($scope.openrcInfo.openrc)
+ }, function(response) {
+ toaster.pop({
+ type: 'error',
+ title: 'error',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ //buildtoEnvInfo
+ function buildToEnvInfoOpenrc(object) {
+ var tempKeyArray = Object.keys(object);
+ $scope.envInfo = [];
+
+
+ for (var i = 0; i < tempKeyArray.length; i++) {
+ var tempkey = tempKeyArray[i];
+ var tempValue = object[tempKeyArray[i]];
+ var temp = {
+ name: tempkey,
+ value: tempValue
+ };
+ $scope.envInfo.push(temp);
+ }
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ ]);
diff --git a/gui/app/scripts/controllers/pod.controller.js b/gui/app/scripts/controllers/pod.controller.js
new file mode 100644
index 000000000..3ef236854
--- /dev/null
+++ b/gui/app/scripts/controllers/pod.controller.js
@@ -0,0 +1,179 @@
+'use strict';
+
+angular.module('yardStickGui2App')
+ .controller('PodController', ['$scope', '$state', '$stateParams', 'mainFactory', 'Upload', 'toaster', '$location', 'ngDialog',
+ function($scope, $state, $stateParams, mainFactory, Upload, toaster, $location, ngDialog) {
+
+
+ init();
+ $scope.showloading = false;
+ $scope.loadingOPENrc = false;
+
+ function init() {
+
+
+ $scope.uuid = $stateParams.uuid;
+ $scope.uploadFiles = uploadFiles;
+ getItemIdDetail();
+
+ }
+
+ function getItemIdDetail() {
+ mainFactory.ItemDetail().get({
+ 'envId': $scope.uuid
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.name = response.result.environment.name;
+ $scope.podId = response.result.environment.pod_id;
+ if ($scope.podId != null) {
+ getPodDetail($scope.podId);
+ } else {
+ $scope.podData = null;
+ }
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ function getPodDetail(id) {
+ mainFactory.getPodDetail().get({
+ 'podId': id
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.podData = response.result;
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+
+ }
+
+ //upload pod file
+ function uploadFiles($file, $invalidFiles) {
+ $scope.loadingOPENrc = true;
+
+ $scope.displayOpenrcFile = $file;
+ timeConstruct($scope.displayOpenrcFile.lastModified);
+ Upload.upload({
+ url: Base_URL + '/api/v2/yardstick/pods',
+ data: { file: $file, 'environment_id': $scope.uuid, 'action': 'upload_pod_file' }
+ }).then(function(response) {
+
+ $scope.loadingOPENrc = false;
+ if (response.data.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'upload success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+
+ $scope.podData = response.data.result;
+
+ getItemIdDetail();
+
+
+ } else {
+
+ }
+
+ }, function(error) {
+ $scope.uploadfile = null;
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ function timeConstruct(array) {
+ var date = new Date(1398250549490);
+ var Y = date.getFullYear() + '-';
+ var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
+ var D = date.getDate() + ' ';
+ var h = date.getHours() + ':';
+ var m = date.getMinutes() + ':';
+ var s = date.getSeconds();
+ $scope.filelastModified = Y + M + D + h + m + s;
+
+ }
+ $scope.goBack = function goBack() {
+ $state.go('app2.projectList');
+ }
+
+
+ $scope.goNext = function goNext() {
+ $scope.path = $location.path();
+ $scope.uuid = $scope.path.split('/').pop();
+ $state.go('app.container', { uuid: $scope.uuid });
+ }
+
+ $scope.openDeleteEnv = function openDeleteEnv(id, name) {
+ $scope.deleteName = name;
+ $scope.deleteId = id;
+ ngDialog.open({
+ template: 'views/modal/deleteConfirm.html',
+ scope: $scope,
+ className: 'ngdialog-theme-default',
+ width: 500,
+ showClose: true,
+ closeByDocument: false
+ })
+
+ }
+
+ $scope.deletePod = function deletePod() {
+ mainFactory.deletePod().delete({ 'podId': $scope.podId }).$promise.then(function(response) {
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'delete pod success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ ngDialog.close();
+ $scope.uuid = $stateParams.uuid;
+ $scope.uploadFiles = uploadFiles;
+ $scope.displayOpenrcFile = null;
+ getItemIdDetail();
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'Wrong',
+ body: response.result,
+ timeout: 3000
+ });
+ }
+
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+
+ })
+ }
+
+
+
+
+
+ }
+ ]); \ No newline at end of file
diff --git a/gui/app/scripts/controllers/project.controller.js b/gui/app/scripts/controllers/project.controller.js
new file mode 100644
index 000000000..0a7b8b932
--- /dev/null
+++ b/gui/app/scripts/controllers/project.controller.js
@@ -0,0 +1,160 @@
+'use strict';
+
+angular.module('yardStickGui2App')
+ .controller('ProjectController', ['$scope', '$state', '$stateParams', 'mainFactory', 'Upload', 'toaster', 'ngDialog', '$loading',
+ function($scope, $state, $stateParams, mainFactory, Upload, toaster, ngDialog, $loading) {
+
+
+ init();
+
+
+ function init() {
+
+
+ getProjectList();
+ $scope.openCreateProject = openCreateProject;
+ $scope.createName = createName;
+ $scope.gotoDetail = gotoDetail;
+
+
+ }
+
+ function getProjectList() {
+ $loading.start('key');
+ mainFactory.projectList().get({}).$promise.then(function(response) {
+ $loading.finish('key');
+ if (response.status == 1) {
+ $scope.projectListData = response.result.projects;
+
+
+ } else {
+
+ }
+ }, function(error) {
+ $loading.finish('key');
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+
+ })
+ }
+
+ function openCreateProject() {
+
+ ngDialog.open({
+ template: 'views/modal/projectCreate.html',
+ scope: $scope,
+ className: 'ngdialog-theme-default',
+ width: 400,
+ showClose: true,
+ closeByDocument: false
+ })
+ }
+
+ function createName(name) {
+
+ mainFactory.createProjectName().post({
+ 'action': 'create_project',
+ 'args': {
+ 'name': name,
+ }
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'create project success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ ngDialog.close();
+ getProjectList();
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'failed',
+ body: 'create project failed',
+ timeout: 3000
+ });
+ }
+
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'failed',
+ body: 'Something Wrong',
+ timeout: 3000
+ });
+ })
+ }
+
+ function gotoDetail(id) {
+ $state.go('app2.projectdetail', { projectId: id })
+ }
+
+
+ $scope.openDeleteEnv = function openDeleteEnv(id, name) {
+ $scope.deleteName = name;
+ $scope.deleteId = id;
+ ngDialog.open({
+ template: 'views/modal/deleteConfirm.html',
+ scope: $scope,
+ className: 'ngdialog-theme-default',
+ width: 500,
+ showClose: true,
+ closeByDocument: false
+ })
+
+ }
+
+ $scope.deleteProject = function deleteProject() {
+ mainFactory.deleteProject().delete({ 'project_id': $scope.deleteId }).$promise.then(function(response) {
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'delete Project success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ ngDialog.close();
+ getProjectList();
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'Wrong',
+ body: response.result,
+ timeout: 3000
+ });
+ }
+
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+
+ })
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ ]);
diff --git a/gui/app/scripts/controllers/projectDetail.controller.js b/gui/app/scripts/controllers/projectDetail.controller.js
new file mode 100644
index 000000000..4ab4a055a
--- /dev/null
+++ b/gui/app/scripts/controllers/projectDetail.controller.js
@@ -0,0 +1,690 @@
+'use strict';
+
+angular.module('yardStickGui2App')
+ .controller('ProjectDetailController', ['$scope', '$state', '$stateParams', 'mainFactory', 'Upload', 'toaster', 'ngDialog', '$localStorage', '$loading', '$interval',
+ function($scope, $state, $stateParams, mainFactory, Upload, toaster, ngDialog, $localStorage, $loading, $interval) {
+
+
+ init();
+ // $scope.taskListDisplay = [];
+ $scope.blisterPackTemplates = [{ id: 1, name: "Test Case" }, { id: 2, name: "Test Suite" }]
+ $scope.selectType = null;
+ $scope.ifHasEnv = false;
+ $scope.ifHasCase = false;
+ $scope.ifHasSuite = false;
+ $scope.$on('$destroy', function() {
+ $interval.cancel($scope.intervalCount)
+ });
+ $scope.finalTaskListDisplay = [];
+
+
+ function init() {
+
+
+ getProjectDetail();
+
+ $scope.openCreate = openCreate;
+ $scope.createTask = createTask;
+ $scope.constructTestSuit = constructTestSuit;
+ $scope.addEnvToTask = addEnvToTask;
+ $scope.triggerContent = triggerContent;
+ $scope.constructTestCase = constructTestCase;
+ $scope.getTestDeatil = getTestDeatil;
+ $scope.confirmAddCaseOrSuite = confirmAddCaseOrSuite;
+ $scope.runAtask = runAtask;
+ $scope.gotoDetail = gotoDetail;
+ $scope.gotoReport = gotoReport;
+ $scope.gotoModify = gotoModify;
+ $scope.goBack = goBack;
+ $scope.goToExternal = goToExternal;
+
+
+ }
+
+ function getProjectDetail() {
+ if ($scope.intervalCount != undefined) {
+ $interval.cancel($scope.intervalCount);
+ }
+ $loading.start('key');
+ $scope.taskListDisplay = [];
+ $scope.finalTaskListDisplay = [];
+ mainFactory.getProjectDetail().get({
+ project_id: $stateParams.projectId
+ }).$promise.then(function(response) {
+ $loading.finish('key');
+ if (response.status == 1) {
+
+ $scope.projectData = response.result.project;
+ if ($scope.projectData.tasks.length != 0) {
+
+
+ for (var i = 0; i < $scope.projectData.tasks.length; i++) {
+ getDetailTaskForList($scope.projectData.tasks[i]);
+ }
+ $scope.intervalCount = $interval(function() {
+ getDetailForEachTask();
+ }, 10000);
+ } else {
+
+ if ($scope.intervalCount != undefined) {
+ $interval.cancel($scope.intervalCount);
+ }
+ }
+ } else {
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+
+ })
+ }
+
+ // function getProjectDetailSimple() {
+ // getDetailForEachTask();
+ // }
+
+ function openCreate() {
+ $scope.newUUID = null;
+ $scope.displayEnvName = null;
+ $scope.selectEnv = null;
+ $scope.selectCase = null;
+ $scope.selectType = null;
+ $scope.contentInfo = null;
+ $scope.ifHasEnv = false;
+ $scope.ifHasCase = false;
+ $scope.ifHasSuite = false;
+
+ // getEnvironmentList();
+ $scope.selectEnv = null;
+ ngDialog.open({
+ template: 'views/modal/taskCreate.html',
+ scope: $scope,
+ className: 'ngdialog-theme-default',
+ width: 800,
+ showClose: true,
+ closeByDocument: false,
+ preCloseCallback: function(value) {
+ getProjectDetail();
+ },
+ })
+ }
+
+ function createTask(name) {
+ mainFactory.createTask().post({
+ 'action': 'create_task',
+ 'args': {
+ 'name': name,
+ 'project_id': $stateParams.projectId
+ }
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'create task success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ $scope.newUUID = response.result.uuid;
+ getEnvironmentList();
+
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'create task wrong',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ }
+
+
+
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'create task wrong',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ })
+ }
+
+ function getDetailTaskForList(id) {
+
+ mainFactory.getTaskDetail().get({
+ 'taskId': id
+ }).$promise.then(function(response) {
+
+ if (response.status == 1) {
+ if (response.result.task.status == -1) {
+ response.result.task['stausWidth'] = '5%';
+ } else if (response.result.task.status == 0) {
+ response.result.task['stausWidth'] = '50%';
+ } else if (response.result.task.status == 1) {
+ response.result.task['stausWidth'] = '100%';
+ } else if (response.result.task.status == 2) {
+ response.result.task['stausWidth'] = 'red';
+ }
+ $scope.taskListDisplay.push(response.result.task);
+ console.log($scope.taskListDisplay);
+
+ $scope.finalTaskListDisplay = $scope.taskListDisplay;
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+
+ })
+ }
+
+ function getDetailTaskForListSimple(id, index) {
+
+ mainFactory.getTaskDetail().get({
+ 'taskId': id
+ }).$promise.then(function(response) {
+
+ if (response.status == 1) {
+ if (response.result.task.status == -1) {
+
+ $scope.finalTaskListDisplay[index].stausWidth = '5%';
+ $scope.finalTaskListDisplay[index].status = response.result.task.status;
+ } else if (response.result.task.status == 0) {
+
+ $scope.finalTaskListDisplay[index].stausWidth = '50%';
+ $scope.finalTaskListDisplay[index].status = response.result.task.status;
+ } else if (response.result.task.status == 1) {
+
+ $scope.finalTaskListDisplay[index].stausWidth = '100%';
+ $scope.finalTaskListDisplay[index].status = response.result.task.status;
+ } else if (response.result.task.status == 2) {
+
+ $scope.finalTaskListDisplay[index].stausWidth = 'red';
+ $scope.finalTaskListDisplay[index].status = response.result.task.status;
+ }
+
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+
+ })
+ }
+
+ function getDetailForEachTask() {
+ for (var i = 0; i < $scope.finalTaskListDisplay.length; i++) {
+ if ($scope.finalTaskListDisplay[i].status != 1 && $scope.finalTaskListDisplay[i].status != -1) {
+ getDetailTaskForListSimple($scope.finalTaskListDisplay[i].uuid, i);
+ }
+ }
+ }
+
+ function getEnvironmentList() {
+ mainFactory.getEnvironmentList().get().$promise.then(function(response) {
+ $scope.environmentList = response.result.environments;
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+
+ })
+ }
+
+ function constructTestSuit(id, name) {
+ $scope.displayEnvName = name;
+ $scope.selectEnv = id;
+
+ }
+
+ function constructTestCase(name) {
+
+ $scope.selectCase = name;
+ if ($scope.selectType.name == 'Test Case') {
+ getCaseInfo();
+ } else {
+ getSuiteInfo();
+ }
+
+ }
+
+
+
+
+ function addEnvToTask() {
+ mainFactory.taskAddEnv().put({
+ 'taskId': $scope.newUUID,
+ 'action': 'add_environment',
+ 'args': {
+ 'task_id': $scope.newUUID,
+ 'environment_id': $scope.selectEnv
+ }
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'add environment success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ $scope.ifHasEnv = true;
+
+
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'create task wrong',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ }
+
+
+
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'create task wrong',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ })
+ }
+
+ function triggerContent(name) {
+ $scope.selectCase = null;
+ $scope.displayTable = true;
+
+ $scope.selectType = name;
+ if (name.name == 'Test Case') {
+ getTestcaseList();
+ } else if (name.name == 'Test Suite') {
+ getsuiteList();
+ }
+ }
+
+ function getTestcaseList() {
+ mainFactory.getTestcaselist().get({
+
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.testcaselist = response.result;
+
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ function getsuiteList() {
+ mainFactory.suiteList().get({
+
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.testsuitlist = response.result.testsuites;
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ function getTestDeatil() {
+
+
+ if ($scope.selectType.name == 'Test Case') {
+ getTestcaseDetail();
+ } else {
+ getSuiteDetail();
+ }
+
+ }
+
+ function getCaseInfo() {
+
+
+
+ mainFactory.getTestcaseDetail().get({
+ 'testcasename': $scope.selectCase
+
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+
+ $scope.contentInfo = response.result.testcase;
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ function getSuiteInfo() {
+ mainFactory.suiteDetail().get({
+ 'suiteName': $scope.selectCase.split('.')[0]
+
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.contentInfo = response.result.testsuite;
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+
+ function getSuiteDetail() {
+ mainFactory.suiteDetail().get({
+ 'suiteName': $scope.selectCase.split('.')[0]
+
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.displayTable = false;
+ $scope.contentInfo = response.result.testsuite;
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+
+ function getTestcaseDetail() {
+ mainFactory.getTestcaseDetail().get({
+ 'testcasename': $scope.selectCase
+
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+
+ $scope.displayTable = false;
+ $scope.contentInfo = response.result.testcase;
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ function addCasetoTask(content) {
+ mainFactory.taskAddEnv().put({
+ 'taskId': $scope.newUUID,
+ 'action': 'add_case',
+ 'args': {
+ 'task_id': $scope.newUUID,
+ 'case_name': $scope.selectCase,
+ 'case_content': content
+ }
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'add test case success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ $scope.ifHasCase = true;
+
+
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'create task wrong',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'create task wrong',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ })
+ }
+
+ function addSuitetoTask(content) {
+ mainFactory.taskAddEnv().put({
+ 'taskId': $scope.newUUID,
+ 'action': 'add_suite',
+ 'args': {
+ 'task_id': $scope.newUUID,
+ 'suite_name': $scope.selectCase.split('.')[0],
+ 'suite_content': content
+ }
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'add test suite success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ $scope.ifHasSuite = true;
+
+
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'create task wrong',
+ body: 'wrong',
+ timeout: 3000
+ });
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'create task wrong',
+ body: 'something wrong',
+ timeout: 3000
+ });
+ })
+ }
+
+ function confirmAddCaseOrSuite(content) {
+ if ($scope.selectType.name == "Test Case") {
+ addCasetoTask(content);
+ } else {
+ addSuitetoTask(content);
+ }
+ }
+
+ function runAtask(id) {
+ mainFactory.taskAddEnv().put({
+ 'taskId': id,
+ 'action': 'run',
+ 'args': {
+ 'task_id': id
+ }
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'run a task success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ ngDialog.close();
+ // getProjectDetail();
+ } else {
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+
+ })
+ }
+
+ $scope.runAtaskForTable = function runAtaskForTable(id) {
+ mainFactory.taskAddEnv().put({
+ 'taskId': id,
+ 'action': 'run',
+ 'args': {
+ 'task_id': id
+ }
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ // toaster.pop({
+ // type: 'success',
+ // title: 'run a task success',
+ // body: 'you can go next step',
+ // timeout: 3000
+ // });
+ // ngDialog.close();
+ getProjectDetail();
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: response.result,
+ timeout: 3000
+ });
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+
+ })
+ }
+
+ function gotoDetail(id) {
+
+
+ $state.go('app2.tasklist', { taskId: id });
+
+ }
+
+ function gotoReport(id) {
+ $state.go('app2.report', { taskId: id });
+ }
+
+ function gotoModify(id) {
+ $state.go('app2.taskModify', { taskId: id });
+ }
+
+ function goBack() {
+ window.history.back();
+ }
+
+ function goToExternal() {
+ window.open(External_URL, '_blank');
+ }
+
+ $scope.openDeleteEnv = function openDeleteEnv(id, name) {
+ $scope.deleteName = name;
+ $scope.deleteId = id;
+ ngDialog.open({
+ template: 'views/modal/deleteConfirm.html',
+ scope: $scope,
+ className: 'ngdialog-theme-default',
+ width: 500,
+ showClose: true,
+ closeByDocument: false
+ })
+
+ }
+
+ $scope.deleteTask = function deleteTask() {
+ mainFactory.deleteTask().delete({ 'task_id': $scope.deleteId }).$promise.then(function(response) {
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'delete Task success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ ngDialog.close();
+ getProjectDetail();
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'Wrong',
+ body: response.result,
+ timeout: 3000
+ });
+ }
+
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+
+ })
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ ]); \ No newline at end of file
diff --git a/gui/app/scripts/controllers/report.controller.js b/gui/app/scripts/controllers/report.controller.js
new file mode 100644
index 000000000..9b6b5958b
--- /dev/null
+++ b/gui/app/scripts/controllers/report.controller.js
@@ -0,0 +1,115 @@
+'use strict';
+
+angular.module('yardStickGui2App')
+ .controller('ReportController', ['$scope', '$state', '$stateParams', 'mainFactory', 'Upload', 'toaster', 'ngDialog',
+ function($scope, $state, $stateParams, mainFactory, Upload, toaster, ngDialog) {
+
+
+ init();
+
+
+ function init() {
+ getDetailTaskForList();
+
+
+
+ }
+ $scope.goBack = function goBack() {
+ window.history.back();
+ }
+
+ function getDetailTaskForList(id) {
+ mainFactory.getTaskDetail().get({
+ 'taskId': $stateParams.taskId
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ if (response.result.task.status == -1) {
+ response.result.task['stausWidth'] = '5%';
+ } else if (response.result.task.status == 0) {
+ response.result.task['stausWidth'] = '50%';
+ } else if (response.result.task.status == 1) {
+ response.result.task['stausWidth'] = '100%';
+ } else if (response.result.task.status == 2) {
+ response.result.task['stausWidth'] = 'red';
+ }
+ $scope.result = response.result.task;
+ $scope.testcaseinfo = response.result.task.result.testcases;
+ var key = Object.keys($scope.testcaseinfo);
+ $scope.testcaseResult = $scope.testcaseinfo[key];
+
+ $scope.envIdForTask = response.result.task.environment_id;
+ getItemIdDetail();
+
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+
+ })
+ }
+
+ $scope.goToExternal = function goToExternal(id) {
+ var url = External_URL + ':' + $scope.jumpPort + '/dashboard/db' + '/' + id;
+
+ window.open(url, '_blank');
+ }
+
+ function getItemIdDetail() {
+ $scope.displayContainerInfo = [];
+ mainFactory.ItemDetail().get({
+ 'envId': $scope.envIdForTask
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ if (response.result.environment.container_id.grafana != null) {
+ getConDetail(response.result.environment.container_id.grafana);
+
+ } else {
+ $scope.jumpPort = 3000;
+ }
+
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ function getConDetail(id) {
+ mainFactory.containerDetail().get({
+ 'containerId': id
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ // $scope.podData = response.result;
+ $scope.jumpPort = response.result.container.port;
+
+ }
+
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+
+ }
+
+
+
+
+
+
+
+ }
+ ]);
diff --git a/gui/app/scripts/controllers/suitecreate.controller.js b/gui/app/scripts/controllers/suitecreate.controller.js
new file mode 100644
index 000000000..4a7b6fe85
--- /dev/null
+++ b/gui/app/scripts/controllers/suitecreate.controller.js
@@ -0,0 +1,104 @@
+'use strict';
+
+angular.module('yardStickGui2App')
+ .controller('suitcreateController', ['$scope', '$state', '$stateParams', 'mainFactory', 'Upload', 'toaster', 'ngDialog',
+ function($scope, $state, $stateParams, mainFactory, Upload, toaster, ngDialog) {
+
+
+ init();
+
+
+ function init() {
+
+ getTestcaseList();
+ $scope.constructTestSuit = constructTestSuit;
+ $scope.openDialog = openDialog;
+ $scope.createSuite = createSuite;
+
+ }
+
+ function getTestcaseList() {
+ mainFactory.getTestcaselist().get({
+
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.testcaselist = response.result;
+
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ $scope.testsuiteList = [];
+ $scope.suitReconstructList = [];
+
+ function constructTestSuit(name) {
+
+ var index = $scope.testsuiteList.indexOf(name);
+ if (index > -1) {
+ $scope.testsuiteList.splice(index, 1);
+ } else {
+ $scope.testsuiteList.push(name);
+ }
+
+
+ $scope.suitReconstructList = $scope.testsuiteList;
+
+ }
+
+ function createSuite(name) {
+ mainFactory.suiteCreate().post({
+ 'action': 'create_suite',
+ 'args': {
+ 'name': name,
+ 'testcases': $scope.testsuiteList
+ }
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'create suite success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ ngDialog.close();
+ } else {
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ function openDialog() {
+ ngDialog.open({
+ template: 'views/modal/suiteName.html',
+ className: 'ngdialog-theme-default',
+ scope: $scope,
+ width: 314,
+ showClose: true,
+ closeByDocument: false
+ })
+ }
+
+
+
+
+
+
+
+
+ }
+ ]); \ No newline at end of file
diff --git a/gui/app/scripts/controllers/suitedetail.controller.js b/gui/app/scripts/controllers/suitedetail.controller.js
new file mode 100644
index 000000000..0dd39c389
--- /dev/null
+++ b/gui/app/scripts/controllers/suitedetail.controller.js
@@ -0,0 +1,48 @@
+'use strict';
+
+angular.module('yardStickGui2App')
+ .controller('suiteDetailController', ['$scope', '$state', '$stateParams', 'mainFactory', 'Upload', 'toaster',
+ function($scope, $state, $stateParams, mainFactory, Upload, toaster) {
+
+
+ init();
+
+
+ function init() {
+
+ getSuiteDetail();
+
+ }
+
+ function getSuiteDetail() {
+ mainFactory.suiteDetail().get({
+ 'suiteName': $stateParams.name
+
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.suiteinfo = response.result.testsuite;
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+ $scope.goBack = function goBack() {
+ window.history.back();
+ }
+
+
+
+
+
+
+
+
+
+ }
+ ]);
diff --git a/gui/app/scripts/controllers/task.controller.js b/gui/app/scripts/controllers/task.controller.js
new file mode 100644
index 000000000..05546f9bf
--- /dev/null
+++ b/gui/app/scripts/controllers/task.controller.js
@@ -0,0 +1,175 @@
+'use strict';
+
+angular.module('yardStickGui2App')
+ .controller('TaskController', ['$scope', '$state', '$stateParams', 'mainFactory', 'Upload', 'toaster', 'ngDialog',
+ function($scope, $state, $stateParams, mainFactory, Upload, toaster, ngDialog) {
+
+
+ init();
+
+
+ function init() {
+ getDetailTaskForList();
+
+ }
+
+ function getDetailTaskForList() {
+ mainFactory.getTaskDetail().get({
+ 'taskId': $stateParams.taskId
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ if (response.result.task.status == -1) {
+ response.result.task['stausWidth'] = '5%';
+ } else if (response.result.task.status == 0) {
+ response.result.task['stausWidth'] = '50%';
+ } else if (response.result.task.status == 1) {
+ response.result.task['stausWidth'] = '100%';
+ } else if (response.result.task.status == 2) {
+ response.result.task['stausWidth'] = 'red';
+ }
+
+ $scope.taskDetailData = response.result.task;
+ if ($scope.taskDetailData.environment_id != null) {
+ getItemIdDetail($scope.taskDetailData.environment_id);
+ }
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+
+ function getItemIdDetail(id) {
+ mainFactory.ItemDetail().get({
+ 'envId': id
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.displayEnv = response.result.environment;
+
+ if (response.result.environment.pod_id != null) {
+ getPodDetail(response.result.environment.pod_id);
+ } else if (response.result.environment.image_id != null) {
+ getImageDetail(response.result.environment.image_id);
+ } else if (response.result.environment.openrc_id != null) {
+ getOpenrcDetail(response.result.environment.openrc_id != null);
+ } else if (response.result.environment.container_id.length != 0) {
+ $scope.displayContainerDetail = [];
+ var containerArray = response.result.environment.container_id;
+ for (var i = 0; i < containerArray.length; i++) {
+ getContainerId(containerArray[i]);
+ }
+
+ }
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: response.error_msg,
+ timeout: 3000
+ });
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ //getopenRcid
+ function getOpenrcDetail(openrcId) {
+ mainFactory.getEnvironmentDetail().get({
+ 'openrc_id': openrcId
+ }).$promise.then(function(response) {
+ //openrc数据
+ $scope.openrcInfo = response.result;
+ // buildToEnvInfo($scope.openrcInfo.openrc)
+ }, function(response) {
+
+ })
+ }
+
+
+ //getImgDetail
+ function getImageDetail(id) {
+ mainFactory.ImageDetail().get({
+ 'image_id': id
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.imageDetail = response.result.image;
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ //getPodDetail
+ function getPodDetail(id) {
+ mainFactory.podDeatil().get({
+ 'podId': id
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.podDetail = response.result.pod;
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+ //getContainerDetail
+ function getContainerId(containerId) {
+ mainFactory.containerDetail().get({
+ 'containerId': containerId
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.container = response.result.container;
+ $scope.displayContainerDetail.push($scope.container);
+
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: response.error_msg,
+ timeout: 3000
+ });
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+ $scope.goBack = function goBack() {
+ window.history.back();
+ }
+
+
+
+
+
+
+
+
+ }
+ ]); \ No newline at end of file
diff --git a/gui/app/scripts/controllers/taskModify.controller.js b/gui/app/scripts/controllers/taskModify.controller.js
new file mode 100644
index 000000000..757d65866
--- /dev/null
+++ b/gui/app/scripts/controllers/taskModify.controller.js
@@ -0,0 +1,533 @@
+'use strict';
+
+angular.module('yardStickGui2App')
+ .controller('TaskModifyController', ['$scope', '$state', '$stateParams', 'mainFactory', 'Upload', 'toaster',
+ function($scope, $state, $stateParams, mainFactory, Upload, toaster) {
+
+
+ init();
+ $scope.blisterPackTemplates = [{ id: 1, name: "Test Case" }, { id: 2, name: "Test Suite" }]
+ $scope.selectType = null;
+
+ $scope.sourceShow = null;
+
+
+
+ function init() {
+ getDetailTaskForList();
+ getEnvironmentList();
+ $scope.triggerContent = triggerContent;
+ $scope.constructTestSuit = constructTestSuit;
+ $scope.constructTestCase = constructTestCase;
+ $scope.getTestDeatil = getTestDeatil;
+ $scope.confirmToServer = confirmToServer;
+ $scope.addEnvToTask = addEnvToTask;
+ }
+
+ function getDetailTaskForList() {
+ mainFactory.getTaskDetail().get({
+ 'taskId': $stateParams.taskId
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ if (response.result.task.status == -1) {
+ response.result.task['stausWidth'] = '5%';
+ } else if (response.result.task.status == 0) {
+ response.result.task['stausWidth'] = '50%';
+ } else if (response.result.task.status == 1) {
+ response.result.task['stausWidth'] = '100%';
+ } else if (response.result.task.status == 2) {
+ response.result.task['stausWidth'] = 'red';
+ }
+
+ $scope.taskDetailData = response.result.task;
+ $scope.selectEnv = $scope.taskDetailData.environment_id;
+
+ if ($scope.taskDetailData.environment_id != null) {
+ getItemIdDetail($scope.taskDetailData.environment_id);
+ }
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ function getItemIdDetail(id) {
+ mainFactory.ItemDetail().get({
+ 'envId': id
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.envName = response.result.environment.name;
+ // $scope.selectEnv = $scope.envName;
+ } else {
+ alert('Something Wrong!');
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ //getopenRcid
+ function getOpenrcDetail(openrcId) {
+ mainFactory.getEnvironmentDetail().get({
+ 'openrc_id': openrcId
+ }).$promise.then(function(response) {
+ $scope.openrcInfo = response.result;
+ // buildToEnvInfo($scope.openrcInfo.openrc)
+ }, function(response) {
+
+ })
+ }
+
+
+ //getImgDetail
+ function getImageDetail(id) {
+ mainFactory.ImageDetail().get({
+ 'image_id': id
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.imageDetail = response.result.image;
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ //getPodDetail
+ function getPodDetail(id) {
+ mainFactory.podDeatil().get({
+ 'podId': id
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.podDetail = response.result.pod;
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+ //getContainerDetail
+ function getContainerId(containerId) {
+ mainFactory.containerDetail().get({
+ 'containerId': containerId
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.container = response.result.container;
+ $scope.displayContainerDetail.push($scope.container);
+
+ } else {
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ function getEnvironmentList() {
+ mainFactory.getEnvironmentList().get().$promise.then(function(response) {
+ $scope.environmentList = response.result.environments;
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+
+ function triggerContent(name) {
+ $scope.selectCase = null;
+ $scope.displayTable = true;
+
+ $scope.selectType = name;
+ if (name.name == 'Test Case') {
+ $scope.taskDetailData.suite = false;
+ getTestcaseList();
+ } else if (name.name == 'Test Suite') {
+ $scope.taskDetailData.suite = true;
+ getsuiteList();
+ }
+ }
+
+ function getTestcaseList() {
+ mainFactory.getTestcaselist().get({
+
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.testcaselist = response.result;
+
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ function getsuiteList() {
+ mainFactory.suiteList().get({
+
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.testsuitlist = response.result.testsuites;
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+
+ function constructTestSuit(id, name) {
+
+ $scope.envName = name;
+ $scope.selectEnv = id;
+
+ }
+
+ function constructTestCase(name) {
+
+ $scope.selectCase = name;
+ if ($scope.selectType.name == 'Test Case') {
+ getCaseInfo();
+ } else {
+ getSuiteInfo();
+ }
+
+ }
+
+ function getCaseInfo() {
+
+
+
+ mainFactory.getTestcaseDetail().get({
+ 'testcasename': $scope.selectCase
+
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+
+ $scope.contentInfo = response.result.testcase;
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ function getSuiteInfo() {
+ mainFactory.suiteDetail().get({
+ 'suiteName': $scope.selectCase.split('.')[0]
+
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.contentInfo = response.result.testsuite;
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+
+ function getTestDeatil() {
+
+
+ if ($scope.selectType.name == 'Test Case') {
+ getTestcaseDetail();
+ } else {
+ getSuiteDetail();
+ }
+
+ }
+
+ function getSuiteDetail() {
+ mainFactory.suiteDetail().get({
+ 'suiteName': $scope.selectCase.split('.')[0]
+
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.displayTable = false;
+ $scope.contentInfo = response.result.testsuite;
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+
+ function getTestcaseDetail() {
+ mainFactory.getTestcaseDetail().get({
+ 'testcasename': $scope.selectCase
+
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+
+ $scope.displayTable = false;
+ $scope.contentInfo = response.result.testcase;
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+
+
+ function addCasetoTask(content) {
+ mainFactory.taskAddEnv().put({
+ 'taskId': $stateParams.taskId,
+ 'action': 'add_case',
+ 'args': {
+ 'task_id': $stateParams.taskId,
+ 'case_name': $scope.selectCase,
+ 'case_content': content
+ }
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'add test case success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ $scope.ifHasCase = true;
+
+
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'create task wrong',
+ body: '',
+ timeout: 3000
+ });
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'create task wrong',
+ body: '',
+ timeout: 3000
+ });
+ })
+ }
+
+ function addSuitetoTask(content) {
+ mainFactory.taskAddEnv().put({
+ 'taskId': $stateParams.taskId,
+ 'action': 'add_suite',
+ 'args': {
+ 'task_id': $stateParams.taskId,
+ 'suite_name': $scope.selectCase.split('.')[0],
+ 'suite_content': content
+ }
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'add test suite success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ $scope.ifHasSuite = true;
+
+
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'create task wrong',
+ body: 'wrong',
+ timeout: 3000
+ });
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'create task wrong',
+ body: 'something wrong',
+ timeout: 3000
+ });
+ })
+ }
+ $scope.changeStatussourceTrue = function changeStatussourceTrue() {
+ $scope.selectCase = null;
+ $scope.sourceShow = true;
+ }
+
+ $scope.changeStatussourceFalse = function changeStatussourceFalse() {
+ $scope.sourceShow = false;
+ }
+
+ function confirmToServer(content1, content2) {
+
+ var content;
+ if ($scope.sourceShow == false) {
+ content = content2;
+ $scope.selectCase = $scope.taskDetailData.case_name;
+ } else if ($scope.sourceShow == true) {
+ content = content1;
+ }
+ if ($scope.selectCase == 'Test Case' || $scope.taskDetailData.suite == false) {
+
+ addCasetoTask(content);
+ } else {
+ addSuitetoTask(content);
+ }
+ }
+
+
+ function addEnvToTask() {
+
+ mainFactory.taskAddEnv().put({
+ 'taskId': $stateParams.taskId,
+ 'action': 'add_environment',
+ 'args': {
+ 'task_id': $stateParams.taskId,
+ 'environment_id': $scope.selectEnv
+ }
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'add environment success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ $scope.ifHasEnv = true;
+
+
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'create task wrong',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ }
+
+
+
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'create task wrong',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ })
+ }
+
+ $scope.goBack = function goBack() {
+ window.history.back();
+ }
+
+ $scope.runAtask = function runAtask() {
+ mainFactory.taskAddEnv().put({
+ 'taskId': $stateParams.taskId,
+ 'action': 'run',
+ 'args': {
+ 'task_id': $stateParams.taskId
+ }
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'run a task success',
+ body: 'go to task list page...',
+ timeout: 3000
+ });
+ setTimeout(function() {
+ window.history.back();
+ }, 2000);
+
+
+
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: response.error_msg,
+ timeout: 3000
+ });
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ ]);
diff --git a/gui/app/scripts/controllers/testcase.controller.js b/gui/app/scripts/controllers/testcase.controller.js
new file mode 100644
index 000000000..616ceb4a8
--- /dev/null
+++ b/gui/app/scripts/controllers/testcase.controller.js
@@ -0,0 +1,154 @@
+'use strict';
+
+angular.module('yardStickGui2App')
+ .controller('TestcaseController', ['$scope', '$state', '$stateParams', 'mainFactory', 'Upload', 'toaster', 'ngDialog', '$loading',
+ function($scope, $state, $stateParams, mainFactory, Upload, toaster, ngDialog, $loading) {
+
+
+ init();
+ $scope.loadingOPENrc = false;
+
+
+ function init() {
+ $scope.testcaselist = [];
+ getTestcaseList();
+ $scope.gotoDetail = gotoDetail;
+ $scope.uploadFiles = uploadFiles;
+
+
+ }
+
+ function getTestcaseList() {
+ $loading.start('key');
+ mainFactory.getTestcaselist().get({
+
+ }).$promise.then(function(response) {
+ $loading.finish('key');
+ if (response.status == 1) {
+ $scope.testcaselist = response.result;
+
+
+ }
+ }, function(error) {
+ $loading.finish('key');
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ function gotoDetail(name) {
+ $state.go('app2.testcasedetail', { name: name });
+ }
+
+
+ function uploadFiles($file, $invalidFiles) {
+ $scope.loadingOPENrc = true;
+
+ $scope.displayOpenrcFile = $file;
+ timeConstruct($scope.displayOpenrcFile.lastModified);
+ Upload.upload({
+ url: Base_URL + '/api/v2/yardstick/testcases',
+ data: { file: $file, 'action': 'upload_case' }
+ }).then(function(response) {
+
+ $scope.loadingOPENrc = false;
+ if (response.data.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'upload success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+
+
+
+ } else {
+
+ }
+
+ }, function(error) {
+ $scope.uploadfile = null;
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ function timeConstruct(array) {
+ var date = new Date(1398250549490);
+ var Y = date.getFullYear() + '-';
+ var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
+ var D = date.getDate() + ' ';
+ var h = date.getHours() + ':';
+ var m = date.getMinutes() + ':';
+ var s = date.getSeconds();
+ $scope.filelastModified = Y + M + D + h + m + s;
+
+ }
+ $scope.goBack = function goBack() {
+ $state.go('app2.projectList');
+ }
+
+ $scope.openDeleteEnv = function openDeleteEnv(id, name) {
+ $scope.deleteName = name;
+ $scope.deleteId = id;
+ ngDialog.open({
+ template: 'views/modal/deleteConfirm.html',
+ scope: $scope,
+ className: 'ngdialog-theme-default',
+ width: 500,
+ showClose: true,
+ closeByDocument: false
+ })
+
+ }
+
+ $scope.deleteTestCase = function deleteTestCase() {
+ mainFactory.deleteTestCase().delete({ 'caseName': $scope.deleteId }).$promise.then(function(response) {
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'delete Test Case success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ ngDialog.close();
+ getTestcaseList();
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'Wrong',
+ body: response.result,
+ timeout: 3000
+ });
+ }
+
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+
+ })
+ }
+
+
+
+
+
+
+
+
+
+
+ }
+ ]); \ No newline at end of file
diff --git a/gui/app/scripts/controllers/testcasedetail.controller.js b/gui/app/scripts/controllers/testcasedetail.controller.js
new file mode 100644
index 000000000..4e824ca85
--- /dev/null
+++ b/gui/app/scripts/controllers/testcasedetail.controller.js
@@ -0,0 +1,50 @@
+'use strict';
+
+angular.module('yardStickGui2App')
+ .controller('testcaseDetailController', ['$scope', '$state', '$stateParams', 'mainFactory', 'Upload', 'toaster',
+ function($scope, $state, $stateParams, mainFactory, Upload, toaster) {
+
+
+ init();
+
+
+ function init() {
+
+ getTestcaseDetail();
+
+
+ }
+
+ function getTestcaseDetail() {
+ mainFactory.getTestcaseDetail().get({
+ 'testcasename': $stateParams.name
+
+ }).$promise.then(function(response) {
+ if (response.status == 1) {
+ $scope.testcaseInfo = response.result.testcase;
+
+ }
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ $scope.goBack = function goBack() {
+ window.history.back();
+ }
+
+
+
+
+
+
+
+
+
+ }
+ ]); \ No newline at end of file
diff --git a/gui/app/scripts/controllers/testsuit.controller.js b/gui/app/scripts/controllers/testsuit.controller.js
new file mode 100644
index 000000000..abc9095c7
--- /dev/null
+++ b/gui/app/scripts/controllers/testsuit.controller.js
@@ -0,0 +1,119 @@
+'use strict';
+
+angular.module('yardStickGui2App')
+ .controller('SuiteListController', ['$scope', '$state', '$stateParams', 'mainFactory', 'Upload', 'toaster', 'ngDialog', '$loading',
+ function($scope, $state, $stateParams, mainFactory, Upload, toaster, ngDialog, $loading) {
+
+
+ init();
+
+
+ function init() {
+ $scope.testsuitlist = [];
+ getsuiteList();
+ $scope.gotoDetail = gotoDetail;
+ $scope.gotoCreateSuite = gotoCreateSuite;
+
+
+ }
+
+ function getsuiteList() {
+ $loading.start('key');
+ mainFactory.suiteList().get({
+
+ }).$promise.then(function(response) {
+ $loading.finish('key');
+ if (response.status == 1) {
+ $scope.testsuitlist = response.result.testsuites;
+
+ }
+ }, function(error) {
+ $loading.finish('key');
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+ function gotoDetail(name) {
+ var temp = name.split('.')[0];
+
+ $state.go('app2.suitedetail', { name: temp })
+
+ }
+
+ function gotoCreateSuite() {
+ $state.go('app2.suitcreate');
+ }
+
+ $scope.goBack = function goBack() {
+ $state.go('app2.projectList');
+ }
+
+
+ $scope.openDeleteEnv = function openDeleteEnv(id, name) {
+ $scope.deleteName = name;
+ $scope.deleteId = id.split('.')[0];
+ ngDialog.open({
+ template: 'views/modal/deleteConfirm.html',
+ scope: $scope,
+ className: 'ngdialog-theme-default',
+ width: 500,
+ showClose: true,
+ closeByDocument: false
+ })
+
+ }
+
+ $scope.deleteSuite = function deleteSuite() {
+ mainFactory.deleteTestSuite().delete({ 'suite_name': $scope.deleteId }).$promise.then(function(response) {
+ if (response.status == 1) {
+ toaster.pop({
+ type: 'success',
+ title: 'delete Test Suite success',
+ body: 'you can go next step',
+ timeout: 3000
+ });
+ ngDialog.close();
+ getTestcaseList();
+ } else {
+ toaster.pop({
+ type: 'error',
+ title: 'Wrong',
+ body: response.result,
+ timeout: 3000
+ });
+ }
+
+ }, function(error) {
+ toaster.pop({
+ type: 'error',
+ title: 'fail',
+ body: 'unknow error',
+ timeout: 3000
+ });
+ })
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ ]); \ No newline at end of file
diff --git a/gui/app/scripts/factory/main.factory.js b/gui/app/scripts/factory/main.factory.js
new file mode 100644
index 000000000..f8e9df9a1
--- /dev/null
+++ b/gui/app/scripts/factory/main.factory.js
@@ -0,0 +1,247 @@
+'use strict';
+
+/**
+ * get data factory
+ */
+
+
+var Base_URL;
+var Grafana_URL;
+
+angular.module('yardStickGui2App')
+ .factory('mainFactory', ['$resource','$rootScope','$http', '$location',function($resource, $rootScope,$http,$location) {
+
+ Base_URL = 'http://' + $location.host() + ':' + $location.port();
+ Grafana_URL = 'http://' + $location.host();
+
+ return {
+
+ postEnvironmentVariable: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/openrcs', {}, {
+ 'post': {
+ method: 'POST'
+ }
+ })
+ },
+ uploadOpenrc: function() {
+ return $resource(Base_URL + '/ap/v2/yardstick/openrcs', {}, {
+ 'post': {
+ method: 'POST'
+ }
+ })
+ },
+ getEnvironmentList: function() {
+ return $resource(Base_URL+ '/api/v2/yardstick/environments', {}, {
+ 'get': {
+ method: 'GET'
+ }
+ })
+ },
+ getEnvironmentDetail: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/openrcs/:openrc_id', { openrc_id: "@openrc_id" }, {
+ 'get': {
+ method: 'GET'
+ }
+ })
+ },
+ addEnvName: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/environments', {}, {
+ 'post': {
+ method: 'POST'
+ }
+ })
+ },
+ ItemDetail: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/environments/:envId', { envId: "@envId" }, {
+ 'get': {
+ method: 'GET'
+ }
+ })
+ },
+ ImageDetail: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/images/:image_id', { image_id: "@image_id" }, {
+ 'get': {
+ method: 'GET'
+ }
+ })
+ },
+ podDeatil: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/pods/:podId', { podId: "@podId" }, {
+ 'get': {
+ method: 'GET'
+ }
+ })
+ },
+ containerDetail: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/containers/:containerId', { containerId: "@containerId" }, {
+ 'get': {
+ method: 'GET'
+ }
+ })
+ },
+ ImageList: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/images', {}, {
+ 'get': {
+ method: 'GET'
+ }
+ })
+ },
+ uploadImage: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/images', {}, {
+ 'post': {
+ method: 'POST'
+ }
+ })
+ },
+ getPodDetail: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/pods/:podId', { podId: "@podId" }, {
+ 'get': {
+ method: 'GET'
+ }
+ })
+ },
+ runAcontainer: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/containers', { podId: "@podId" }, {
+ 'post': {
+ method: 'POST'
+ }
+ })
+ },
+ getTestcaselist: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/testcases', {}, {
+ 'get': {
+ method: 'GET'
+ }
+ })
+ },
+ getTestcaseDetail: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/testcases/:testcasename', { testcasename: "@testcasename" }, {
+ 'get': {
+ method: 'GET'
+ }
+ })
+ },
+ suiteList: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/testsuites', {}, {
+ 'get': {
+ method: 'GET'
+ }
+ })
+ },
+ suiteDetail: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/testsuites/:suiteName', { suiteName: "@suiteName" }, {
+ 'get': {
+ method: 'GET'
+ }
+ })
+ },
+ suiteCreate: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/testsuites', {}, {
+ 'post': {
+ method: 'POST'
+ }
+ })
+ },
+ projectList: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/projects', {}, {
+ 'get': {
+ method: 'GET'
+ }
+ })
+ },
+ createProjectName: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/projects', {}, {
+ 'post': {
+ method: 'POST'
+ }
+ })
+ },
+ getProjectDetail: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/projects/:project_id', { project_id: "@project_id" }, {
+ 'post': {
+ method: 'POST'
+ }
+ })
+ },
+ createTask: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/tasks', {}, {
+ 'post': {
+ method: 'POST'
+ }
+ })
+ },
+ getTaskDetail: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/tasks/:taskId', { taskId: "@taskId" }, {
+ 'get': {
+ method: 'GET'
+ }
+ })
+ },
+
+ taskAddEnv: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/tasks/:taskId', { taskId: "@taskId" }, {
+ 'put': {
+ method: 'PUT'
+ }
+ })
+ },
+ //delete operate
+ deleteEnv: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/environments/:env_id', { env_id: '@env_id' }, {
+ 'delete': {
+ method: 'DELETE'
+ }
+ })
+ },
+ deleteOpenrc: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/openrcs/:openrc', { openrc: '@openrc' }, {
+ 'delete': {
+ method: 'DELETE'
+ }
+ })
+ },
+ deletePod: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/pods/:podId', { podId: '@podId' }, {
+ 'delete': {
+ method: 'DELETE'
+ }
+ })
+ },
+ deleteContainer: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/containers/:containerId', { containerId: '@containerId' }, {
+ 'delete': {
+ method: 'DELETE'
+ }
+ })
+ },
+ deleteTestCase: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/testcases/:caseName', { caseName: '@caseName' }, {
+ 'delete': {
+ method: 'DELETE'
+ }
+ })
+ },
+ deleteTestSuite: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/testsuites/:suite_name', { suite_name: '@suite_name' }, {
+ 'delete': {
+ method: 'DELETE'
+ }
+ })
+ },
+ deleteProject: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/projects/:project_id', { project_id: '@project_id' }, {
+ 'delete': {
+ method: 'DELETE'
+ }
+ })
+ },
+ deleteTask: function() {
+ return $resource(Base_URL + '/api/v2/yardstick/tasks/:task_id', { task_id: '@task_id' }, {
+ 'delete': {
+ method: 'DELETE'
+ }
+ })
+ }
+
+ };
+ }]);
diff --git a/gui/app/scripts/router.config.js b/gui/app/scripts/router.config.js
new file mode 100644
index 000000000..b42954272
--- /dev/null
+++ b/gui/app/scripts/router.config.js
@@ -0,0 +1,184 @@
+'use strict';
+
+angular.module('yardStickGui2App')
+ .run(
+ ['$rootScope', '$state', '$stateParams',
+ function($rootScope, $state, $stateParams) {
+ $rootScope.$state = $state;
+ $rootScope.$stateParams = $stateParams;
+
+ }
+ ]
+ )
+ .config(['$stateProvider', '$urlRouterProvider', '$locationProvider',
+ function($stateProvider, $urlRouterProvider, $locationProvider) {
+ $urlRouterProvider
+ .otherwise('main/environment');
+
+
+
+
+ $stateProvider
+
+ .state('app2', {
+ url: "/main",
+ controller: 'ContentController',
+ templateUrl: "views/main2.html",
+ ncyBreadcrumb: {
+ label: 'Main'
+ }
+ })
+ .state('app', {
+ url: "/main",
+ controller: 'ContentController',
+ templateUrl: "views/main.html",
+ ncyBreadcrumb: {
+ label: 'Main'
+ }
+ })
+
+ .state('app2.environment', {
+ url: '/environment',
+ templateUrl: 'views/environmentList.html',
+ controller: 'MainCtrl',
+ ncyBreadcrumb: {
+ label: 'Environment'
+ }
+ })
+ .state('app2.testcase', {
+ url: '/testcase',
+ templateUrl: 'views/testcaselist.html',
+ controller: 'TestcaseController',
+ ncyBreadcrumb: {
+ label: 'Test Case'
+ }
+ })
+ .state('app2.testsuite', {
+ url: '/suite',
+ templateUrl: 'views/suite.html',
+ controller: 'SuiteListController',
+ ncyBreadcrumb: {
+ label: 'Test Suite'
+ }
+ })
+ .state('app2.suitcreate', {
+ url: '/suitcreate',
+ templateUrl: 'views/testcasechoose.html',
+ controller: 'suitcreateController',
+ ncyBreadcrumb: {
+ label: 'Suite Create'
+ }
+ })
+ .state('app2.testcasedetail', {
+ url: '/testdetail/:name',
+ templateUrl: 'views/testcasedetail.html',
+ controller: 'testcaseDetailController',
+ ncyBreadcrumb: {
+ label: 'Test Case Detail'
+ },
+ params: { name: null }
+ })
+ .state('app2.suitedetail', {
+ url: '/suitedetail/:name',
+ templateUrl: 'views/suitedetail.html',
+ controller: 'suiteDetailController',
+ ncyBreadcrumb: {
+ label: 'Suite Detail'
+ },
+ params: { name: null }
+ })
+ .state('app.environmentDetail', {
+ url: '/envDetail/:uuid',
+ templateUrl: 'views/environmentDetail.html',
+ controller: 'DetailController',
+ params: { uuid: null, ifNew: null },
+ ncyBreadcrumb: {
+ label: 'Environment Detail'
+ }
+ })
+ .state('app.uploadImage', {
+ url: '/envimageDetail/:uuid',
+ templateUrl: 'views/uploadImage.html',
+ controller: 'ImageController',
+ params: { uuid: null },
+ ncyBreadcrumb: {
+ label: 'Upload Image'
+ }
+
+ })
+ .state('app.podUpload', {
+ url: '/envpodupload/:uuid',
+ templateUrl: 'views/podupload.html',
+ controller: 'PodController',
+ params: { uuid: null },
+ ncyBreadcrumb: {
+ label: 'Pod Upload'
+ }
+ })
+ .state('app.container', {
+ url: '/envcontainer/:uuid',
+ templateUrl: 'views/container.html',
+ controller: 'ContainerController',
+ params: { uuid: null },
+ ncyBreadcrumb: {
+ label: 'Container Manage'
+ }
+ })
+ .state('app2.projectList', {
+ url: '/project',
+ templateUrl: 'views/projectList.html',
+ controller: 'ProjectController',
+ ncyBreadcrumb: {
+ label: 'Project'
+ }
+
+ })
+ .state('app2.tasklist', {
+ url: '/task/:taskId',
+ templateUrl: 'views/taskList.html',
+ controller: 'TaskController',
+ params: { taskId: null },
+ ncyBreadcrumb: {
+ label: 'Task'
+ }
+
+ })
+ .state('app2.report', {
+ url: '/report/:taskId',
+ templateUrl: 'views/report.html',
+ controller: 'ReportController',
+ params: { taskId: null },
+ ncyBreadcrumb: {
+ label: 'Report'
+ }
+
+ })
+ .state('app2.projectdetail', {
+ url: '/projectdetail/:projectId',
+ templateUrl: 'views/projectdetail.html',
+ controller: 'ProjectDetailController',
+ params: { projectId: null },
+ ncyBreadcrumb: {
+ label: 'Project Detail'
+ }
+
+ })
+ .state('app2.taskModify', {
+ url: '/taskModify/:taskId',
+ templateUrl: 'views/taskmodify.html',
+ controller: 'TaskModifyController',
+ params: { taskId: null },
+ ncyBreadcrumb: {
+ label: 'Modify Task'
+ }
+
+
+ })
+
+
+
+
+
+ }
+ ])
+ .run();
diff --git a/gui/app/styles/main.css b/gui/app/styles/main.css
new file mode 100644
index 000000000..e13a66bce
--- /dev/null
+++ b/gui/app/styles/main.css
@@ -0,0 +1,208 @@
+.browsehappy {
+ margin: 0.2em 0;
+ background: #ccc;
+ color: #000;
+ padding: 0.2em 0;
+}
+
+body {
+ padding: 0;
+}
+
+
+/* Everything but the jumbotron gets side spacing for mobile first views */
+
+.header,
+.marketing,
+.footer {
+ /*padding-left: 15px;
+ padding-right: 15px;*/
+}
+
+
+/* Custom page header */
+
+.header {
+ border-bottom: 1px solid #e5e5e5;
+ margin-bottom: 10px;
+}
+
+
+/* Make the masthead heading the same height as the navigation */
+
+.header h3 {
+ margin-top: 0;
+ margin-bottom: 0;
+ line-height: 40px;
+ padding-bottom: 19px;
+}
+
+
+/* Custom page footer */
+
+.footer {
+ padding-top: 19px;
+ color: #777;
+ border-top: 1px solid #e5e5e5;
+}
+
+.container-narrow>hr {
+ margin: 30px 0;
+}
+
+
+/* Main marketing message and sign up button */
+
+.jumbotron {
+ text-align: center;
+ border-bottom: 1px solid #e5e5e5;
+}
+
+.jumbotron .btn {
+ font-size: 21px;
+ padding: 14px 24px;
+}
+
+
+/* Supporting marketing content */
+
+.marketing {
+ margin: 40px 0;
+}
+
+.marketing p+h4 {
+ margin-top: 28px;
+}
+
+
+/* Responsive: Portrait tablets and up */
+
+@media screen and (min-width: 768px) {
+ .container {
+ max-width: 730px;
+ }
+ /* Remove the padding we set earlier */
+ .header,
+ .marketing,
+ .footer {
+ padding-left: 0;
+ padding-right: 0;
+ }
+ /* Space out the masthead */
+ .header {
+ margin-bottom: 30px;
+ }
+ /* Remove the bottom border on the jumbotron for visual effect */
+ .jumbotron {
+ border-bottom: 0;
+ }
+}
+
+.jumbotron.ng-scope {
+ margin-top: 100px;
+}
+
+.content {
+ /*margin-left: 300px;*/
+ /*margin-left: 100px;*/
+ position: relative;
+ margin-left: 25%;
+ width: 70%;
+ border: 1px solid #dfe3e4;
+ border-radius: 5px;
+ padding: 20px 20px 5px 20px;
+ height: 100%;
+ margin-right: 10px;
+ /*border-bottom: none;*/
+ margin-bottom: 10px;
+ padding-bottom: 20px;
+ /* overflow: hidden; */
+}
+
+.ngdialog.ngdialog-theme-default .ngdialog-content {
+ background-color: #fff;
+}
+
+.progree-parent {
+ width: 50%;
+ background-color: #dfe3e4;
+ height: 10px;
+ border-radius: 10px;
+}
+
+.progree-child {
+ width: 50%;
+ background-color: #2ecc71;
+ /* background-color: white; */
+ height: 10px;
+ border-radius: 5px;
+}
+
+textarea {
+ width: 100%;
+ height: 400px;
+ border-radius: 5px;
+ border: 1px solid #e8e8e8;
+}
+
+.naviSide {
+ position: fixed;
+ left: 0px;
+ top: 0px;
+ height: 150%;
+ background-color: #f8f8f8;
+ width: 210px;
+ padding: 30px 0 0 0;
+ border-radius: 10px;
+ border-right: 1px solid #e7e7e7;
+ z-index: 2;
+}
+
+.panel-body {
+ border: none;
+}
+
+.panel-group {
+ width: 210px;
+}
+
+.panel-group {
+ margin-bottom: 0px;
+}
+
+* {
+ border-radius: 0px ! important;
+}
+
+.naviSide.ng-scope {
+ box-shadow: 1px 1px 2px #888888;
+}
+
+.pagination>li>a,
+.pagination>li>span {
+ color: #333;
+}
+
+.pagination>.active>a,
+.pagination>.active>span,
+.pagination>.active>a:hover,
+.pagination>.active>span:hover,
+.pagination>.active>a:focus,
+.pagination>.active>span:focus {
+ background-color: #f9f9f9;
+ color: #333;
+ border-color: #ddd;
+}
+
+.ngdialog.ngdialog-theme-default .ngdialog-close{
+ border: none;
+ background-color: #fff;
+}
+
+button:focus {outline:0;}
+input:focus{outline: 0}
+
+.ngdialog-content {
+ overflow: hidden;
+}
+
diff --git a/gui/app/views/container.html b/gui/app/views/container.html
new file mode 100644
index 000000000..b3d78bfb1
--- /dev/null
+++ b/gui/app/views/container.html
@@ -0,0 +1,134 @@
+<!--container management-->
+
+<div class="content">
+ <div style="display:flex;flex-direction:row;">
+ <div style="width:750px;">
+
+ <h3>{{envName}} -- Container
+ <!--<button class="btn btn-default" style="float:right">Go Next</button>-->
+
+ </h3>
+ <!--<p>In this process, you can input your define openrc config or upload a openrc file</p>-->
+
+ <hr/>
+
+ <select ng-model="selectContainer" data-ng-options="container as container.name for container in containerList">
+ <option value="">Choose...</option>
+ </select>
+
+ <button class="btn btn-default" ng-click="createContainer()" ng-disabled="selectContainer==null">
+ <div ng-show="!showloading">Create</div>
+ <img src="images/loading2.gif" width="25" height="25" ng-if="showloading" />
+ </button>
+
+ <hr/>
+
+ <div>
+ <h4 ng-show="displayContainerInfo.length==0">No Container Data</h4>
+ <div ng-show="displayContainerInfo.length!=0">
+ <h4>Current Container</h4>
+ <table class="table table-striped">
+
+ <tr>
+ <th>name</th>
+ <th>status</th>
+ <th>time</th>
+ <th>delete</th>
+
+ </tr>
+ <tr ng-repeat="con in displayContainerInfo">
+ <td>{{con.name}}</td>
+ <td>{{con.status}}</td>
+ <td>{{con.time}}</td>
+ <td>
+ <button class="btn btn-default btn-sm" ng-click="openDeleteEnv(con.id,'container')">Delete</button>
+ </td>
+
+
+ </tr>
+
+
+
+ </table>
+ </div>
+ </div>
+
+
+
+
+
+
+
+
+
+
+ </div>
+
+
+ </div>
+
+</div>
+<toaster-container></toaster-container>
+
+<style>
+ .form-control {
+ border-radius: 5px;
+ width: 200px;
+ margin-bottom: 10px;
+ }
+
+ .uploadbutton {
+ background-color: #007ACC;
+ color: #fff;
+ border: 0px;
+ border-radius: 5px;
+ height: 27px;
+ }
+
+ .edit-title {
+ border: 0px;
+ background-color: #ffffff;
+ margin-bottom: 5px;
+ font-size: 12px;
+ }
+
+ .null-edit-title {
+ border: 1px solid #e5e6e7;
+ border-radius: 5px;
+ margin-bottom: 3px;
+ }
+
+ .item-info {
+ display: flex;
+ flex-direction: row;
+ }
+
+ .delete-img {
+ width: 15px;
+ height: 15px;
+ opacity: 0.8;
+ margin-left: -10px;
+ margin-top: -3px;
+ cursor: pointer;
+ }
+
+ .nextButton {
+ margin-top: 30px;
+ border: none;
+ border-radius: 5px;
+ padding: 6px;
+ background-color: #339933;
+ color: #ffffff;
+ text-align: center;
+ /* margin-left: 300px; */
+ }
+
+ select {
+ height: 30px;
+ border-radius: 5px;
+ border: 1px solid #e8e8e8;
+ width: 135px;
+ margin-top: 20px;
+ margin-left: 20px;
+ }
+</style>
diff --git a/gui/app/views/content.html b/gui/app/views/content.html
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/gui/app/views/content.html
diff --git a/gui/app/views/environmentDetail.html b/gui/app/views/environmentDetail.html
new file mode 100644
index 000000000..4d5f21c68
--- /dev/null
+++ b/gui/app/views/environmentDetail.html
@@ -0,0 +1,143 @@
+<!--environment detail page-->
+
+<div class="content" style="overflow-x: scroll;">
+ <div style="display:flex;flex-direction:row;">
+ <div>
+
+
+ <h3> {{baseElementInfo.name}} -- Openrc
+ <button class="btn btn-default" style="float:right" ng-click="goNext()">Next</button>
+ <button class="btn btn-default" style="float:right;margin-right:10px;" ng-click="openDeleteEnv(1,'openrc')">Delete</button>
+ </h3>
+ <!--<p>In this process, you can input your define openrc config or upload a openrc file</p>-->
+
+
+
+ <div>
+
+ <button style="display:inline;" class="btn btn-default" ng-click="addEnvironment()" ng-show="uuid==null">Add Name</button>
+ </div>
+
+
+
+ <hr/>
+ <div bs-tabs style="width:600px;">
+ <div data-title="Detail" bs-pane ng-if="openrcInfo.openrc!=null">
+
+ <h4>
+ You have already set up the openrc parameters
+ </h4>
+ <hr />
+ <div ng-repeat="(key,value) in openrcInfo.openrc">
+ <nobr>
+ <font style="font-weight:600;font-size:14px;">{{key}} : </font>
+ <font style="font-size:14px;">{{value}}</font>
+ </nobr>
+ </div>
+
+ </div>
+ <div data-title="Update" bs-pane>
+
+ <div style="margin-top:20px;">
+ <button class="btn btn-default" ng-click="addInfo()" style="margin-bottom:20px;">Add</button>
+ <div style="height:300px;width:800px;display:flex;flex-direction:column;flex-wrap:wrap;margin-left:5px;">
+ <div ng-repeat="info in envInfo">
+ <!--<div> {{info.name}}</div>-->
+
+ <input class="edit-title" ng-model="info.name" ng-class="{'null-edit-title':info.name==null}" ng-attr-type="{{info.name.indexOf('PASSWORD')>-1 ? password : text}}" />
+
+ <div class="item-info">
+ <input class="form-control" type="text" ng-model="info.value" />
+ <!--<button class="delete-button" ng-click="deleteEnvItem($index)">delete</button>-->
+ <img src="images/close.png" ng-click="deleteEnvItem($index)" class="delete-img" />
+ </div>
+
+
+
+ </div>
+ </div>
+ <button class="btn btn-default" ng-click="submitOpenRcFile()" style="margin-bottom:20px;">
+ <div ng-if="!showloading">submit</div>
+ <img src="images/loading2.gif" width="25" height="25" ng-if="showloading" />
+ </button>
+
+ </div>
+
+ </div>
+ <div data-title="Upload File" bs-pane>
+ <div style="margin-top:20px;height:405px;">
+ <button class="btn btn-default" style="margin-bottom:20px;" ngf-select="uploadFiles($file, $invalidFiles)" ngf-max-size="5MB">
+ <div ng-show="!loadingOPENrc">Upload</div>
+ <img src="images/loading2.gif" width="25" height="25" ng-if="loadingOPENrc" />
+ </button>
+
+ <!--<div ng-show="displayOpenrcFile!=null || displayOpenrcFile!=undefined">
+ {{displayOpenrcFile.name}} last modified: {{filelastModified}}
+ </div>-->
+ </div>
+ </div>
+ </div>
+
+
+
+ </div>
+
+
+ </div>
+
+</div>
+<toaster-container></toaster-container>
+
+<style>
+ .form-control {
+ border-radius: 5px;
+ width: 200px;
+ margin-bottom: 10px;
+ }
+
+ .uploadbutton {
+ background-color: #007ACC;
+ color: #fff;
+ border: 0px;
+ border-radius: 5px;
+ height: 27px;
+ }
+
+ .edit-title {
+ border: 0px;
+ background-color: #ffffff;
+ margin-bottom: 5px;
+ font-size: 12px;
+ }
+
+ .null-edit-title {
+ border: 1px solid #e5e6e7;
+ border-radius: 5px;
+ margin-bottom: 3px;
+ }
+
+ .item-info {
+ display: flex;
+ flex-direction: row;
+ }
+
+ .delete-img {
+ width: 15px;
+ height: 15px;
+ opacity: 0.8;
+ margin-left: -10px;
+ margin-top: -3px;
+ cursor: pointer;
+ }
+
+ .nextButton {
+ margin-top: 30px;
+ border: none;
+ border-radius: 5px;
+ padding: 6px;
+ background-color: #339933;
+ color: #ffffff;
+ text-align: center;
+ /* margin-left: 300px; */
+ }
+</style>
diff --git a/gui/app/views/environmentList.html b/gui/app/views/environmentList.html
new file mode 100644
index 000000000..29273a724
--- /dev/null
+++ b/gui/app/views/environmentList.html
@@ -0,0 +1,155 @@
+<div class="content">
+
+ <!--environmentList-->
+ <i class="fa fa-arrow-left fa-1x" aria-hidden="true" style="color: #999;cursor:pointer" ng-click="goBack()">Back</i>
+
+ <div>
+
+ <h3>Environments
+ <button class="btn btn-default btn-sm" style="margin-left:30px;display:inline" ng-click="openEnvironmentDialog()">Add</button>
+ </h3>
+ <hr/>
+
+ <!--<div ng-repeat="env in environmentList">
+ {{env.name}}
+ </div>-->
+ <div dw-loading="key" dw-loading-options="{text:'loading'}">
+ <div style="display:flex;flex-direction:row;justify-content:space-between;padding:8px;border-top: 1px solid #e9ecec;background-color: #f9f9f9;">
+ <div style="font-weight:600">Name</div>
+ <div style="font-weight:600;margin-right:80px;">Action</div>
+
+ </div>
+
+ <div dir-paginate="env in environmentList | orderBy:'-id' | itemsPerPage: 10 ">
+ <div style="display:flex;flex-direction:row;justify-content:space-between;padding:8px;border-top: 1px solid #e9ecec;">
+ <div> <a style="color:#e95420" ng-click="gotoDetail('false',env.uuid)">{{env.name}}</a></div>
+ <div>
+
+ <!-- <button class="btn btn-default btn-sm" ng-click="openDeleteEnv(env.uuid,'environment')">Delete</button> -->
+
+ <div class="btn-group" uib-dropdown is-open="status.isopen" style="margin-right:60px;">
+ <button id="single-button" type="button" class="btn btn-default btn-sm" uib-dropdown-toggle>
+ Modify <span class="caret"></span>
+ </button>
+ <ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="single-button">
+ <li role="menuitem"><a ng-click="openDeleteEnv(env.uuid,'environment')">Delete</a></li>
+ <!-- <li role="menuitem"><a href="#">Another action</a></li>
+ <li role="menuitem"><a href="#">Something else here</a></li>
+ <li class="divider"></li>
+ <li role="menuitem"><a href="#">Separated link</a></li> -->
+ </ul>
+ </div>
+ </div>
+
+
+
+ </div>
+
+ </div>
+ <center>
+ <dir-pagination-controls></dir-pagination-controls>
+ </center>
+ </div>
+
+
+
+
+
+
+
+ </div>
+
+
+
+
+</div>
+
+<toaster-container></toaster-container>
+
+<style>
+ .form-control {
+ border-radius: 5px;
+ width: 300px;
+ margin-bottom: 10px;
+ }
+
+ .uploadbutton {
+ background-color: #007ACC;
+ color: #fff;
+ border: 0px;
+ border-radius: 5px;
+ height: 27px;
+ }
+
+ .edit-title {
+ border: 0px;
+ background-color: #ffffff;
+ margin-bottom: 5px;
+ }
+
+ .null-edit-title {
+ border: 1px solid #e5e6e7;
+ border-radius: 5px;
+ margin-bottom: 3px;
+ }
+
+ .item-info {
+ display: flex;
+ flex-direction: row;
+ }
+
+ .delete-img {
+ width: 19px;
+ height: 19px;
+ opacity: 0.8;
+ margin-left: 5px;
+ margin-top: 4px;
+ cursor: pointer;
+ }
+
+ .deepColor {
+ background-color: #f9f9f9;
+ }
+
+ .nextButton {
+ margin-top: 30px;
+ border: none;
+ border-radius: 5px;
+ padding: 6px;
+ background-color: #339933;
+ color: #ffffff;
+ text-align: center;
+ /* margin-left: 300px; */
+ }
+
+ .bs-sidenav {
+ margin-top: 40px;
+ margin-bottom: 20px;
+ width: 124px;
+ }
+
+ .nav {
+ margin-bottom: 0;
+ padding-left: 0;
+ list-style: none;
+ }
+
+ .nav>li {
+ position: relative;
+ display: block;
+ }
+
+ li {
+ display: list-item;
+ text-align: -webkit-match-parent;
+ }
+
+ a {
+ cursor: pointer;
+ }
+
+ a.active {
+ background-color: #EEEEEE;
+ border-radius: 5px;
+ }
+</style>
diff --git a/gui/app/views/layout/footer.html b/gui/app/views/layout/footer.html
new file mode 100644
index 000000000..cfdf74af3
--- /dev/null
+++ b/gui/app/views/layout/footer.html
@@ -0,0 +1,5 @@
+<div class="footer">
+ <div class="container">
+ <p></p>
+ </div>
+</div> \ No newline at end of file
diff --git a/gui/app/views/layout/header.html b/gui/app/views/layout/header.html
new file mode 100644
index 000000000..033322a62
--- /dev/null
+++ b/gui/app/views/layout/header.html
@@ -0,0 +1,43 @@
+<div class="header">
+ <div class="navbar navbar-default" role="navigation">
+ <div>
+ <div class="navbar-header">
+
+
+
+ <a class="navbar-brand" href="#/">Yardstick</a>
+ </div>
+
+
+ </div>
+ </div>
+</div>
+</div>
+
+<style>
+ .header {
+ position: fixed;
+ top: 0px;
+ width: 100%;
+ /*box-shadow: 3px 2px 5px #888888;*/
+ z-index: 9;
+ }
+
+ .navbar {
+ position: relative;
+ min-height: 50px;
+ margin-bottom: 0px;
+ border: none;
+ /* border: 1px solid transparent; */
+ }
+
+ .navbar {
+ border-radius: 0px;
+ background-color: #e95420;
+ color: #fff;
+ }
+
+ .navbar-default .navbar-brand {
+ color: #fff;
+ }
+</style>
diff --git a/gui/app/views/layout/sideNav.html b/gui/app/views/layout/sideNav.html
new file mode 100644
index 000000000..42dcbbc6e
--- /dev/null
+++ b/gui/app/views/layout/sideNav.html
@@ -0,0 +1,141 @@
+<div class="naviSide">
+
+
+
+
+ <ul class="nav bs-sidenav">
+
+ <div class="panel-group " role="tablist " aria-multiselectable="true " bs-collapse style="margin-bottom:0px; ">
+ <div class="panel panel-default ">
+ <div class="panel-heading " role="tab ">
+ <h4 class="panel-title ">
+ <a bs-collapse-toggle style=" text-decoration: none;" ng-click="gotoProject();">
+ Project
+ </a>
+ </h4>
+
+ </div>
+
+ </div>
+ </div>
+ <div class="panel-group" role="tablist" aria-multiselectable="true" bs-collapse style="margin-bottom:0px;" ng-model="activeStatus">
+ <div class="panel panel-default">
+ <div class="panel-heading" role="tab">
+ <h4 class="panel-title">
+ <a bs-collapse-toggle style=" text-decoration: none;">
+ <div style="display:inline;" ng-click="gotoEnviron()">Environment </div>
+ <i class="fa fa-sort-asc" aria-hidden="true" style="margin-left: 71px;display:inline" ng-show="activeStatus==0"></i>
+ <i class="fa fa-sort-desc" aria-hidden="true" style="margin-left: 71px;display:inline" ng-show="activeStatus==-1"></i>
+ </a>
+ </h4>
+ </div>
+ <div class="panel-collapse" role="tabpanel" bs-collapse-target>
+ <div class="panel-body" style="border-top: 2px solid grey;text-align: right;cursor:pointer" ng-click="gotoOpenrcPage()" ng-class="{active:$state.includes('app.environmentDetail')}">
+ Openrc
+ </div>
+ <div class="panel-body " style="border:none;text-align: right;cursor:pointer" ng-click="gotoUploadPage()" ng-class="{active:$state.includes('app.uploadImage')}">
+ Image
+ </div>
+ <div class="panel-body " style="border:none;text-align: right;cursor:pointer" ng-click="gotoPodPage()" ng-class="{active:$state.includes('app.podUpload')}">
+ Pod File
+ </div>
+ <div class="panel-body " style="border:none;text-align: right;cursor:pointer" ng-click="gotoContainerPage()" ng-class="{active:$state.includes('app.container')}">
+ Container
+ </div>
+ <div class="panel-body " style="border:none;text-align: right;">
+ Others
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="panel-group " role="tablist " aria-multiselectable="true " bs-collapse style="margin-bottom:0px; ">
+ <div class="panel panel-default ">
+ <div class="panel-heading " role="tab ">
+ <h4 class="panel-title ">
+ <a bs-collapse-toggle style=" text-decoration: none;" ng-click="gotoTestcase()">
+ Test Case
+ </a>
+ </h4>
+
+ </div>
+
+ </div>
+ </div>
+
+ <div class="panel-group " role="tablist " aria-multiselectable="true " bs-collapse style="margin-bottom:0px; ">
+ <div class="panel panel-default ">
+ <div class="panel-heading " role="tab ">
+ <h4 class="panel-title ">
+ <a bs-collapse-toggle style=" text-decoration: none;" ng-click="gotoSuite()">
+ Test Suite
+ </a>
+ </h4>
+
+ </div>
+
+ </div>
+ </div>
+
+
+
+ </ul>
+
+
+
+
+
+</div>
+
+
+<style>
+ .bs-sidenav {
+ margin-top: 40px;
+ margin-bottom: 20px;
+ width: 124px;
+ }
+
+ .naviSide {
+ height: 150%;
+ }
+
+ .nav {
+ margin-bottom: 0;
+ padding-left: 0;
+ list-style: none;
+ }
+
+ .nav>li {
+ position: relative;
+ display: block;
+ }
+
+ li {
+ display: list-item;
+ text-align: -webkit-match-parent;
+ }
+
+ a {
+ cursor: pointer;
+ }
+
+ a.active {
+ background-color: #EEEEEE;
+ border-radius: 5px;
+ width: 165px;
+ }
+ /*
+ a:hover {
+ width: 165px;
+ }*/
+
+ .nav>li>a:hover,
+ .nav>li>a:focus {
+ text-decoration: underline;
+ background-color: transparent;
+ }
+
+ .active.panel-body {
+ background-color: #dfe3e4;
+ }
+</style>
diff --git a/gui/app/views/layout/sideNav2.html b/gui/app/views/layout/sideNav2.html
new file mode 100644
index 000000000..104a9c6cf
--- /dev/null
+++ b/gui/app/views/layout/sideNav2.html
@@ -0,0 +1,108 @@
+<div class="naviSide">
+
+
+ <ul class="nav bs-sidenav">
+
+ <div class="panel-group " role="tablist " aria-multiselectable="true " bs-collapse style="margin-bottom:0px; ">
+ <div class="panel panel-default ">
+ <div class="panel-heading " role="tab ">
+ <h4 class="panel-title ">
+ <a bs-collapse-toggle style=" text-decoration: none;" ng-click="gotoProject();">
+ Project
+ </a>
+ </h4>
+
+ </div>
+
+ </div>
+ </div>
+ <div class="panel-group" role="tablist" aria-multiselectable="false" bs-collapse style="margin-bottom:0px;">
+ <div class="panel panel-default">
+ <div class="panel-heading" role="tab">
+ <h4 class="panel-title">
+ <a bs-collapse-toggle style=" text-decoration: none;">
+ <div style="display:inline;" ng-click="gotoEnviron()">Environment </div>
+ <!--<i class="fa fa-sort-asc" aria-hidden="true" style="margin-left: 71px;display:inline"></i>-->
+ </a>
+ </h4>
+ </div>
+
+ </div>
+ </div>
+
+ <div class="panel-group " role="tablist " aria-multiselectable="true " bs-collapse style="margin-bottom:0px; ">
+ <div class="panel panel-default ">
+ <div class="panel-heading " role="tab ">
+ <h4 class="panel-title ">
+ <a bs-collapse-toggle style=" text-decoration: none;" ng-click="gotoTestcase()">
+ Test Case
+ </a>
+ </h4>
+
+ </div>
+
+ </div>
+ </div>
+
+ <div class="panel-group " role="tablist " aria-multiselectable="true " bs-collapse style="margin-bottom:0px; ">
+ <div class="panel panel-default ">
+ <div class="panel-heading " role="tab ">
+ <h4 class="panel-title ">
+ <a bs-collapse-toggle style=" text-decoration: none;" ng-click="gotoSuite()">
+ Test Suite
+ </a>
+ </h4>
+
+ </div>
+
+ </div>
+ </div>
+
+
+
+ </ul>
+
+</div>
+
+<style>
+ .bs-sidenav {
+ margin-top: 40px;
+ margin-bottom: 20px;
+ width: 124px;
+ }
+
+ .nav {
+ margin-bottom: 0;
+ padding-left: 0;
+ list-style: none;
+ }
+
+ .nav>li {
+ position: relative;
+ display: block;
+ }
+
+ li {
+ display: list-item;
+ text-align: -webkit-match-parent;
+ }
+
+ a {
+ cursor: pointer;
+ }
+
+ a.active {
+ background-color: #EEEEEE;
+ border-radius: 5px;
+ width: 165px;
+ }
+ /*a:hover {
+ width: 165px;
+ }*/
+
+ .nav>li>a:hover,
+ .nav>li>a:focus {
+ text-decoration: underline;
+ background-color: transparent;
+ }
+</style>
diff --git a/gui/app/views/main.html b/gui/app/views/main.html
new file mode 100644
index 000000000..d5f7a3af3
--- /dev/null
+++ b/gui/app/views/main.html
@@ -0,0 +1,174 @@
+<div>
+ <div ng-include="'views/layout/header.html'"></div>
+</div>
+<div ng-include="'views/layout/sideNav.html'"></div>
+
+
+<div style="margin-top:80px;margin-left:100px;display:flex;flex-direction:row">
+ <!--<div ncy-breadcrumb></div>-->
+ <div>
+ <ol class="progressDefine">
+ <li data-step="1" ng-click="gotoProject();" style="cursor:pointer" ng-class="{'is-complete':projectShow}">
+ Project
+ </li>
+ <li data-step="2" ng-class="{'is-complete':taskShow}">
+ Task
+ </li>
+
+ <li data-step="3" ng-class="{'progressDefine__last':reportShow}">
+ Reporting
+ </li>
+
+ </ol>
+ </div>
+
+
+</div>
+
+
+
+
+
+
+
+
+
+<div ui-view></div>
+
+
+
+<style>
+ .stepsContent {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-around;
+ margin-left: 120px;
+ margin-top: 100px;
+ }
+
+ .stepItem {
+ display: flex;
+ flex-direction: column;
+ }
+
+ .nextButton {
+ margin-left: 500px;
+ }
+
+ .progressDefine {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ display: table;
+ table-layout: fixed;
+ width: 100%;
+ color: #849397;
+ }
+
+ .progressDefine>li {
+ position: relative;
+ display: table-cell;
+ text-align: center;
+ font-size: 0.8em;
+ }
+
+ .progressDefine>li:before {
+ content: attr(data-step);
+ display: block;
+ margin: 0 auto;
+ background: #DFE3E4;
+ width: 3em;
+ height: 3em;
+ text-align: center;
+ margin-bottom: 0.25em;
+ line-height: 3em;
+ border-radius: 100%;
+ position: relative;
+ z-index: 5;
+ }
+
+ .progressDefine>li:after {
+ content: '';
+ position: absolute;
+ display: block;
+ background: #DFE3E4;
+ width: 100%;
+ height: 0.5em;
+ top: 1.25em;
+ left: 50%;
+ margin-left: 1.5em\9;
+ z-index: -1;
+ }
+
+ .progressDefine>li:last-child:after {
+ display: none;
+ }
+
+ .progressDefine>li.is-complete {
+ color: #e95420;
+ }
+
+ .progressDefine>li.is-complete:before,
+ .progressDefine>li.is-complete:after {
+ color: #FFF;
+ background: #e95420;
+ }
+
+ .progressDefine>li.is-active {
+ color: #3498DB;
+ }
+
+ .progressDefine>li.is-active:before {
+ color: #FFF;
+ background: #3498DB;
+ }
+ /**
+ * Needed for IE8
+ */
+
+ .progressDefine__last:after {
+ display: none !important;
+ }
+ /**
+ * Size Extensions
+ */
+
+ .progressDefine--medium {
+ font-size: 1.5em;
+ }
+
+ .progressDefine--large {
+ font-size: 2em;
+ }
+ /**
+ * Some Generic Stylings
+ */
+
+ *,
+ *:after,
+ *:before {
+ box-sizing: border-box;
+ }
+
+ h1 {
+ margin-bottom: 1.5em;
+ }
+
+ .progressDefine {
+ margin-bottom: 3em;
+ }
+
+ a {
+ color: #3498DB;
+ text-decoration: none;
+ }
+
+ a:hover {
+ text-decoration: underline;
+ }
+ /*
+ body {
+ text-align: center;
+ color: #444;
+ }*/
+</style>
diff --git a/gui/app/views/main2.html b/gui/app/views/main2.html
new file mode 100644
index 000000000..661d604c6
--- /dev/null
+++ b/gui/app/views/main2.html
@@ -0,0 +1,174 @@
+<div>
+ <div ng-include="'views/layout/header.html'"></div>
+</div>
+<div ng-include="'views/layout/sideNav2.html'"></div>
+
+
+<div style="margin-top:80px;margin-left:220px;">
+ <!--<div ncy-breadcrumb></div>-->
+ <div>
+ <ol class="progressDefine">
+ <li data-step="1" ng-click="gotoProject();" style="cursor:pointer" ng-class="{'is-complete':projectShow}">
+ Project
+ </li>
+ <li data-step="2" ng-class="{'is-complete':taskShow}">
+ Task
+ </li>
+
+ <li data-step="3" ng-class="{'is-complete':reportShow}">
+ Reporting
+ </li>
+
+ </ol>
+ </div>
+
+
+</div>
+
+
+
+
+
+
+
+
+
+<div ui-view></div>
+
+
+
+<style>
+ .stepsContent {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-around;
+ margin-left: 120px;
+ margin-top: 100px;
+ }
+
+ .stepItem {
+ display: flex;
+ flex-direction: column;
+ }
+
+ .nextButton {
+ margin-left: 500px;
+ }
+
+ .progressDefine {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ display: table;
+ table-layout: fixed;
+ width: 100%;
+ color: #849397;
+ }
+
+ .progressDefine>li {
+ position: relative;
+ display: table-cell;
+ text-align: center;
+ font-size: 0.8em;
+ }
+
+ .progressDefine>li:before {
+ content: attr(data-step);
+ display: block;
+ margin: 0 auto;
+ background: #DFE3E4;
+ width: 3em;
+ height: 3em;
+ text-align: center;
+ margin-bottom: 0.25em;
+ line-height: 3em;
+ border-radius: 100%;
+ position: relative;
+ z-index: 5;
+ }
+
+ .progressDefine>li:after {
+ content: '';
+ position: absolute;
+ display: block;
+ background: #DFE3E4;
+ width: 100%;
+ height: 0.5em;
+ top: 1.25em;
+ left: 50%;
+ margin-left: 1.5em\9;
+ z-index: -1;
+ }
+
+ .progressDefine>li:last-child:after {
+ display: none;
+ }
+
+ .progressDefine>li.is-complete {
+ color: #e95420;
+ }
+
+ .progressDefine>li.is-complete:before,
+ .progressDefine>li.is-complete:after {
+ color: #FFF;
+ background: #e95420;
+ }
+
+ .progressDefine>li.is-active {
+ color: #3498DB;
+ }
+
+ .progressDefine>li.is-active:before {
+ color: #FFF;
+ background: #3498DB;
+ }
+ /**
+ * Needed for IE8
+ */
+
+ .progressDefine__last:after {
+ display: none !important;
+ }
+ /**
+ * Size Extensions
+ */
+
+ .progressDefine--medium {
+ font-size: 1.5em;
+ }
+
+ .progressDefine--large {
+ font-size: 2em;
+ }
+ /**
+ * Some Generic Stylings
+ */
+
+ *,
+ *:after,
+ *:before {
+ box-sizing: border-box;
+ }
+
+ h1 {
+ margin-bottom: 1.5em;
+ }
+
+ .progressDefine {
+ margin-bottom: 3em;
+ }
+
+ a {
+ color: #3498DB;
+ text-decoration: none;
+ }
+
+ a:hover {
+ text-decoration: underline;
+ }
+ /*
+ body {
+ text-align: center;
+ color: #444;
+ }*/
+</style>
diff --git a/gui/app/views/modal/chooseContainer.html b/gui/app/views/modal/chooseContainer.html
new file mode 100644
index 000000000..4b857b22f
--- /dev/null
+++ b/gui/app/views/modal/chooseContainer.html
@@ -0,0 +1,15 @@
+<h3>Choose Containers</h3>
+<hr/>
+
+
+
+<style>
+ select {
+ height: 30px;
+ border-radius: 5px;
+ border: 1px solid #e8e8e8;
+ width: 135px;
+ margin-top: 20px;
+ margin-left: 20px;
+ }
+</style> \ No newline at end of file
diff --git a/gui/app/views/modal/deleteConfirm.html b/gui/app/views/modal/deleteConfirm.html
new file mode 100644
index 000000000..1659b884b
--- /dev/null
+++ b/gui/app/views/modal/deleteConfirm.html
@@ -0,0 +1,19 @@
+<div>Confirm delete {{deleteName}} ?</div>
+
+<div style="display:flex;flex-direction:row; margin-left: 150px;margin-top: 30px;">
+ <button class="btn btn-default" ng-click="deleteEnv()" ng-show="deleteName=='environment'">Confirm</button>
+ <button class="btn btn-default" ng-click="deleteProject()" ng-show="deleteName=='project'">Confirm</button>
+ <button class="btn btn-default" ng-click="deleteTask()" ng-show="deleteName=='task'">Confirm</button>
+ <button class="btn btn-default" ng-click="deleteTestCase()" ng-show="deleteName=='test case'">Confirm</button>
+ <button class="btn btn-default" ng-click="deleteSuite()" ng-show="deleteName=='test suite'">Confirm</button>
+ <button class="btn btn-default" ng-click="deleteContainer()" ng-show="deleteName=='container'">Confirm</button>
+ <button class="btn btn-default" ng-click="deletePod()" ng-show="deleteName=='pod'">Confirm</button>
+ <button class="btn btn-default" ng-click="deleteOpenRc()" ng-show="deleteName=='openrc'">Confirm</button>
+
+
+
+
+
+ <button class="btn btn-default" style="margin-left:10px;" ng-click="closeThisDialog()">Cancel</button>
+
+</div> \ No newline at end of file
diff --git a/gui/app/views/modal/environmentDialog.html b/gui/app/views/modal/environmentDialog.html
new file mode 100644
index 000000000..a5b88d240
--- /dev/null
+++ b/gui/app/views/modal/environmentDialog.html
@@ -0,0 +1,330 @@
+<!--environment input dialog-->
+
+<div>
+ <div ng-if="uuidEnv==null">
+ <h4>Environment Name</h4>
+ <input type="text" ng-model="name" style="width:300px;" />
+
+ <div style="text-align:center;margin-top:20px;">
+ <button class="btn btn-default" ng-disabled=" name==null || name==''" ng-click="addEnvironment(name)">Create</button>
+ </div>
+ </div>
+
+
+ <div style="display:flex;flex-direction:row;" ng-if="uuidEnv!=null&&showImage==null">
+ <div>
+ <h3> {{name}} -- Openrc
+ <!--<button class="btn btn-default" style="float:right" ng-click="goNext()">Next</button>-->
+ <button class="btn btn-default" ng-click="goToImage()" style="margin-bottom:20px;float:right" ng-disabled="showNextOpenRc==null && showNextOpenRc==null ">
+ Next
+ </button>
+ </h3>
+ <!--<p>In this process, you can input your define openrc config or upload a openrc file</p>-->
+ <div>
+
+ <!--<button style="display:inline;" class="btn btn-default" ng-click="addEnvironment()" ng-show="uuid==null">Add Name</button>-->
+ </div>
+
+ <hr/>
+ <div bs-tabs style="width:750px;">
+ <div data-title="Detail" bs-pane ng-if="openrcInfo.openrc!=null">
+
+ <h4>
+ You have already set up the openrc parameters
+ </h4>
+ <hr />
+ <div ng-repeat="(key,value) in openrcInfo.openrc">
+ <nobr>
+ <font style="font-weight:600;font-size:14px;">{{key}} : </font>
+ <font style="font-size:14px;">{{value}}</font>
+ </nobr>
+ </div>
+
+ </div>
+ <div data-title="Update" bs-pane>
+
+ <div style="margin-top:20px;">
+ <button class="btn btn-default" ng-click="addInfo()" style="margin-bottom:20px;">Add</button>
+ <div style="height:300px;width:800px;display:flex;flex-direction:column;flex-wrap:wrap;margin-left:5px;overflow-x:scroll">
+ <div ng-repeat="info in envInfo">
+ <!--<div> {{info.name}}</div>-->
+
+ <input class="edit-title" ng-model="info.name" ng-class="{'null-edit-title':info.name==null}" ng-attr-type="{{info.name.indexOf('PASSWORD')>-1 ? password : text}}" />
+
+ <div class="item-info">
+ <input class="form-control" type="text" ng-model="info.value" />
+ <!--<button class="delete-button" ng-click="deleteEnvItem($index)">delete</button>-->
+ <img src="images/close.png" ng-click="deleteEnvItem($index)" class="delete-img" />
+ </div>
+
+
+
+ </div>
+ </div>
+ <button class="btn btn-default" ng-click="submitOpenRcFile();" style="margin-bottom:20px;">
+ <div ng-if="!showloading">Submit</div>
+ <img src="images/loading2.gif" width="25" height="25" ng-if="showloading" />
+ </button>
+
+
+ </div>
+
+ </div>
+ <div data-title="Upload File" bs-pane>
+ <div style="margin-top:20px;height:405px;">
+ <button class="btn btn-default" style="margin-bottom:20px;" ngf-select="uploadFiles($file, $invalidFiles);" ngf-max-size="5MB">
+ <div ng-show="!loadingOPENrc">Upload</div>
+ <img src="images/loading2.gif" width="25" height="25" ng-if="loadingOPENrc" />
+ </button>
+ <!--<button class="btn btn-default" style="margin-bottom:20px;" ng-disabled="showNextOpenRc==null" ng-click="goToImage()">
+ Next
+ </button>-->
+
+ <!--<div ng-if="displayOpenrcFile!=null || displayOpenrcFile!=undefined">
+ {{displayOpenrcFile.name}} last modified: {{filelastModified}}
+ </div>-->
+ </div>
+ </div>
+ </div>
+
+
+
+ </div>
+
+
+ </div>
+
+ <div ng-if="showImage==1&&showPod==null">
+ <div style="display:flex;flex-direction:row;">
+ <div style="width:750px;">
+
+ <h3>{{name}} -- Image
+
+ <button class="btn btn-default" ng-click="goToPod()" ng-disabled="showNextPod==null" style="float:right">
+ Next
+ </button>
+ <button class="btn btn-default" ng-click="goToPodPrev()" style="margin-right:5px;float:right">
+ Previous
+ </button>
+
+ </h3>
+ <!--<p>In this process, you can input your define openrc config or upload a openrc file</p>-->
+
+ <hr/>
+
+ <button class="btn btn-default" ng-click="uploadImage()">
+ <div ng-if="!showloading">Load Image</div>
+ <img src="images/loading2.gif" width="25" height="25" ng-if="showloading" />
+
+ </button>
+
+ <i class="fa fa-check" aria-hidden="true" style="margin-top:34px;margin-left:5px;color: #2ecc71;" ng-show="imageStatus==1&&showImageStatus==1">done</i>
+ <i class="fa fa-spinner" aria-hidden="true" style="margin-top:34px;margin-left:5px;color: #2ecc71;" ng-show="imageStatus==0&&showImageStatus==1">loading</i>
+ <i class="fa fa-exclamation-triangle" aria-hidden="true" style="margin-top:34px;margin-left:5px;color: red;" ng-show="imageStatus==2&&showImageStatus==1">error</i>
+
+
+ <!--<button class="btn btn-default" ng-click="goToPod()" ng-disabled="showNextPod==null">
+ Next
+ </button>-->
+ <hr>
+ <h4>Current Images</h4>
+
+ <div>
+ <table class="table table-striped">
+
+ <tr>
+ <th>name</th>
+ <th>size</th>
+ <th>status</th>
+ <th>time</th>
+ </tr>
+ <tr ng-repeat="image in imageListData">
+ <td>{{image.name}}</td>
+ <td>{{image.size/1024}} mb</td>
+ <td>{{image.status}}</td>
+ <td>{{image.time}}</td>
+
+ </tr>
+
+
+
+ </table>
+ </div>
+
+
+ </div>
+
+
+ </div>
+ </div>
+
+ <div ng-if="showPod==1&&showContainer==null">
+ <div style="display:flex;flex-direction:row;">
+ <div style="width:750px;">
+
+
+ <h3>{{name}} -- Pod File
+ <div style="float:right">
+ <button class="btn btn-default" ng-click="skipPodPrev()">Previous</button>
+ <button class="btn btn-default" ng-click="skipPod()" ng-show="podData==null">Skip</button>
+ <button class="btn btn-default" ng-click="skipPod()" ng-show="podData!=null">Next</button>
+
+ </div>
+
+ </h3>
+
+ <hr/>
+
+ <button class="btn btn-default" ngf-select="uploadFilesPod($file, $invalidFiles)" ngf-max-size="5MB">
+ <div ng-show="!loadingOPENrc">Upload</div>
+ <img src="images/loading2.gif" width="25" height="25" ng-if="loadingOPENrc" />
+ </button>
+
+
+ <hr/>
+
+ <div>
+ <h4>Current Pod Configuration</h4>
+ <table class="table table-striped">
+
+ <tr>
+ <th>ip</th>
+ <th>name</th>
+ <th>password</th>
+ <th>role</th>
+ <th>user</th>
+ </tr>
+ <tr ng-repeat="pod in podData.pod.nodes">
+ <td>{{pod.ip}}</td>
+ <td>{{pod.name}}</td>
+ <td>{{pod.password}}</td>
+ <td>{{pod.role}}</td>
+ <td>{{pod.user}}</td>
+
+ </tr>
+ <tr ng-show="podData.length==0">
+ <td>no data</td>
+
+ </tr>
+
+
+
+ </table>
+ </div>
+
+
+
+
+
+
+
+
+
+
+ </div>
+
+
+ </div>
+
+ </div>
+
+ <div ng-if="showContainer!=null">
+ <div style="display:flex;flex-direction:row;">
+ <div style="width:750px;">
+
+ <h3>{{name}} -- Container
+ <div style="float:right">
+ <button class="btn btn-default" ng-click="skipContainerPrev()">Previous</button>
+ <button class="btn btn-default" ng-click="skipContainer()" ng-show="ifskipOrClose!=1">
+ Skip
+ </button>
+ <button class="btn btn-default" ng-click="closeThisDialog(); getEnvironmentList();" ng-show="ifskipOrClose==1">
+ Close
+ </button>
+ </div>
+ <!--<button class="btn btn-default" style="float:right">Go Next</button>-->
+ </h3>
+ <!--<p>In this process, you can input your define openrc config or upload a openrc file</p>-->
+
+ <hr/>
+
+ <select ng-model="selectContainer" data-ng-options="container as container.name for container in containerList">
+ <option value="">Choose...</option>
+ </select>
+
+ <button class="btn btn-default" ng-click="createContainer(selectContainer)" ng-disabled="selectContainer==null">
+ <div ng-show="!showloading">Create</div>
+ <img src="images/loading2.gif" width="25" height="25" ng-if="showloading" />
+ </button>
+ <!--<button class="btn btn-default" ng-click="skipContainer()" ng-show="ifskipOrClose!=1">
+ Skip
+ </button>
+ <button class="btn btn-default" ng-click="closeThisDialog(); getEnvironmentList();" ng-show="ifskipOrClose==1">
+ Close
+ </button>-->
+
+ <hr/>
+
+ <div>
+ <h4>Current Contain</h4>
+ <table class="table table-striped">
+
+ <tr>
+ <th>name</th>
+ <th>status</th>
+ <th>time</th>
+
+ </tr>
+ <tr ng-repeat="con in displayContainerInfo">
+ <td>{{con.name}}</td>
+ <td>{{con.status}}</td>
+ <td>{{con.time}}</td>
+
+
+ </tr>
+
+
+
+ </table>
+ </div>
+
+
+
+
+
+
+
+
+
+
+ </div>
+
+
+ </div>
+
+ </div>
+
+
+
+
+
+
+</div>
+
+
+<style>
+ input {
+ border-radius: 10px;
+ border: 1px solid #eeeeee;
+ width: 100%;
+ }
+
+ select {
+ height: 30px;
+ border-radius: 5px;
+ border: 1px solid #e8e8e8;
+ width: 135px;
+ margin-top: 20px;
+ margin-left: 20px;
+ }
+</style>
diff --git a/gui/app/views/modal/projectCreate.html b/gui/app/views/modal/projectCreate.html
new file mode 100644
index 000000000..74839e798
--- /dev/null
+++ b/gui/app/views/modal/projectCreate.html
@@ -0,0 +1,21 @@
+<div>
+
+ <h4>Enter Project Name</h4>
+ <input type="text" ng-model="name" />
+
+ <div style="text-align:center;margin-top:20px;">
+ <button class="btn btn-default" ng-disabled=" name==null || name==''" ng-click="createName(name)">Create</button>
+ </div>
+
+
+
+</div>
+
+
+<style>
+ input {
+ border-radius: 10px;
+ border: 1px solid #eeeeee;
+ width: 100%;
+ }
+</style>
diff --git a/gui/app/views/modal/suiteName.html b/gui/app/views/modal/suiteName.html
new file mode 100644
index 000000000..981d24210
--- /dev/null
+++ b/gui/app/views/modal/suiteName.html
@@ -0,0 +1,18 @@
+<h4>Enter Suite Name</h4>
+<hr/> You have choose:
+<div ng-repeat="selected in suitReconstructList">{{selected}}</div>
+<hr/>
+<input type="text" ng-model="name" />
+
+<div style="text-align:center;margin-top:20px;">
+ <button class="btn btn-default" ng-disabled="testsuiteList.length==0 || name==null || name==''" ng-click="createSuite(name)">Create</button>
+</div>
+
+
+<style>
+ input {
+ border-radius: 10px;
+ border: 1px solid #eeeeee;
+ width: 100%;
+ }
+</style>
diff --git a/gui/app/views/modal/taskCreate.html b/gui/app/views/modal/taskCreate.html
new file mode 100644
index 000000000..e7812cf2b
--- /dev/null
+++ b/gui/app/views/modal/taskCreate.html
@@ -0,0 +1,134 @@
+
+<h4>Create Task</h4>
+<hr/>
+<div>
+ <div style="display:inline">Name <input type="text" ng-model="name" style="width:200px" /></div>
+ <button style="display:inline" class="btn btn-default" ng-disabled="name==null || name==''" ng-click="createTask(name)" ng-show="newUUID==null">Create</button>
+</div>
+<hr/>
+
+<div bs-tabs ng-show="newUUID!=null">
+ <div data-title="Environment" bs-pane>
+ <div style="margin-top:10px" ng-show="displayEnvName!=null">
+ <div style="display:inline">Choose Environment : {{displayEnvName}}</div>
+ <button class="btn btn-default" style="display:inline;float:right;margin-right:10px;margin-top: -4px;" ng-click="addEnvToTask()">confirm</button>
+ </div>
+ <hr />
+ <div dir-paginate="env in environmentList | orderBy:'-id' | itemsPerPage: 10 ">
+ <div style="display:flex;flex-direction:row;justify-content:space-between;padding:8px;border-top: 1px solid #e9ecec;" ng-class="{deepColor: $index%2==0}">
+ <div> {{env.name}}</div>
+ <!--<button class="btn btn-default btn-sm" ng-click="gotoDetail('false',env.uuid)">detail</button>-->
+ <img src="images/checkyes.png" style="height:18px;cursor:pointer" ng-click="constructTestSuit(env.uuid,env.name)" ng-show="selectEnv==env.uuid" />
+ <img src="images/checkno.png" style="height:18px;cursor:pointer" ng-click="constructTestSuit(env.uuid,env.name)" ng-show="selectEnv!=env.uuid" />
+
+ </div>
+ <!--<hr style="margin-top:5px;margin-bottom:5px;" />-->
+ </div>
+ <center>
+ <dir-pagination-controls></dir-pagination-controls>
+ </center>
+
+ </div>
+ <div data-title="Content" bs-pane>
+ <div style="display:flex;flex-direction:row">
+ <div style="margin-top:20px;">Source of Content</div>
+
+
+ <select ng-model="selectType" ng-change="triggerContent(selectType)" data-ng-options="blisterPackTemplate as blisterPackTemplate.name for blisterPackTemplate in blisterPackTemplates">
+ <option value="">Choose...</option>
+ </select>
+
+ </div>
+ <div style="margin-top:10px" ng-show="selectCase!=null">
+ <div style="display:inline">Choose Source: {{selectCase}}</div>
+ <button class="btn btn-default" style="display:inline;float:right;margin-right:10px;margin-top: -4px;" ng-click="confirmAddCaseOrSuite(contentInfo)">Confirm</button>
+ <button class="btn btn-default" style="display:inline;float:right;margin-right:10px;margin-top: -4px;" ng-click="getTestDeatil()">Edit</button>
+ </div>
+ <hr/>
+
+ <div ng-show="displayTable==true">
+ <div ng-show="testcaselist.testcases.length!=0 && selectType.name=='Test Case'">
+ <div dir-paginate="test in testcaselist.testcases | itemsPerPage: 10" pagination-id="testcase">
+ <div style="display:flex;flex-direction:row;justify-content:space-between;padding:8px;border-top: 1px solid #e9ecec;" ng-class="{deepColor: $index%2==0}">
+ <div> {{test.Name}}</div>
+ <div style="font-size:10px;">{{test.Description}}</div>
+ <img src="images/checkyes.png" style="height:18px;cursor:pointer" ng-click="constructTestCase(test.Name)" ng-show="selectCase==test.Name" />
+ <img src="images/checkno.png" style="height:18px;cursor:pointer" ng-click="constructTestCase(test.Name)" ng-show="selectCase!=test.Name" />
+
+ </div>
+ <!--<hr style="margin-top:5px;margin-bottom:5px;" />-->
+ </div>
+ <center>
+ <dir-pagination-controls pagination-id="testcase"></dir-pagination-controls>
+ </center>
+ </div>
+
+ <div ng-show="testsuitlist.length!=0 && selectType.name=='Test Suite'">
+ <div dir-paginate="suite in testsuitlist | itemsPerPage: 10" pagination-id="testsuite">
+ <div style="display:flex;flex-direction:row;justify-content:space-between;padding:8px;border-top: 1px solid #e9ecec;" ng-class="{deepColor: $index%2==0}">
+ <div> {{suite}}</div>
+
+ <img src="images/checkyes.png" style="height:18px;cursor:pointer" ng-click="constructTestCase(suite)" ng-show="selectCase==suite" />
+ <img src="images/checkno.png" style="height:18px;cursor:pointer" ng-click="constructTestCase(suite)" ng-show="selectCase!=suite" />
+
+ </div>
+ <!--<hr style="margin-top:5px;margin-bottom:5px;" />-->
+ </div>
+ <center>
+ <dir-pagination-controls pagination-id="testsuite"></dir-pagination-controls>
+ </center>
+ </div>
+ </div>
+
+ <div ng-show="displayTable==false">
+ <textarea ng-model="contentInfo" spellcheck="false">
+
+
+ </textarea>
+
+
+ </div>
+
+
+
+
+ </div>
+</div>
+
+<div style="text-align:center;margin-top:20px;">
+ <button class="btn btn-default" ng-click="closeThisDialog()" ng-disabled="newUUID===null || ifHasEnv!=true || (ifHasCase!=true && ifHasSuite!=true)">Close</button>
+ <button class="btn btn-default" ng-disabled="newUUID===null || ifHasEnv!=true || (ifHasCase!=true && ifHasSuite!=true)" ng-click="runAtask(newUUID)">Run</button>
+</div>
+
+
+<style>
+ input {
+ border-radius: 10px;
+ border: 1px solid #eeeeee;
+ width: 100%;
+ }
+
+ .deepColor {
+ background-color: #f9f9f9;
+ }
+
+ select {
+ height: 30px;
+ border-radius: 5px;
+ border: 1px solid #e8e8e8;
+ width: 135px;
+ margin-top: 20px;
+ margin-left: 20px;
+ }
+
+ textarea {
+ width: 100%;
+ height: 400px;
+ border-radius: 5px;
+ border: 1px solid #e8e8e8;
+ }
+
+ .deepColor {
+ background-color: #f9f9f9;
+ }
+</style>
diff --git a/gui/app/views/podupload.html b/gui/app/views/podupload.html
new file mode 100644
index 000000000..99e83aca2
--- /dev/null
+++ b/gui/app/views/podupload.html
@@ -0,0 +1,136 @@
+<!--pod file upload-->
+
+<div class="content">
+ <div style="display:flex;flex-direction:row;">
+ <div style="width:750px;">
+ <!--<i class="fa fa-arrow-left fa-1x" aria-hidden="true" style="color: #999;cursor:pointer" ng-click="goBack()">Back</i>-->
+
+
+ <h3>{{name}} -- Pod File
+ <button class="btn btn-default" style="float:right" ng-click="goNext()">Next</button>
+ </h3>
+ <!--<p>In this process, you can input your define openrc config or upload a openrc file</p>-->
+
+ <hr/>
+
+ <button class="btn btn-default" ngf-select="uploadFiles($file, $invalidFiles)" ngf-max-size="5MB">
+ <div ng-show="!loadingOPENrc">Upload</div>
+ <img src="images/loading2.gif" width="25" height="25" ng-if="loadingOPENrc" />
+ </button>
+ <button class="btn btn-default" ng-click="openDeleteEnv(1,'pod')">Delete</button>
+
+ <!--<div ng-show="displayOpenrcFile!=null || displayOpenrcFile!=undefined ||podData.pod.nodes!=null ">
+ {{displayOpenrcFile.name}} last modified: {{filelastModified}}
+ </div>-->
+
+ <hr/>
+
+ <div>
+ <h4 ng-show="podData.pod.nodes==null">No Pod Configuration</h4>
+ <div ng-show="podData.pod.nodes!=null">
+ <h4>Current Pod Configuration</h4>
+ <table class="table table-striped">
+
+ <tr>
+ <th>ip</th>
+ <th>name</th>
+ <th>password</th>
+ <th>role</th>
+ <th>user</th>
+ </tr>
+ <tr ng-repeat="pod in podData.pod.nodes">
+ <td>{{pod.ip}}</td>
+ <td>{{pod.name}}</td>
+ <td>{{pod.password}}</td>
+ <td>{{pod.role}}</td>
+ <td>{{pod.user}}</td>
+
+ </tr>
+
+
+
+ </table>
+ </div>
+ </div>
+
+
+
+
+
+
+
+
+
+
+ </div>
+ <!--<div style="margin-top:60px;margin-left:67px;">
+ <h3>Openrc parameters</h3>
+ <div>
+ You have already set up the openrc parameters
+ </div>
+ <div ng-repeat="(key,value) in openrcInfo.openrc">
+ <nobr>
+ <font style="font-weight:600;font-size:15px;">{{key}} : </font>
+ <font style="font-size:15px;">{{value}}</font>
+ </nobr>
+ </div>
+ </div>-->
+
+ </div>
+
+</div>
+<toaster-container></toaster-container>
+
+<style>
+ .form-control {
+ border-radius: 5px;
+ width: 200px;
+ margin-bottom: 10px;
+ }
+
+ .uploadbutton {
+ background-color: #007ACC;
+ color: #fff;
+ border: 0px;
+ border-radius: 5px;
+ height: 27px;
+ }
+
+ .edit-title {
+ border: 0px;
+ background-color: #ffffff;
+ margin-bottom: 5px;
+ font-size: 12px;
+ }
+
+ .null-edit-title {
+ border: 1px solid #e5e6e7;
+ border-radius: 5px;
+ margin-bottom: 3px;
+ }
+
+ .item-info {
+ display: flex;
+ flex-direction: row;
+ }
+
+ .delete-img {
+ width: 15px;
+ height: 15px;
+ opacity: 0.8;
+ margin-left: -10px;
+ margin-top: -3px;
+ cursor: pointer;
+ }
+
+ .nextButton {
+ margin-top: 30px;
+ border: none;
+ border-radius: 5px;
+ padding: 6px;
+ background-color: #339933;
+ color: #ffffff;
+ text-align: center;
+ /* margin-left: 300px; */
+ }
+</style>
diff --git a/gui/app/views/projectList.html b/gui/app/views/projectList.html
new file mode 100644
index 000000000..ea6e63d6b
--- /dev/null
+++ b/gui/app/views/projectList.html
@@ -0,0 +1,57 @@
+<div class="content">
+
+ <h3>Projects
+ <button class="btn btn-default btn-sm" style="margin-left:30px;" ng-click="openCreateProject()">Create</button>
+ </h3>
+
+ <hr/>
+
+
+
+ <div dw-loading="key" dw-loading-options="{text:'loading'}">
+ <div style="display:flex;flex-direction:row;justify-content:space-between;padding:8px;border-top: 1px solid #e9ecec;background-color:#f9f9f9;">
+ <div style="font-weight:600">Name</div>
+ <div style="font-weight:600;margin-right:4px;">Action</div>
+
+ </div>
+
+ <div dir-paginate="project in projectListData | orderBy:'-id' | itemsPerPage: 10 ">
+ <div style="display:flex;flex-direction:row;justify-content:space-between;padding:8px;border-top: 1px solid #e9ecec;">
+ <div>
+ <a ng-click="gotoDetail(project.uuid)" style="color:#e95420"> {{project.name}}</a>
+ </div>
+ <div>
+ <!-- <button class="btn btn-default btn-sm" ng-click="gotoDetail(project.uuid)">Detail</button> -->
+ <!--<button class="btn btn-default btn-sm" ng-click="openDeleteEnv(project.uuid,'project')">Delete</button>-->
+ <div class="btn-group" uib-dropdown is-open="status.isopen" style="margin-right:20px;">
+ <button id="single-button" type="button" class="btn btn-default btn-sm" uib-dropdown-toggle>
+ Modify <span class="caret"></span>
+ </button>
+ <ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="single-button">
+ <li role="menuitem" ng-show="task.status!=0"><a ng-click="openDeleteEnv(project.uuid,'project')">delete</a></li>
+
+
+ </ul>
+ </div>
+ </div>
+
+ </div>
+ <!--<hr style="margin-top:5px;margin-bottom:5px;" />-->
+ </div>
+ <center>
+ <dir-pagination-controls></dir-pagination-controls>
+ </center>
+
+ </div>
+
+
+</div>
+
+<toaster-container></toaster-container>
+
+
+<style>
+ .deepColor {
+ background-color: #f9f9f9;
+ }
+</style>
diff --git a/gui/app/views/projectdetail.html b/gui/app/views/projectdetail.html
new file mode 100644
index 000000000..ff61c5fed
--- /dev/null
+++ b/gui/app/views/projectdetail.html
@@ -0,0 +1,97 @@
+<div class="content">
+ <i class="fa fa-arrow-left fa-1x" aria-hidden="true" style="color: #999;cursor:pointer" ng-click="goBack()">Back</i>
+
+ <h3>Project -- Task
+
+ </h3>
+
+ <hr/>
+
+ <div>
+
+ <h4>{{projectData.name}}</h4>
+ <h5>{{projectData.time}}</h5>
+ <hr/>
+ <h4>Tasks
+ <button class="btn btn-default btn-sm" style="margin-left:30px;" ng-click="openCreate()">Create</button>
+ </h4>
+ <div ng-show="projectData.tasks.length==0">No task in this project</div>
+ <table class="table " width="100%" dw-loading="key" dw-loading-options="{text:'loading'}">
+ <tr style="background-color:#f9f9f9">
+ <td style="font-weight:700">Name</td>
+ <td style="font-weight:700"> Status</td>
+ <td style="font-weight:700">Action</td>
+ </tr>
+ <tr dir-paginate="task in finalTaskListDisplay | orderBy:'-id' | itemsPerPage: 6 " pagination-id="table">
+
+ <td width="10%"> <a ng-click="gotoDetail(task.uuid)" style="color:#e95420"> {{task.name}} </a></td>
+ <td width="40%">
+ <div class="progree-parent" ng-show="task.status!=2">
+ <div class="progree-child" ng-style="{'width':task.stausWidth}">
+ </div>
+ </div>
+ <div class="progree-parent" ng-show="task.status==2" style="background-color:red">
+ <div class="progree-child" style="width:0">
+ </div>
+ </div>
+ </td>
+
+
+ <td width="50%">
+
+ <div class="btn-group" uib-dropdown is-open="status.isopen" style="margin-right:20px;">
+ <button id="single-button" type="button" class="btn btn-default btn-sm" uib-dropdown-toggle>
+ Modify <span class="caret"></span>
+ </button>
+ <ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="single-button">
+ <li role="menuitem" ng-show="task.status!=0"><a ng-click="runAtaskForTable(task.uuid)">run</a></li>
+
+ <li role="menuitem" ng-show="task.status!=0"><a ng-click="gotoModify(task.uuid)">modify</a></li>
+ <li role="menuitem" ng-show="task.status!=-1 && task.status!=0"><a ng-click="gotoReport(task.uuid)" style="color:#2ecc71">reporting</a></li>
+ <li role="menuitem"><a ng-click="openDeleteEnv(task.uuid,'task')">delete</a></li>
+
+
+ </ul>
+ </div>
+ <!-- <button class="btn btn-default btn-sm" ng-click="runAtask(task.uuid)" ng-disabled="task.status!=-1">run</button>
+ <button class="btn btn-default btn-sm" ng-click="gotoDetail(task.uuid)">detail</button>
+ <button class="btn btn-default btn-sm" ng-click="gotoModify(task.uuid)" ng-disabled="task.status==0">modify</button>
+ <button class="btn btn-default btn-sm" ng-click="gotoReport(task.uuid)" style="color:#2ecc71" ng-disabled="task.status==-1 || task.status==0">reporting</button>
+ <button class="btn btn-default btn-sm" ng-click="openDeleteEnv(task.uuid,'task')">delete</button> -->
+
+ </td>
+
+ </tr>
+ </table>
+
+
+
+ </div>
+ <center>
+ <dir-pagination-controls pagination-id="table"></dir-pagination-controls>
+ </center>
+
+</div>
+
+
+
+</div>
+
+<toaster-container></toaster-container>
+
+<style>
+ .progree-parent {
+ width: 50%;
+ background-color: #dfe3e4;
+ height: 10px;
+ border-radius: 10px;
+ }
+
+ .progree-child {
+ width: 50%;
+ background-color: #2ecc71;
+ /* background-color: white; */
+ height: 10px;
+ border-radius: 5px;
+ }
+</style>
diff --git a/gui/app/views/report.html b/gui/app/views/report.html
new file mode 100644
index 000000000..78ac6a0c9
--- /dev/null
+++ b/gui/app/views/report.html
@@ -0,0 +1,56 @@
+<div class="content">
+ <i class="fa fa-arrow-left fa-1x" aria-hidden="true" style="color: #999;cursor:pointer" ng-click="goBack()">Back</i>
+ <h3>Yardstick Report </h3>
+ <hr/>
+ <div>
+
+ <div>Task ID : {{result.result.task_id}} </div>
+ <div style="margin-top:5px;">Criteria :
+ <font style="color:#2ECC71" ng-show="result.result.criteria=='PASS'"> {{result.result.criteria}}</font>
+ <font style="color:red" ng-show="result.result.criteria=='FAIL'"> {{result.result.criteria}}</font>
+ </div>
+ <hr/>
+ <caption>Information</caption>
+ <table class="table table-striped">
+ <tr>
+ <th>#</th>
+ <th>key</th>
+ <th>value</th>
+ </tr>
+ <tbody>
+ <tr ng-repeat="(key,value) in result.result.info">
+ <td>{{$index}}</td>
+ <td>{{key}}</td>
+ <td>{{value}}</td>
+ </tr>
+
+ </tbody>
+ </table>
+ <hr/>
+
+ <caption>Test Cases</caption>
+ <table class="table table-striped">
+ <tr>
+ <th>#</th>
+ <th>key</th>
+
+ <th>value</th>
+ <th>grafana</th>
+ </tr>
+ <tbody>
+ <tr ng-repeat="(key,value) in result.result.testcases">
+ <td>{{$index}}</td>
+ <td>{{key}}</td>
+
+ <td>{{value.criteria}}</td>
+ <td> <button class="btn btn-default btn-sm" ng-click="goToExternal(key)"> grafana</button></td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+</div>
+
+
+
+</div>
diff --git a/gui/app/views/suite.html b/gui/app/views/suite.html
new file mode 100644
index 000000000..8e1348333
--- /dev/null
+++ b/gui/app/views/suite.html
@@ -0,0 +1,149 @@
+<div class="content">
+ <!--suitelist-->
+ <i class="fa fa-arrow-left fa-1x" aria-hidden="true" style="color: #999;cursor:pointer" ng-click="goBack()">Back</i>
+
+ <div>
+ Test Suites
+ <button class="btn btn-default" style="margin-left:20px;" ng-click="gotoCreateSuite()">
+ Create
+
+ </button>
+
+ <!--<div ng-show="displayOpenrcFile!=null || displayOpenrcFile!=undefined">
+ {{displayOpenrcFile.name}} last modified: {{filelastModified}}
+ </div>-->
+ <hr/>
+
+ <!--<div ng-repeat="env in environmentList">
+ {{env.name}}
+ </div>-->
+ <div dw-loading="key" dw-loading-options="{text:'loading'}">
+ <div style="display:flex;flex-direction:row;justify-content:space-between;padding:8px;border-top: 1px solid #e9ecec;background-color: #f9f9f9;" >
+ <div style="font-weight:600">Name</div>
+ <div style="font-weight:600;margin-right:4px;">Operate</div>
+
+ </div>
+
+
+ <div dir-paginate="suite in testsuitlist | itemsPerPage: 10">
+ <div style="display:flex;flex-direction:row;justify-content:space-between;padding:8px;border-top: 1px solid #e9ecec;">
+ <div>
+ <a style="color:#e95420" ng-click="gotoDetail(suite)"> {{suite}}
+ </a>
+ </div>
+ <div>
+ <!-- <button class="btn btn-default btn-sm" ng-click="gotoDetail(suite)">Detail</button> -->
+ <button class="btn btn-default btn-sm" ng-click="openDeleteEnv(suite,'test suite')">Delete</button>
+ </div>
+
+ </div>
+
+ </div>
+ <center>
+ <dir-pagination-controls></dir-pagination-controls>
+ </center>
+ </div>
+
+
+
+
+
+
+
+
+ </div>
+
+
+
+
+</div>
+
+<toaster-container></toaster-container>
+
+<style>
+ .deepColor {
+ background-color: #f9f9f9;
+ }
+
+ .form-control {
+ border-radius: 5px;
+ width: 300px;
+ margin-bottom: 10px;
+ }
+
+ .uploadbutton {
+ background-color: #007ACC;
+ color: #fff;
+ border: 0px;
+ border-radius: 5px;
+ height: 27px;
+ }
+
+ .edit-title {
+ border: 0px;
+ background-color: #ffffff;
+ margin-bottom: 5px;
+ }
+
+ .null-edit-title {
+ border: 1px solid #e5e6e7;
+ border-radius: 5px;
+ margin-bottom: 3px;
+ }
+
+ .item-info {
+ display: flex;
+ flex-direction: row;
+ }
+
+ .delete-img {
+ width: 19px;
+ height: 19px;
+ opacity: 0.8;
+ margin-left: 5px;
+ margin-top: 4px;
+ cursor: pointer;
+ }
+
+ .nextButton {
+ margin-top: 30px;
+ border: none;
+ border-radius: 5px;
+ padding: 6px;
+ background-color: #339933;
+ color: #ffffff;
+ text-align: center;
+ /* margin-left: 300px; */
+ }
+
+ .bs-sidenav {
+ margin-top: 40px;
+ margin-bottom: 20px;
+ width: 124px;
+ }
+
+ .nav {
+ margin-bottom: 0;
+ padding-left: 0;
+ list-style: none;
+ }
+
+ .nav>li {
+ position: relative;
+ display: block;
+ }
+
+ li {
+ display: list-item;
+ text-align: -webkit-match-parent;
+ }
+
+ a {
+ cursor: pointer;
+ }
+
+ a.active {
+ background-color: #EEEEEE;
+ border-radius: 5px;
+ }
+</style>
diff --git a/gui/app/views/suitedetail.html b/gui/app/views/suitedetail.html
new file mode 100644
index 000000000..6122f6560
--- /dev/null
+++ b/gui/app/views/suitedetail.html
@@ -0,0 +1,110 @@
+<div class="content">
+ <!--testcaselist-->
+ <div>
+ <i class="fa fa-arrow-left fa-1x" aria-hidden="true" style="color: #999;cursor:pointer" ng-click="goBack()">Back</i>
+
+ <h3>Detail</h3>
+ <hr/>
+
+ <textarea ng-model="suiteinfo" spellcheck="false">
+
+ </textarea>
+
+
+
+
+
+
+
+ </div>
+
+
+
+
+</div>
+
+<toaster-container></toaster-container>
+
+<style>
+ .form-control {
+ border-radius: 5px;
+ width: 300px;
+ margin-bottom: 10px;
+ }
+
+ .uploadbutton {
+ background-color: #007ACC;
+ color: #fff;
+ border: 0px;
+ border-radius: 5px;
+ height: 27px;
+ }
+
+ .edit-title {
+ border: 0px;
+ background-color: #ffffff;
+ margin-bottom: 5px;
+ }
+
+ .null-edit-title {
+ border: 1px solid #e5e6e7;
+ border-radius: 5px;
+ margin-bottom: 3px;
+ }
+
+ .item-info {
+ display: flex;
+ flex-direction: row;
+ }
+
+ .delete-img {
+ width: 19px;
+ height: 19px;
+ opacity: 0.8;
+ margin-left: 5px;
+ margin-top: 4px;
+ cursor: pointer;
+ }
+
+ .nextButton {
+ margin-top: 30px;
+ border: none;
+ border-radius: 5px;
+ padding: 6px;
+ background-color: #339933;
+ color: #ffffff;
+ text-align: center;
+ /* margin-left: 300px; */
+ }
+
+ .bs-sidenav {
+ margin-top: 40px;
+ margin-bottom: 20px;
+ width: 124px;
+ }
+
+ .nav {
+ margin-bottom: 0;
+ padding-left: 0;
+ list-style: none;
+ }
+
+ .nav>li {
+ position: relative;
+ display: block;
+ }
+
+ li {
+ display: list-item;
+ text-align: -webkit-match-parent;
+ }
+
+ a {
+ cursor: pointer;
+ }
+
+ a.active {
+ background-color: #EEEEEE;
+ border-radius: 5px;
+ }
+</style>
diff --git a/gui/app/views/taskList.html b/gui/app/views/taskList.html
new file mode 100644
index 000000000..159fed5c9
--- /dev/null
+++ b/gui/app/views/taskList.html
@@ -0,0 +1,62 @@
+<div class="content">
+ <i class="fa fa-arrow-left fa-1x" aria-hidden="true" style="color: #999;cursor:pointer" ng-click="goBack()">Back</i>
+ <h3>Detail</h3>
+ <hr/>
+ <div style="display:flex;flex-direction:row">
+ <div>
+ <h4>{{taskDetailData.name}}</h4>
+ <div style="margin-top:5px;">{{taskDetailData.time}}</div>
+ </div>
+ <div class="progree-parent" ng-show="taskDetailData.status!=2" style="margin-top:34px;margin-left:30px;">
+ <div class="progree-child" ng-style="{'width':taskDetailData.stausWidth}">
+ </div>
+
+ </div>
+ <div class="progree-parent" ng-show="taskDetailData.status==2" style="background-color:red;margin-top:34px;margin-left:30px;">
+ <div class="progree-child" style="width:0">
+ </div>
+ </div>
+ <i class="fa fa-check" aria-hidden="true" style="margin-top:34px;margin-left:5px;color: #2ecc71;" ng-show="taskDetailData.status==1">finish</i>
+ <i class="fa fa-spinner" aria-hidden="true" style="margin-top:34px;margin-left:5px;color: #2ecc71;" ng-show="taskDetailData.status==0">runing</i>
+ <i class="fa fa-exclamation-triangle" aria-hidden="true" style="margin-top:34px;margin-left:5px;color: red;" ng-show="taskDetailData.status==2">failed</i>
+ </div>
+
+ <div style="margin-top:5px;">Environment : {{displayEnv.name}} </div>
+ <div ng-show="taskDetailData.case_name!=false" style="margin-top:5px;margin-bottom:5px;"> Name : {{taskDetailData.case_name}}</div>
+ <textarea ng-model="taskDetailData.content" spellcheck="false">
+
+ </textarea>
+
+ <div style="text-align:center;margin-top:20px;">
+ <button class="btn btn-default" ng-click="createTask(name)" ng-show="">Run</button>
+ </div>
+</div>
+
+
+<style>
+ input {
+ border-radius: 10px;
+ border: 1px solid #eeeeee;
+ width: 100%;
+ }
+
+ select {
+ height: 30px;
+ border-radius: 5px;
+ border: 1px solid #e8e8e8;
+ width: 135px;
+ margin-top: 20px;
+ margin-left: 20px;
+ }
+
+ textarea {
+ width: 100%;
+ height: 350px;
+ border-radius: 5px;
+ border: 1px solid #e8e8e8;
+ }
+
+ .content {
+ height: 90%;
+ }
+</style>
diff --git a/gui/app/views/taskmodify.html b/gui/app/views/taskmodify.html
new file mode 100644
index 000000000..a4593f745
--- /dev/null
+++ b/gui/app/views/taskmodify.html
@@ -0,0 +1,162 @@
+<div class="content">
+ <i class="fa fa-arrow-left fa-1x" aria-hidden="true" style="color: #999;cursor:pointer" ng-click="goBack()">Back</i>
+ <h4>Modify </h4>
+
+ <hr/>
+
+ <div>
+ <div style="display:inline">Name <input type="text" ng-model="taskDetailData.name" style="width:200px" /></div>
+
+ <button class="btn btn-default" ng-click="runAtask()" style="float:right;margin-right:10px;">Run</button>
+ </div>
+ <hr/>
+
+ <div bs-tabs>
+ <div data-title="Environment" bs-pane>
+ <div style="margin-top:10px">
+ <div style="display:inline">Choose Environment : {{envName}}</div>
+ <button class="btn btn-default" style="display:inline;float:right;margin-right:10px;margin-top: -4px;" ng-click="addEnvToTask()">Confirm</button>
+ </div>
+ <hr />
+ <div dir-paginate="env in environmentList | orderBy:'-id' | itemsPerPage: 10 ">
+ <div style="display:flex;flex-direction:row;justify-content:space-between;padding:8px;border-top: 1px solid #e9ecec;" ng-class="{deepColor: $index%2==0}">
+ <div> {{env.name}}</div>
+ <!--<button class="btn btn-default btn-sm" ng-click="gotoDetail('false',env.uuid)">detail</button>-->
+ <img src="images/checkyes.png" style="height:18px;cursor:pointer" ng-click="constructTestSuit(env.uuid,env.name)" ng-show="selectEnv==env.uuid" />
+ <img src="images/checkno.png" style="height:18px;cursor:pointer" ng-click="constructTestSuit(env.uuid,env.name)" ng-show="selectEnv!=env.uuid" />
+
+ </div>
+ <!--<hr style="margin-top:5px;margin-bottom:5px;" />-->
+ </div>
+ <center>
+ <dir-pagination-controls></dir-pagination-controls>
+ </center>
+
+ </div>
+ <div data-title="Content" bs-pane>
+ <div style="margin-top:10px;">
+ <button class="btn btn-default" ng-click="changeStatussourceFalse()">Modify Content</button>
+ <button class="btn btn-default" ng-click="changeStatussourceTrue()">Modify Source</button>
+ <div class="label-type" ng-show="taskDetailData.suite==false"> Test Case</div>
+ <div class="label-type" ng-show="taskDetailData.suite==true"> Test Suite</div>
+ <button class="btn btn-default" style="float:right" ng-disabled="sourceShow==null" ng-click="confirmToServer(contentInfo,taskDetailData.content)">Confirm</button>
+ </div>
+
+
+ <textarea ng-model="taskDetailData.content" ng-show="sourceShow==false" style="margin-top:5px;" spellcheck="false">
+
+
+ </textarea>
+
+ <div ng-show="sourceShow==true">
+ <div style="display:flex;flex-direction:row">
+ <div style="margin-top:20px;">Source of Content</div>
+
+
+ <select ng-model="selectType" ng-change="triggerContent(selectType)" data-ng-options="blisterPackTemplate as blisterPackTemplate.name for blisterPackTemplate in blisterPackTemplates">
+ <option value="">Choose...</option>
+ </select>
+
+ </div>
+
+ <div style="margin-top:10px" ng-show="selectCase!=null ">
+ <div style="display:inline">Choose Source : {{selectCase}}</div>
+ <!--<button class="btn btn-default" style="display:inline;float:right;margin-right:10px;margin-top: -4px;" ng-click="confirmAddCaseOrSuite(contentInfo)">Confirm</button>-->
+ <button class="btn btn-default" style="display:inline;float:right;margin-right:10px;margin-top: -4px;" ng-click="getTestDeatil()">Edit</button>
+ </div>
+ <hr/>
+
+ <div ng-show="displayTable==true">
+ <div ng-show="testcaselist.testcases.length!=0 && selectType.name=='Test Case'">
+ <div dir-paginate="test in testcaselist.testcases | itemsPerPage: 10" pagination-id="testcase">
+ <div style="display:flex;flex-direction:row;justify-content:space-between;padding:8px;border-top: 1px solid #e9ecec;" ng-class="{deepColor: $index%2==0}">
+ <div> {{test.Name}}</div>
+ <div style="font-size:10px;">{{test.Description}}</div>
+ <img src="images/checkyes.png" style="height:18px;cursor:pointer" ng-click="constructTestCase(test.Name)" ng-show="selectCase==test.Name" />
+ <img src="images/checkno.png" style="height:18px;cursor:pointer" ng-click="constructTestCase(test.Name)" ng-show="selectCase!=test.Name" />
+
+ </div>
+ <!--<hr style="margin-top:5px;margin-bottom:5px;" />-->
+ </div>
+ <center>
+ <dir-pagination-controls pagination-id="testcase"></dir-pagination-controls>
+ </center>
+ </div>
+
+ <div ng-show="testsuitlist.length!=0 && selectType.name=='Test Suite'">
+ <div dir-paginate="suite in testsuitlist | itemsPerPage: 10" pagination-id="testsuite">
+ <div style="display:flex;flex-direction:row;justify-content:space-between;padding:8px;border-top: 1px solid #e9ecec;" ng-class="{deepColor: $index%2==0}">
+ <div> {{suite}}</div>
+
+ <img src="images/checkyes.png" style="height:18px;cursor:pointer" ng-click="constructTestCase(suite)" ng-show="selectCase==suite" />
+ <img src="images/checkno.png" style="height:18px;cursor:pointer" ng-click="constructTestCase(suite)" ng-show="selectCase!=suite" />
+
+ </div>
+ <!--<hr style="margin-top:5px;margin-bottom:5px;" />-->
+ </div>
+ <center>
+ <dir-pagination-controls pagination-id="testsuite"></dir-pagination-controls>
+ </center>
+ </div>
+ </div>
+
+ <div ng-show="displayTable==false">
+ <textarea ng-model="contentInfo" spellcheck="false">
+ </textarea>
+
+
+ </div>
+ </div>
+
+
+
+
+ </div>
+ </div>
+
+
+</div>
+<toaster-container></toaster-container>
+
+
+<style>
+ input {
+ border-radius: 10px;
+ border: 1px solid #eeeeee;
+ width: 100%;
+ padding: 5px;
+ }
+
+ .deepColor {
+ background-color: #f9f9f9;
+ }
+
+ select {
+ height: 30px;
+ border-radius: 5px;
+ border: 1px solid #e8e8e8;
+ width: 135px;
+ margin-top: 20px;
+ margin-left: 20px;
+ }
+
+ textarea {
+ width: 100%;
+ height: 350px;
+ border-radius: 5px;
+ border: 1px solid #e8e8e8;
+ }
+
+ .label-type {
+ display: inline;
+ background-color: #2ecc71;
+ color: #fff;
+ border-radius: 5px;
+ padding: 3px;
+ font-size: 10px;
+ }
+
+ .content {
+ height: auto;
+ }
+</style>
diff --git a/gui/app/views/testcasechoose.html b/gui/app/views/testcasechoose.html
new file mode 100644
index 000000000..12bdb834f
--- /dev/null
+++ b/gui/app/views/testcasechoose.html
@@ -0,0 +1,48 @@
+<div class="content">
+
+ <div>
+ Test case list
+ <button class="btn btn-default" style="margin-left:20px;" ng-click="openDialog()">
+ <div ng-show="!loadingOPENrc">Create </div>
+ <img src="images/loading2.gif" width="25" height="25" ng-if="loadingOPENrc" />
+ </button>
+
+
+ <hr/> You have choose :
+ <div ng-repeat="selected in suitReconstructList" style="display:inline;" class="item">{{selected}}</div>
+ <hr/>
+
+ <!--<div ng-repeat="env in environmentList">
+ {{env.name}}
+ </div>-->
+ <div dir-paginate="test in testcaselist.testcases | itemsPerPage: 10">
+ <div style="display:flex;flex-direction:row;">
+ <img src="images/checkyes.png" style="height:12px;cursor:pointer" ng-click="constructTestSuit(test.Name)" ng-show="testsuiteList.indexOf(test.Name)>-1" />
+ <img src="images/checkno.png" style="height:12px;cursor:pointer" ng-click="constructTestSuit(test.Name)" ng-show="testsuiteList.indexOf(test.Name)==-1" />
+ <div style="margin-left:50px;"> {{test.Name}}</div>
+ <div style="font-size:10px;margin-left:100px">{{test.Description}}</div>
+
+ </div>
+ <hr style="margin-top:5px;margin-bottom:5px;" />
+ </div>
+ <center>
+ <dir-pagination-controls></dir-pagination-controls>
+ </center>
+
+
+
+ </div>
+ <toaster-container></toaster-container>
+
+ <style>
+ .item {
+ background-color: #3498db;
+ color: #fff;
+ width: 150px;
+ border-radius: 5px;
+ padding-left: 10px;
+ margin-left: 2px;
+ margin-top: 3px;
+ padding: 4px;
+ }
+ </style>
diff --git a/gui/app/views/testcasedetail.html b/gui/app/views/testcasedetail.html
new file mode 100644
index 000000000..43a51537f
--- /dev/null
+++ b/gui/app/views/testcasedetail.html
@@ -0,0 +1,110 @@
+<div class="content">
+ <!--testcaselist-->
+ <div>
+ <i class="fa fa-arrow-left fa-1x" aria-hidden="true" style="color: #999;cursor:pointer" ng-click="goBack()">Back</i>
+
+ <h4>Detail</h4>
+ <hr/>
+
+ <textarea ng-model="testcaseInfo" spellcheck="false">
+
+ </textarea>
+
+
+
+
+
+
+
+ </div>
+
+
+
+
+</div>
+
+<toaster-container></toaster-container>
+
+<style>
+ .form-control {
+ border-radius: 5px;
+ width: 300px;
+ margin-bottom: 10px;
+ }
+
+ .uploadbutton {
+ background-color: #007ACC;
+ color: #fff;
+ border: 0px;
+ border-radius: 5px;
+ height: 27px;
+ }
+
+ .edit-title {
+ border: 0px;
+ background-color: #ffffff;
+ margin-bottom: 5px;
+ }
+
+ .null-edit-title {
+ border: 1px solid #e5e6e7;
+ border-radius: 5px;
+ margin-bottom: 3px;
+ }
+
+ .item-info {
+ display: flex;
+ flex-direction: row;
+ }
+
+ .delete-img {
+ width: 19px;
+ height: 19px;
+ opacity: 0.8;
+ margin-left: 5px;
+ margin-top: 4px;
+ cursor: pointer;
+ }
+
+ .nextButton {
+ margin-top: 30px;
+ border: none;
+ border-radius: 5px;
+ padding: 6px;
+ background-color: #339933;
+ color: #ffffff;
+ text-align: center;
+ /* margin-left: 300px; */
+ }
+
+ .bs-sidenav {
+ margin-top: 40px;
+ margin-bottom: 20px;
+ width: 124px;
+ }
+
+ .nav {
+ margin-bottom: 0;
+ padding-left: 0;
+ list-style: none;
+ }
+
+ .nav>li {
+ position: relative;
+ display: block;
+ }
+
+ li {
+ display: list-item;
+ text-align: -webkit-match-parent;
+ }
+
+ a {
+ cursor: pointer;
+ }
+
+ a.active {
+ background-color: #EEEEEE;
+ border-radius: 5px;
+ }
+</style>
diff --git a/gui/app/views/testcaselist.html b/gui/app/views/testcaselist.html
new file mode 100644
index 000000000..3e8cfccf9
--- /dev/null
+++ b/gui/app/views/testcaselist.html
@@ -0,0 +1,150 @@
+<div class="content">
+ <!--testcaselist-->
+ <i class="fa fa-arrow-left fa-1x" aria-hidden="true" style="color: #999;cursor:pointer" ng-click="goBack()">Back</i>
+
+ <div>
+ Test Cases
+ <button class="btn btn-default" style="margin-left:20px;" ngf-select="uploadFiles($file, $invalidFiles)" ngf-max-size="5MB">
+ <div ng-show="!loadingOPENrc">Upload</div>
+ <img src="images/loading2.gif" width="25" height="25" ng-if="loadingOPENrc" />
+ </button>
+
+ <!--<div ng-show="displayOpenrcFile!=null || displayOpenrcFile!=undefined">
+ {{displayOpenrcFile.name}} last modified: {{filelastModified}}
+ </div>-->
+ <hr/>
+
+ <!--<div ng-repeat="env in environmentList">
+ {{env.name}}
+ </div>-->
+ <div dw-loading="key" dw-loading-options="{text:'loading'}">
+ <div style="display:flex;flex-direction:row;justify-content:space-between;padding:8px;border-top: 1px solid #e9ecec;background-color: #f9f9f9">
+ <div style="font-weight:600">Name</div>
+ <div style="font-weight:600;margin-right:4px;">Operate</div>
+
+ </div>
+
+ <div dir-paginate="test in testcaselist.testcases | itemsPerPage: 10">
+ <div style="display:flex;flex-direction:row;justify-content:space-between;padding:8px;border-top: 1px solid #e9ecec;">
+ <div>
+
+ <a style="color:#e95420" ng-click="gotoDetail(test.Name)">
+ {{test.Name}}
+ </a>
+ </div>
+ <div style="font-size:10px;">{{test.Description}}</div>
+ <div>
+ <!-- <button class="btn btn-default btn-sm" ng-click="gotoDetail(test.Name)">Detail</button> -->
+ <button class="btn btn-default btn-sm" ng-click="openDeleteEnv(test.Name,'test case')">Delete</button>
+ </div>
+
+ </div>
+
+ </div>
+ <center>
+ <dir-pagination-controls></dir-pagination-controls>
+ </center>
+ </div>
+
+
+
+
+
+
+
+ </div>
+
+
+
+
+</div>
+
+<toaster-container></toaster-container>
+
+<style>
+ .deepColor {
+ background-color: #f9f9f9;
+ }
+
+ .form-control {
+ border-radius: 5px;
+ width: 300px;
+ margin-bottom: 10px;
+ }
+
+ .uploadbutton {
+ background-color: #007ACC;
+ color: #fff;
+ border: 0px;
+ border-radius: 5px;
+ height: 27px;
+ }
+
+ .edit-title {
+ border: 0px;
+ background-color: #ffffff;
+ margin-bottom: 5px;
+ }
+
+ .null-edit-title {
+ border: 1px solid #e5e6e7;
+ border-radius: 5px;
+ margin-bottom: 3px;
+ }
+
+ .item-info {
+ display: flex;
+ flex-direction: row;
+ }
+
+ .delete-img {
+ width: 19px;
+ height: 19px;
+ opacity: 0.8;
+ margin-left: 5px;
+ margin-top: 4px;
+ cursor: pointer;
+ }
+
+ .nextButton {
+ margin-top: 30px;
+ border: none;
+ border-radius: 5px;
+ padding: 6px;
+ background-color: #339933;
+ color: #ffffff;
+ text-align: center;
+ /* margin-left: 300px; */
+ }
+
+ .bs-sidenav {
+ margin-top: 40px;
+ margin-bottom: 20px;
+ width: 124px;
+ }
+
+ .nav {
+ margin-bottom: 0;
+ padding-left: 0;
+ list-style: none;
+ }
+
+ .nav>li {
+ position: relative;
+ display: block;
+ }
+
+ li {
+ display: list-item;
+ text-align: -webkit-match-parent;
+ }
+
+ a {
+ cursor: pointer;
+ }
+
+ a.active {
+ background-color: #EEEEEE;
+ border-radius: 5px;
+ }
+</style>
diff --git a/gui/app/views/uploadImage.html b/gui/app/views/uploadImage.html
new file mode 100644
index 000000000..17ccfdb8b
--- /dev/null
+++ b/gui/app/views/uploadImage.html
@@ -0,0 +1,145 @@
+<!--upload image page-->
+
+<div class="content">
+ <div style="display:flex;flex-direction:row;">
+ <div style="width:750px;">
+
+ <h3>{{baseElementInfo.name}} -- Image
+ <button class="btn btn-default" style="float:right" ng-click="goNext()">Next</button>
+ </h3>
+ <!--<p>In this process, you can input your define openrc config or upload a openrc file</p>-->
+
+ <hr/>
+ <button class="btn btn-default" ng-click="uploadImage()">
+ <div ng-if="!showloading">Load Image</div>
+ <img src="images/loading2.gif" width="25" height="25" ng-if="showloading" />
+ </button>
+ <i class="fa fa-check" aria-hidden="true" style="margin-top:34px;margin-left:5px;color: #2ecc71;" ng-show="imageStatus==1&&ifshowStatus==1">done</i>
+ <i class="fa fa-spinner" aria-hidden="true" style="margin-top:34px;margin-left:5px;color: #2ecc71;" ng-show="imageStatus==0&&ifshowStatus==1">loading</i>
+ <i class="fa fa-exclamation-triangle" aria-hidden="true" style="margin-top:34px;margin-left:5px;color: red;" ng-show="imageStatus==2&&ifshowStatus==1">error</i>
+
+ <hr>
+ <h4>Current Images</h4>
+
+ <div>
+ <table class="table table-striped">
+
+ <tr>
+ <th>name</th>
+ <th>size</th>
+ <th>status</th>
+ <th>time</th>
+ </tr>
+ <tr ng-repeat="image in imageListData">
+ <td>{{image.name}}</td>
+ <td>{{image.size/1024}} MB</td>
+ <td>{{image.status}}</td>
+ <td>{{image.time}}</td>
+
+ </tr>
+
+
+
+ </table>
+ </div>
+
+
+
+
+
+
+
+
+
+ </div>
+
+
+ </div>
+
+</div>
+<toaster-container></toaster-container>
+
+<style>
+ .form-control {
+ border-radius: 5px;
+ width: 200px;
+ margin-bottom: 10px;
+ }
+
+ .uploadbutton {
+ background-color: #007ACC;
+ color: #fff;
+ border: 0px;
+ border-radius: 5px;
+ height: 27px;
+ }
+
+ .edit-title {
+ border: 0px;
+ background-color: #ffffff;
+ margin-bottom: 5px;
+ font-size: 12px;
+ }
+
+ .null-edit-title {
+ border: 1px solid #e5e6e7;
+ border-radius: 5px;
+ margin-bottom: 3px;
+ }
+
+ .item-info {
+ display: flex;
+ flex-direction: row;
+ }
+
+ .delete-img {
+ width: 15px;
+ height: 15px;
+ opacity: 0.8;
+ margin-left: -10px;
+ margin-top: -3px;
+ cursor: pointer;
+ }
+
+ .nextButton {
+ margin-top: 30px;
+ border: none;
+ border-radius: 5px;
+ padding: 6px;
+ background-color: #339933;
+ color: #ffffff;
+ text-align: center;
+ /* margin-left: 300px; */
+ }
+
+ .bs-sidenav {
+ margin-top: 40px;
+ margin-bottom: 20px;
+ width: 124px;
+ }
+
+ .nav {
+ margin-bottom: 0;
+ padding-left: 0;
+ list-style: none;
+ }
+
+ .nav>li {
+ position: relative;
+ display: block;
+ }
+
+ li {
+ display: list-item;
+ text-align: -webkit-match-parent;
+ }
+
+ a {
+ cursor: pointer;
+ }
+
+ a.active {
+ background-color: #EEEEEE;
+ border-radius: 5px;
+ }
+</style>
diff --git a/gui/bower.json b/gui/bower.json
new file mode 100644
index 000000000..6da3bee3c
--- /dev/null
+++ b/gui/bower.json
@@ -0,0 +1,45 @@
+{
+ "name": "yard-stick-gui2",
+ "version": "0.0.0",
+ "dependencies": {
+ "angular": "^1.4.0",
+ "bootstrap": "^3.2.0",
+ "angular-strap": "^2.3.12",
+ "angular-ui-router": "^1.0.3",
+ "angular-animate": "^1.6.4",
+ "angular-breadcrumb": "^0.5.0",
+ "angular-wizard": "^0.10.0",
+ "angular-resource": "^1.6.4",
+ "ng-file-upload": "^12.2.13",
+ "AngularJS-Toaster": "angularjs-toaster#^2.1.0",
+ "ng-dialog": "^1.3.0",
+ "angularUtils-pagination": "angular-utils-pagination#^0.11.1",
+ "components-font-awesome": "^4.7.0",
+ "ngstorage": "^0.3.11",
+ "v-accordion": "^1.6.0",
+ "angular-loading": "^0.1.4",
+ "angular-bootstrap": "^2.5.0",
+ "angular-sanitize": "^1.6.5"
+ },
+ "devDependencies": {
+ "angular-mocks": "^1.4.0"
+ },
+ "appPath": "app",
+ "moduleName": "yardStickGui2App",
+ "overrides": {
+ "bootstrap": {
+ "main": [
+ "less/bootstrap.less",
+ "dist/css/bootstrap.css",
+ "dist/js/bootstrap.js"
+ ]
+ },
+ "angular-loading": {
+ "main": [
+ "angular-loading.css",
+ "angular-loading.js",
+ "../spin.js/spin.js"
+ ]
+ }
+ }
+}
diff --git a/gui/gui.sh b/gui/gui.sh
new file mode 100755
index 000000000..12a14923e
--- /dev/null
+++ b/gui/gui.sh
@@ -0,0 +1,8 @@
+apt-get install -y nodejs
+apt-get install -y npm
+ln -s /usr/bin/nodejs /usr/bin/node
+npm install
+npm install -g grunt
+npm install -g bower
+bower install --force --allow-root
+grunt build
diff --git a/gui/package.json b/gui/package.json
new file mode 100644
index 000000000..b85c75469
--- /dev/null
+++ b/gui/package.json
@@ -0,0 +1,43 @@
+{
+ "name": "yardstickgui2",
+ "private": true,
+ "devDependencies": {
+ "autoprefixer-core": "^5.2.1",
+ "grunt": "^0.4.5",
+ "grunt-angular-templates": "^0.5.7",
+ "grunt-concurrent": "^1.0.0",
+ "grunt-contrib-clean": "^0.6.0",
+ "grunt-contrib-concat": "^0.5.0",
+ "grunt-contrib-connect": "^0.9.0",
+ "grunt-contrib-copy": "^0.7.0",
+ "grunt-contrib-cssmin": "^0.12.0",
+ "grunt-contrib-htmlmin": "^0.4.0",
+ "grunt-contrib-imagemin": "^1.0.0",
+ "grunt-contrib-jshint": "^0.11.0",
+ "grunt-contrib-uglify": "^0.7.0",
+ "grunt-contrib-watch": "^0.6.1",
+ "grunt-filerev": "^2.1.2",
+ "grunt-google-cdn": "^0.4.3",
+ "grunt-jscs": "^1.8.0",
+ "grunt-newer": "^1.1.0",
+ "grunt-ng-annotate": "^0.9.2",
+ "grunt-postcss": "^0.5.5",
+ "grunt-svgmin": "^2.0.0",
+ "grunt-usemin": "^3.0.0",
+ "grunt-wiredep": "^2.0.0",
+ "jasmine-core": "^2.6.2",
+ "jit-grunt": "^0.9.1",
+ "jshint-stylish": "^1.0.0",
+ "karma": "^1.7.0",
+ "karma-jasmine": "^1.1.0",
+ "karma-phantomjs-launcher": "^1.0.4",
+ "phantomjs-prebuilt": "^2.1.14",
+ "time-grunt": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {
+ "test": "karma start test\\karma.conf.js"
+ }
+}
diff --git a/gui/test/.jshintrc b/gui/test/.jshintrc
new file mode 100644
index 000000000..b2ce4eff4
--- /dev/null
+++ b/gui/test/.jshintrc
@@ -0,0 +1,18 @@
+{
+ "bitwise": true,
+ "browser": true,
+ "curly": true,
+ "eqeqeq": true,
+ "esnext": true,
+ "jasmine": true,
+ "latedef": true,
+ "noarg": true,
+ "node": true,
+ "strict": true,
+ "undef": true,
+ "unused": true,
+ "globals": {
+ "angular": false,
+ "inject": false
+ }
+}
diff --git a/gui/test/karma.conf.js b/gui/test/karma.conf.js
new file mode 100644
index 000000000..a9ab3a824
--- /dev/null
+++ b/gui/test/karma.conf.js
@@ -0,0 +1,93 @@
+// Karma configuration
+// Generated on 2017-05-31
+
+module.exports = function(config) {
+ 'use strict';
+
+ config.set({
+ // enable / disable watching file and executing tests whenever any file changes
+ autoWatch: true,
+
+ // base path, that will be used to resolve files and exclude
+ basePath: '../',
+
+ // testing framework to use (jasmine/mocha/qunit/...)
+ // as well as any additional frameworks (requirejs/chai/sinon/...)
+ frameworks: [
+ 'jasmine'
+ ],
+
+ // list of files / patterns to load in the browser
+ files: [
+ // bower:js
+ 'bower_components/jquery/dist/jquery.js',
+ 'bower_components/angular/angular.js',
+ 'bower_components/bootstrap/dist/js/bootstrap.js',
+ 'bower_components/angular-strap/dist/angular-strap.js',
+ 'bower_components/angular-strap/dist/angular-strap.tpl.js',
+ 'bower_components/angular-ui-router/release/angular-ui-router.js',
+ 'bower_components/angular-animate/angular-animate.js',
+ 'bower_components/angular-breadcrumb/release/angular-breadcrumb.js',
+ 'bower_components/angular-wizard/dist/angular-wizard.min.js',
+ 'bower_components/angular-resource/angular-resource.js',
+ 'bower_components/ng-file-upload/ng-file-upload.js',
+ 'bower_components/AngularJS-Toaster/toaster.js',
+ 'bower_components/ng-dialog/js/ngDialog.js',
+ 'bower_components/angularUtils-pagination/dirPagination.js',
+ 'bower_components/ngstorage/ngStorage.js',
+ 'bower_components/v-accordion/dist/v-accordion.js',
+ 'bower_components/spin.js/spin.js',
+ 'bower_components/angular-loading/angular-loading.js',
+ 'bower_components/spin.js/spin.js',
+ 'bower_components/angular-bootstrap/ui-bootstrap-tpls.js',
+ 'bower_components/angular-sanitize/angular-sanitize.js',
+ 'bower_components/angular-mocks/angular-mocks.js',
+ // endbower
+ 'app/scripts/**/*.js',
+ 'test/mock/**/*.js',
+ 'test/spec/**/*.js'
+ ],
+
+ // list of files / patterns to exclude
+ exclude: [
+ ],
+
+ // web server port
+ port: 8080,
+
+ // Start these browsers, currently available:
+ // - Chrome
+ // - ChromeCanary
+ // - Firefox
+ // - Opera
+ // - Safari (only Mac)
+ // - PhantomJS
+ // - IE (only Windows)
+ browsers: [
+ 'PhantomJS'
+ ],
+
+ // Which plugins to enable
+ plugins: [
+ 'karma-phantomjs-launcher',
+ 'karma-jasmine'
+ ],
+
+ // Continuous Integration mode
+ // if true, it capture browsers, run tests and exit
+ singleRun: false,
+
+ colors: true,
+
+ // level of logging
+ // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
+ logLevel: config.LOG_INFO,
+
+ // Uncomment the following lines if you are using grunt's server to run the tests
+ // proxies: {
+ // '/': 'http://localhost:9000/'
+ // },
+ // URL root prevent conflicts with the site root
+ // urlRoot: '_karma_'
+ });
+};
diff --git a/gui/test/spec/controllers/main.js b/gui/test/spec/controllers/main.js
new file mode 100644
index 000000000..27e0a5ad3
--- /dev/null
+++ b/gui/test/spec/controllers/main.js
@@ -0,0 +1,23 @@
+'use strict';
+
+describe('Controller: MainCtrl', function () {
+
+ // load the controller's module
+ beforeEach(module('yardStickGui2App'));
+
+ var MainCtrl,
+ scope;
+
+ // Initialize the controller and a mock scope
+ beforeEach(inject(function ($controller, $rootScope) {
+ scope = $rootScope.$new();
+ MainCtrl = $controller('MainCtrl', {
+ $scope: scope
+ // place here mocked dependencies
+ });
+ }));
+
+ it('should attach a list of awesomeThings to the scope', function () {
+ expect(MainCtrl.awesomeThings.length).toBe(3);
+ });
+});
diff --git a/install.sh b/install.sh
index ad14b8e0b..e82ae0233 100755
--- a/install.sh
+++ b/install.sh
@@ -86,7 +86,10 @@ easy_install -U pip
pip install -r requirements.txt
pip install -e .
-/bin/bash "$(pwd)/api/api-prepare.sh"
+/bin/bash "${PWD}/docker/uwsgi.sh"
+/bin/bash "${PWD}/docker/nginx.sh"
+cd "${PWD}/gui" && /bin/bash gui.sh
+mv dist /etc/nginx/yardstick/gui
service nginx restart
uwsgi -i /etc/yardstick/yardstick.ini
diff --git a/samples/vnf_samples/nsut/ping/tc_ping_ovs_dpdk_context.yaml b/samples/vnf_samples/nsut/ping/tc_ping_ovs_dpdk_context.yaml
new file mode 100644
index 000000000..7654b0f96
--- /dev/null
+++ b/samples/vnf_samples/nsut/ping/tc_ping_ovs_dpdk_context.yaml
@@ -0,0 +1,42 @@
+# Copyright (c) 2016-2017 Intel Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+---
+schema: "yardstick:task:0.1"
+
+scenarios:
+-
+ type: NSPerf
+ traffic_profile: ../../traffic_profiles/fixed.yaml
+ topology: ping_tg_topology.yaml # TODO: look in relative path where the tc.yaml is found
+
+ nodes: # This section is copied from pod.xml or resolved via Heat
+ tg__1: trafficgen_1.yardstick
+ vnf__1: vnf.yardstick
+
+ vnf_options:
+ tg__1:
+ target_ip: pingvnf__1.xe0.local_ip # TODO: resolve to config vars
+ vnf__1:
+ target_ip: pinggen__1.xe1.local_ip # TODO: resolve to config vars
+ runner:
+ type: Duration
+ duration: 10
+
+context:
+ type: Standalone
+ name: yardstick
+ nfvi_type: Ovsdpdk
+ vm_deploy: True
+ file: /etc/yardstick/nodes/pod_ovs.yaml
diff --git a/tests/ci/scp_storperf_files.sh b/tests/ci/scp_storperf_files.sh
index ffcc710cb..71306eb80 100644
--- a/tests/ci/scp_storperf_files.sh
+++ b/tests/ci/scp_storperf_files.sh
@@ -13,23 +13,25 @@
ssh_options="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
+scp_files(){
+ export JUMP_HOST_IP
+ sshpass -p root scp 2>/dev/null $ssh_options ~/storperf_admin-rc \
+ root@${JUMP_HOST_IP}:/root/ &> /dev/null
+ sshpass -p root scp 2>/dev/null $ssh_options /home/opnfv/repos/storperf/docker-compose/docker-compose.yaml \
+ root@${JUMP_HOST_IP}:/root/ &> /dev/null
+}
+
case "$NODE_NAME" in
"huawei-pod1")
JUMP_HOST_IP='192.168.10.6'
+ scp_files
;;
"huawei-pod2")
JUMP_HOST_IP='192.168.11.2'
+ scp_files
;;
*)
# no node name, exit
- exit 1
+ echo "storperf test case will not run on this pod, skipping scp files..."
;;
esac
-export JUMP_HOST_IP
-
-sshpass -p root scp 2>/dev/null $ssh_options ~/storperf_admin-rc \
- root@${JUMP_HOST_IP}:/root/ &> /dev/null
-sshpass -p root scp 2>/dev/null $ssh_options /home/opnfv/repos/storperf/docker-compose/docker-compose.yaml \
- root@${JUMP_HOST_IP}:/root/ &> /dev/null
-sshpass -p root scp 2>/dev/null $ssh_options /home/opnfv/repos/storperf/docker-compose/nginx.conf \
- root@${JUMP_HOST_IP}:/root/ &> /dev/null
diff --git a/tests/ci/yardstick-verify b/tests/ci/yardstick-verify
index 751cf65f3..16598df7b 100755
--- a/tests/ci/yardstick-verify
+++ b/tests/ci/yardstick-verify
@@ -99,8 +99,8 @@ set -o pipefail
install_storperf()
{
- # Install Storper on huawei-pod1
- if [ "$INSTALLER_TYPE" == "compass" ]; then
+ # Install Storper on huawei-pod1 and huawei-pod2
+ if [ "$NODE_NAME" == "huawei-pod1" -o "$NODE_NAME" == "huawei-pod2" ]; then
echo
echo "========== Installing storperf =========="
@@ -114,8 +114,8 @@ install_storperf()
remove_storperf()
{
- # remove Storper from huawei-pod1
- if [ "$INSTALLER_TYPE" == "compass" ]; then
+ # remove Storper from huawei-pod1 and huawei-pod2
+ if [ "$NODE_NAME" == "huawei-pod1" -o "$NODE_NAME" == "huawei-pod2" ]; then
echo
echo "========== Removing storperf =========="
@@ -293,8 +293,13 @@ main()
echo
# check OpenStack services
+ if [[ $OS_INSECURE ]] && [[ "$(echo $OS_INSECURE | tr '[:upper:]' '[:lower:]')" = "true" ]]; then
+ SECURE="--insecure"
+ else
+ SECURE=""
+ fi
echo "Checking OpenStack services:"
- for cmd in "openstack image list" "openstack server list" "openstack stack list"; do
+ for cmd in "openstack ${SECURE} image list" "openstack ${SECURE} server list" "openstack ${SECURE} stack list"; do
echo " checking ${cmd} ..."
if ! $cmd >/dev/null; then
echo "error: command \"$cmd\" failed"
diff --git a/tests/opnfv/test_cases/opnfv_yardstick_tc008.yaml b/tests/opnfv/test_cases/opnfv_yardstick_tc008.yaml
index 4c7fdab90..f5ccb255a 100644
--- a/tests/opnfv/test_cases/opnfv_yardstick_tc008.yaml
+++ b/tests/opnfv/test_cases/opnfv_yardstick_tc008.yaml
@@ -29,12 +29,21 @@ scenarios:
packetsize: {{pkt_size}}
number_of_ports: {{num_ports}}
duration: 20
+ # choose vnic name: default to eth0
+ # vnic_name: 'ens3'
+ # turn on multiqueue inside VM
+ # multiqueue: True
+ # choose starting pps: default 1M;
+ # works with binary search runner Dynamictp to find max throughput per sla
+ # pps: 3000000
host: demeter.yardstick-TC008
target: poseidon.yardstick-TC008
runner:
type: Iteration
+ # binary search runner
+ # type: Dynamictp
iterations: 10
interval: 1
diff --git a/tests/unit/benchmark/contexts/nodes_duplicate_sample_new.yaml b/tests/unit/benchmark/contexts/nodes_duplicate_sample_new.yaml
new file mode 100644
index 000000000..306915ca1
--- /dev/null
+++ b/tests/unit/benchmark/contexts/nodes_duplicate_sample_new.yaml
@@ -0,0 +1,32 @@
+nodes:
+-
+ name: sriov
+ role: Sriov1
+ ip: 10.123.123.122
+ user: root
+ auth_type: password
+ password: password
+ vf_macs:
+ - "00:00:00:00:00:00"
+ - "00:00:00:00:00:00"
+ phy_ports: # Physical ports to configure sriov
+ - "0000:06:00.0"
+ - "0000:06:00.1"
+ phy_driver: i40e # kernel driver
+ images: "/var/lib/libvirt/images/ubuntu1.img"
+-
+ name: sriov
+ role: Sriov1
+ ip: 10.123.123.111
+ user: root
+ auth_type: password
+ password: password
+ vf_macs:
+ - "00:00:00:00:00:00"
+ - "00:00:00:00:00:00"
+ phy_ports: # Physical ports to configure sriov
+ - "0000:06:00.0"
+ - "0000:06:00.1"
+ phy_driver: i40e # kernel driver
+ images: "/var/lib/libvirt/images/ubuntu1.img"
+
diff --git a/tests/unit/benchmark/contexts/nodes_duplicate_sample_ovs.yaml b/tests/unit/benchmark/contexts/nodes_duplicate_sample_ovs.yaml
new file mode 100644
index 000000000..65449c91c
--- /dev/null
+++ b/tests/unit/benchmark/contexts/nodes_duplicate_sample_ovs.yaml
@@ -0,0 +1,63 @@
+# Copyright (c) 2016 Intel Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+nodes:
+-
+ name: ovs
+ role: test
+ ip: 10.223.197.222
+ user: root
+ auth_type: password
+ password: intel123
+ vpath: "/usr/local/"
+ vports:
+ - dpdkvhostuser0
+ - dpdkvhostuser1
+ vports_mac:
+ - "00:00:00:00:00:03"
+ - "00:00:00:00:00:04"
+ phy_ports: # Physical ports to configure ovs
+ - "0000:06:00.0"
+ - "0000:06:00.1"
+ flow:
+ - ovs-ofctl add-flow br0 in_port=1,action=output:3
+ - ovs-ofctl add-flow br0 in_port=3,action=output:1
+ - ovs-ofctl add-flow br0 in_port=4,action=output:2
+ - ovs-ofctl add-flow br0 in_port=2,action=output:4
+ phy_driver: i40e # kernel driver
+ images: "/var/lib/libvirt/images/ubuntu1.img"
+-
+ name: ovs
+ role: test
+ ip: 10.223.197.112
+ user: root
+ auth_type: password
+ password: intel123
+ vpath: "/usr/local/"
+ vports:
+ - dpdkvhostuser0
+ - dpdkvhostuser1
+ vports_mac:
+ - "00:00:00:00:00:03"
+ - "00:00:00:00:00:04"
+ phy_ports: # Physical ports to configure ovs
+ - "0000:06:00.0"
+ - "0000:06:00.1"
+ flow:
+ - ovs-ofctl add-flow br0 in_port=1,action=output:3
+ - ovs-ofctl add-flow br0 in_port=3,action=output:1
+ - ovs-ofctl add-flow br0 in_port=4,action=output:2
+ - ovs-ofctl add-flow br0 in_port=2,action=output:4
+ phy_driver: i40e # kernel driver
+ images: "/var/lib/libvirt/images/ubuntu1.img"
+
diff --git a/tests/unit/benchmark/contexts/nodes_sample_new.yaml b/tests/unit/benchmark/contexts/nodes_sample_new.yaml
new file mode 100644
index 000000000..a400bec03
--- /dev/null
+++ b/tests/unit/benchmark/contexts/nodes_sample_new.yaml
@@ -0,0 +1,96 @@
+# Copyright (c) 2016-2017 Intel Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+nodes:
+-
+ name: trafficgen_1
+ role: TrafficGen
+ ip: 10.123.123.123
+ user: root
+ auth_type: password
+ password: password
+ interfaces:
+ xe0: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:03:00.0"
+ driver: ixgbe
+ dpdk_port_num: 0
+ local_ip: "152.16.100.20"
+ netmask: "255.255.255.0"
+ local_mac: "00:00:00:00:00:00"
+ xe1: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:03:00.1"
+ driver: ixgbe
+ dpdk_port_num: 1
+ local_ip: "152.16.100.21"
+ netmask: "255.255.255.0"
+ local_mac: "00:00:00:00:00:00"
+-
+ name: sriov
+ role: Sriov
+ ip: 10.123.123.122
+ user: root
+ auth_type: password
+ password: password
+ vf_macs:
+ - "00:00:00:00:00:00"
+ - "00:00:00:00:00:00"
+ phy_ports: # Physical ports to configure sriov
+ - "0000:06:00.0"
+ - "0000:06:00.1"
+ phy_driver: i40e # kernel driver
+ images: "/var/lib/libvirt/images/ubuntu1.img"
+
+-
+ name: vnf
+ role: vnf
+ ip: 10.123.123.121
+ user: root
+ auth_type: password
+ password: password
+ host: 10.123.123.121 #BM host == ip, SRIOV & ovs-dpdk host == compute node.
+ interfaces:
+ xe0: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:06:00.0"
+ driver: i40e
+ dpdk_port_num: 0
+ local_ip: "152.16.100.19"
+ netmask: "255.255.255.0"
+ local_mac: "00:00:00:00:00:00"
+
+ xe1: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:06:00.1"
+ driver: i40e
+ dpdk_port_num: 1
+ local_ip: "152.16.40.19"
+ netmask: "255.255.255.0"
+ local_mac: "00:00:00:00:00:00"
+ routing_table:
+ - network: "152.16.100.20"
+ netmask: "255.255.255.0"
+ gateway: "152.16.100.20"
+ if: "xe0"
+ - network: "152.16.40.20"
+ netmask: "255.255.255.0"
+ gateway: "152.16.40.20"
+ if: "xe1"
+ nd_route_tbl:
+ - network: "0064:ff9b:0:0:0:0:9810:6414"
+ netmask: "112"
+ gateway: "0064:ff9b:0:0:0:0:9810:6414"
+ if: "xe0"
+ - network: "0064:ff9b:0:0:0:0:9810:2814"
+ netmask: "112"
+ gateway: "0064:ff9b:0:0:0:0:9810:2814"
+ if: "xe1"
+
diff --git a/tests/unit/benchmark/contexts/nodes_sample_new_sriov.yaml b/tests/unit/benchmark/contexts/nodes_sample_new_sriov.yaml
new file mode 100644
index 000000000..55ff2e778
--- /dev/null
+++ b/tests/unit/benchmark/contexts/nodes_sample_new_sriov.yaml
@@ -0,0 +1,82 @@
+nodes:
+-
+ name: trafficgen_1
+ role: TrafficGen
+ ip: 10.123.123.123
+ user: root
+ auth_type: password
+ password: password
+ interfaces:
+ xe0: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:03:00.0"
+ driver: ixgbe
+ dpdk_port_num: 0
+ local_ip: "152.16.100.20"
+ netmask: "255.255.255.0"
+ local_mac: "00:00:00:00:00:00"
+ xe1: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:03:00.1"
+ driver: ixgbe
+ dpdk_port_num: 1
+ local_ip: "152.16.100.21"
+ netmask: "255.255.255.0"
+ local_mac: "00:00:00:00:00:00"
+-
+ name: sriov
+ role: Sriov1
+ ip: 10.123.123.122
+ user: root
+ auth_type: password
+ password: password
+ vf_macs:
+ - "00:00:00:00:00:00"
+ - "00:00:00:00:00:00"
+ phy_ports: # Physical ports to configure sriov
+ - "0000:06:00.0"
+ - "0000:06:00.1"
+ phy_driver: i40e # kernel driver
+ images: "/var/lib/libvirt/images/ubuntu1.img"
+
+-
+ name: vnf
+ role: vnf
+ ip: 10.123.123.121
+ user: root
+ auth_type: password
+ password: password
+ host: 10.123.123.121 #BM host == ip, SRIOV & ovs-dpdk host == compute node.
+ interfaces:
+ xe0: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:06:00.0"
+ driver: i40e
+ dpdk_port_num: 0
+ local_ip: "152.16.100.19"
+ netmask: "255.255.255.0"
+ local_mac: "00:00:00:00:00:00"
+
+ xe1: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:06:00.1"
+ driver: i40e
+ dpdk_port_num: 1
+ local_ip: "152.16.40.19"
+ netmask: "255.255.255.0"
+ local_mac: "00:00:00:00:00:00"
+ routing_table:
+ - network: "152.16.100.20"
+ netmask: "255.255.255.0"
+ gateway: "152.16.100.20"
+ if: "xe0"
+ - network: "152.16.40.20"
+ netmask: "255.255.255.0"
+ gateway: "152.16.40.20"
+ if: "xe1"
+ nd_route_tbl:
+ - network: "0064:ff9b:0:0:0:0:9810:6414"
+ netmask: "112"
+ gateway: "0064:ff9b:0:0:0:0:9810:6414"
+ if: "xe0"
+ - network: "0064:ff9b:0:0:0:0:9810:2814"
+ netmask: "112"
+ gateway: "0064:ff9b:0:0:0:0:9810:2814"
+ if: "xe1"
+
diff --git a/tests/unit/benchmark/contexts/nodes_sample_ovs.yaml b/tests/unit/benchmark/contexts/nodes_sample_ovs.yaml
new file mode 100644
index 000000000..b1da1ea9f
--- /dev/null
+++ b/tests/unit/benchmark/contexts/nodes_sample_ovs.yaml
@@ -0,0 +1,104 @@
+# Copyright (c) 2016 Intel Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+nodes:
+-
+ name: trafficgen_1
+ role: TrafficGen
+ ip: 10.223.197.182
+ user: root
+ auth_type: password
+ password: intel123
+ interfaces:
+ xe0: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:03:00.0"
+ driver: ixgbe
+ dpdk_port_num: 0
+ local_ip: "152.16.100.20"
+ netmask: "255.255.255.0"
+ local_mac: "90:e2:ba:77:ce:68"
+ xe1: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:03:00.1"
+ driver: ixgbe
+ dpdk_port_num: 1
+ local_ip: "152.16.100.21"
+ netmask: "255.255.255.0"
+ local_mac: "90:e2:ba:77:ce:69"
+-
+ name: ovs
+ role: Ovsdpdk
+ ip: 10.223.197.222
+ user: root
+ auth_type: password
+ password: intel123
+ vpath: "/usr/local/"
+ vports:
+ - dpdkvhostuser0
+ - dpdkvhostuser1
+ vports_mac:
+ - "00:00:00:00:00:03"
+ - "00:00:00:00:00:04"
+ phy_ports: # Physical ports to configure ovs
+ - "0000:06:00.0"
+ - "0000:06:00.1"
+ flow:
+ - ovs-ofctl add-flow br0 in_port=1,action=output:3
+ - ovs-ofctl add-flow br0 in_port=3,action=output:1
+ - ovs-ofctl add-flow br0 in_port=4,action=output:2
+ - ovs-ofctl add-flow br0 in_port=2,action=output:4
+ phy_driver: i40e # kernel driver
+ images: "/var/lib/libvirt/images/ubuntu1.img"
+
+-
+ name: vnf
+ role: vnf
+ ip: 10.223.197.155
+ user: root
+ auth_type: password
+ password: intel123
+ host: 10.223.197.140
+ interfaces:
+ xe0: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:00:04.0"
+ driver: virtio-pci
+ dpdk_port_num: 0
+ local_ip: "152.16.100.19"
+ netmask: "255.255.255.0"
+ local_mac: "00:00:00:00:00:03"
+
+ xe1: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:00:05.0"
+ driver: virtio-pci
+ dpdk_port_num: 1
+ local_ip: "152.16.40.19"
+ netmask: "255.255.255.0"
+ local_mac: "00:00:00:00:00:04"
+ routing_table:
+ - network: "152.16.100.20"
+ netmask: "255.255.255.0"
+ gateway: "152.16.100.20"
+ if: "xe0"
+ - network: "152.16.40.20"
+ netmask: "255.255.255.0"
+ gateway: "152.16.40.20"
+ if: "xe1"
+ nd_route_tbl:
+ - network: "0064:ff9b:0:0:0:0:9810:6414"
+ netmask: "112"
+ gateway: "0064:ff9b:0:0:0:0:9810:6414"
+ if: "xe0"
+ - network: "0064:ff9b:0:0:0:0:9810:2814"
+ netmask: "112"
+ gateway: "0064:ff9b:0:0:0:0:9810:2814"
+ if: "xe1"
diff --git a/tests/unit/benchmark/contexts/nodes_sample_ovsdpdk.yaml b/tests/unit/benchmark/contexts/nodes_sample_ovsdpdk.yaml
new file mode 100644
index 000000000..c02849a05
--- /dev/null
+++ b/tests/unit/benchmark/contexts/nodes_sample_ovsdpdk.yaml
@@ -0,0 +1,104 @@
+# Copyright (c) 2016 Intel Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+nodes:
+-
+ name: trafficgen_1
+ role: TrafficGen
+ ip: 10.223.197.182
+ user: root
+ auth_type: password
+ password: intel123
+ interfaces:
+ xe0: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:03:00.0"
+ driver: ixgbe
+ dpdk_port_num: 0
+ local_ip: "152.16.100.20"
+ netmask: "255.255.255.0"
+ local_mac: "90:e2:ba:77:ce:68"
+ xe1: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:03:00.1"
+ driver: ixgbe
+ dpdk_port_num: 1
+ local_ip: "152.16.100.21"
+ netmask: "255.255.255.0"
+ local_mac: "90:e2:ba:77:ce:69"
+-
+ name: ovs
+ role: Ovsdpdk1
+ ip: 10.223.197.222
+ user: root
+ auth_type: password
+ password: intel123
+ vpath: "/usr/local/"
+ vports:
+ - dpdkvhostuser0
+ - dpdkvhostuser1
+ vports_mac:
+ - "00:00:00:00:00:03"
+ - "00:00:00:00:00:04"
+ phy_ports: # Physical ports to configure ovs
+ - "0000:06:00.0"
+ - "0000:06:00.1"
+ flow:
+ - ovs-ofctl add-flow br0 in_port=1,action=output:3
+ - ovs-ofctl add-flow br0 in_port=3,action=output:1
+ - ovs-ofctl add-flow br0 in_port=4,action=output:2
+ - ovs-ofctl add-flow br0 in_port=2,action=output:4
+ phy_driver: i40e # kernel driver
+ images: "/var/lib/libvirt/images/ubuntu1.img"
+
+-
+ name: vnf
+ role: vnf
+ ip: 10.223.197.155
+ user: root
+ auth_type: password
+ password: intel123
+ host: 10.223.197.140
+ interfaces:
+ xe0: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:00:04.0"
+ driver: virtio-pci
+ dpdk_port_num: 0
+ local_ip: "152.16.100.19"
+ netmask: "255.255.255.0"
+ local_mac: "00:00:00:00:00:03"
+
+ xe1: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:00:05.0"
+ driver: virtio-pci
+ dpdk_port_num: 1
+ local_ip: "152.16.40.19"
+ netmask: "255.255.255.0"
+ local_mac: "00:00:00:00:00:04"
+ routing_table:
+ - network: "152.16.100.20"
+ netmask: "255.255.255.0"
+ gateway: "152.16.100.20"
+ if: "xe0"
+ - network: "152.16.40.20"
+ netmask: "255.255.255.0"
+ gateway: "152.16.40.20"
+ if: "xe1"
+ nd_route_tbl:
+ - network: "0064:ff9b:0:0:0:0:9810:6414"
+ netmask: "112"
+ gateway: "0064:ff9b:0:0:0:0:9810:6414"
+ if: "xe0"
+ - network: "0064:ff9b:0:0:0:0:9810:2814"
+ netmask: "112"
+ gateway: "0064:ff9b:0:0:0:0:9810:2814"
+ if: "xe1"
diff --git a/tests/unit/benchmark/contexts/ovs_sample_password.yaml b/tests/unit/benchmark/contexts/ovs_sample_password.yaml
new file mode 100644
index 000000000..b1da1ea9f
--- /dev/null
+++ b/tests/unit/benchmark/contexts/ovs_sample_password.yaml
@@ -0,0 +1,104 @@
+# Copyright (c) 2016 Intel Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+nodes:
+-
+ name: trafficgen_1
+ role: TrafficGen
+ ip: 10.223.197.182
+ user: root
+ auth_type: password
+ password: intel123
+ interfaces:
+ xe0: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:03:00.0"
+ driver: ixgbe
+ dpdk_port_num: 0
+ local_ip: "152.16.100.20"
+ netmask: "255.255.255.0"
+ local_mac: "90:e2:ba:77:ce:68"
+ xe1: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:03:00.1"
+ driver: ixgbe
+ dpdk_port_num: 1
+ local_ip: "152.16.100.21"
+ netmask: "255.255.255.0"
+ local_mac: "90:e2:ba:77:ce:69"
+-
+ name: ovs
+ role: Ovsdpdk
+ ip: 10.223.197.222
+ user: root
+ auth_type: password
+ password: intel123
+ vpath: "/usr/local/"
+ vports:
+ - dpdkvhostuser0
+ - dpdkvhostuser1
+ vports_mac:
+ - "00:00:00:00:00:03"
+ - "00:00:00:00:00:04"
+ phy_ports: # Physical ports to configure ovs
+ - "0000:06:00.0"
+ - "0000:06:00.1"
+ flow:
+ - ovs-ofctl add-flow br0 in_port=1,action=output:3
+ - ovs-ofctl add-flow br0 in_port=3,action=output:1
+ - ovs-ofctl add-flow br0 in_port=4,action=output:2
+ - ovs-ofctl add-flow br0 in_port=2,action=output:4
+ phy_driver: i40e # kernel driver
+ images: "/var/lib/libvirt/images/ubuntu1.img"
+
+-
+ name: vnf
+ role: vnf
+ ip: 10.223.197.155
+ user: root
+ auth_type: password
+ password: intel123
+ host: 10.223.197.140
+ interfaces:
+ xe0: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:00:04.0"
+ driver: virtio-pci
+ dpdk_port_num: 0
+ local_ip: "152.16.100.19"
+ netmask: "255.255.255.0"
+ local_mac: "00:00:00:00:00:03"
+
+ xe1: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:00:05.0"
+ driver: virtio-pci
+ dpdk_port_num: 1
+ local_ip: "152.16.40.19"
+ netmask: "255.255.255.0"
+ local_mac: "00:00:00:00:00:04"
+ routing_table:
+ - network: "152.16.100.20"
+ netmask: "255.255.255.0"
+ gateway: "152.16.100.20"
+ if: "xe0"
+ - network: "152.16.40.20"
+ netmask: "255.255.255.0"
+ gateway: "152.16.40.20"
+ if: "xe1"
+ nd_route_tbl:
+ - network: "0064:ff9b:0:0:0:0:9810:6414"
+ netmask: "112"
+ gateway: "0064:ff9b:0:0:0:0:9810:6414"
+ if: "xe0"
+ - network: "0064:ff9b:0:0:0:0:9810:2814"
+ netmask: "112"
+ gateway: "0064:ff9b:0:0:0:0:9810:2814"
+ if: "xe1"
diff --git a/tests/unit/benchmark/contexts/ovs_sample_ssh_key.yaml b/tests/unit/benchmark/contexts/ovs_sample_ssh_key.yaml
new file mode 100644
index 000000000..896ec33bb
--- /dev/null
+++ b/tests/unit/benchmark/contexts/ovs_sample_ssh_key.yaml
@@ -0,0 +1,69 @@
+##############################################################################
+# Copyright (c) 2015 Huawei Technologies Co.,Ltd 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
+##############################################################################
+---
+# Sample config file about the POD information, including the
+# name/IP/user/ssh key of Bare Metal and Controllers/Computes
+#
+# The options of this config file include:
+# name: the name of this node
+# role: node's role, support role: Master/Controller/Comupte/BareMetal
+# ip: the node's IP address
+# user: the username for login
+# key_filename:the path of the private key file for login
+
+nodes:
+-
+ name: trafficgen_1
+ role: TrafficGen
+ ip: 10.10.10.10
+ auth_type: ssh_key
+ user: root
+ ssh_port: 22
+ key_filename: /root/.ssh/id_rsa
+ interfaces:
+ xe0: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:03:00.0"
+ driver: ixgbe
+ dpdk_port_num: 0
+ local_ip: "152.16.100.20"
+ netmask: "255.255.255.0"
+ local_mac: "90:e2:ba:77:ce:68"
+ xe1: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:03:00.1"
+ driver: ixgbe
+ dpdk_port_num: 1
+ local_ip: "152.16.100.21"
+ netmask: "255.255.255.0"
+ local_mac: "90:e2:ba:77:ce:69"
+-
+ name: ovs
+ role: Ovsdpdk
+ ip: 10.223.197.222
+ auth_type: ssh_key
+ user: root
+ ssh_port: 22
+ key_filename: /root/.ssh/id_rsa
+ vpath: "/usr/local/"
+ vports:
+ - dpdkvhostuser0
+ - dpdkvhostuser1
+ vports_mac:
+ - "00:00:00:00:00:03"
+ - "00:00:00:00:00:04"
+ phy_ports: # Physical ports to configure ovs
+ - "0000:06:00.0"
+ - "0000:06:00.1"
+ flow:
+ - ovs-ofctl add-flow br0 in_port=1,action=output:3
+ - ovs-ofctl add-flow br0 in_port=3,action=output:1
+ - ovs-ofctl add-flow br0 in_port=4,action=output:2
+ - ovs-ofctl add-flow br0 in_port=2,action=output:4
+ phy_driver: i40e # kernel driver
+ images: "/var/lib/libvirt/images/ubuntu1.img"
+
diff --git a/tests/unit/benchmark/contexts/ovs_sample_write_to_file.txt b/tests/unit/benchmark/contexts/ovs_sample_write_to_file.txt
new file mode 100644
index 000000000..f0eec86f6
--- /dev/null
+++ b/tests/unit/benchmark/contexts/ovs_sample_write_to_file.txt
@@ -0,0 +1 @@
+some content \ No newline at end of file
diff --git a/tests/unit/benchmark/contexts/sriov_sample_password.yaml b/tests/unit/benchmark/contexts/sriov_sample_password.yaml
new file mode 100644
index 000000000..4f60e46d5
--- /dev/null
+++ b/tests/unit/benchmark/contexts/sriov_sample_password.yaml
@@ -0,0 +1,52 @@
+# Copyright (c) 2016-2017 Intel Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+nodes:
+-
+ name: trafficgen_1
+ role: TrafficGen
+ ip: 10.10.10.10
+ auth_type: password
+ user: root
+ password: password
+ interfaces:
+ xe0: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:03:00.0"
+ driver: ixgbe
+ dpdk_port_num: 0
+ local_ip: "152.16.100.20"
+ netmask: "255.255.255.0"
+ local_mac: "90:e2:ba:77:ce:68"
+ xe1: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:03:00.1"
+ driver: ixgbe
+ dpdk_port_num: 1
+ local_ip: "152.16.100.21"
+ netmask: "255.255.255.0"
+ local_mac: "90:e2:ba:77:ce:69"
+-
+ name: sriov
+ role: Sriov
+ ip: 10.10.10.11
+ auth_type: password
+ user: root
+ password: password
+ vf_macs:
+ - "00:00:00:71:7d:25"
+ - "00:00:00:71:7d:26"
+ phy_ports: # Physical ports to configure sriov
+ - "0000:06:00.0"
+ - "0000:06:00.1"
+ phy_driver: i40e # kernel driver
+ images: "/var/lib/libvirt/images/ubuntu1.img"
diff --git a/tests/unit/benchmark/contexts/sriov_sample_ssh_key.yaml b/tests/unit/benchmark/contexts/sriov_sample_ssh_key.yaml
new file mode 100644
index 000000000..faa496771
--- /dev/null
+++ b/tests/unit/benchmark/contexts/sriov_sample_ssh_key.yaml
@@ -0,0 +1,54 @@
+# Copyright (c) 2016-2017 Intel Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+nodes:
+-
+ name: trafficgen_1
+ role: TrafficGen
+ ip: 10.10.10.10
+ auth_type: ssh_key
+ user: root
+ ssh_port: 22
+ key_filename: /root/.ssh/id_rsa
+ interfaces:
+ xe0: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:03:00.0"
+ driver: ixgbe
+ dpdk_port_num: 0
+ local_ip: "152.16.100.20"
+ netmask: "255.255.255.0"
+ local_mac: "90:e2:ba:77:ce:68"
+ xe1: # logical name from topology.yaml and vnfd.yaml
+ vpci: "0000:03:00.1"
+ driver: ixgbe
+ dpdk_port_num: 1
+ local_ip: "152.16.100.21"
+ netmask: "255.255.255.0"
+ local_mac: "90:e2:ba:77:ce:69"
+-
+ name: sriov
+ role: Sriov
+ ip: 10.10.10.11
+ auth_type: ssh_key
+ user: root
+ ssh_port: 22
+ key_filename: /root/.ssh/id_rsa
+ vf_macs:
+ - "00:00:00:71:7d:25"
+ - "00:00:00:71:7d:26"
+ phy_ports: # Physical ports to configure sriov
+ - "0000:06:00.0"
+ - "0000:06:00.1"
+ phy_driver: i40e # kernel driver
+ images: "/var/lib/libvirt/images/ubuntu1.img"
diff --git a/tests/unit/benchmark/contexts/sriov_sample_write_to_file.txt b/tests/unit/benchmark/contexts/sriov_sample_write_to_file.txt
new file mode 100644
index 000000000..f0eec86f6
--- /dev/null
+++ b/tests/unit/benchmark/contexts/sriov_sample_write_to_file.txt
@@ -0,0 +1 @@
+some content \ No newline at end of file
diff --git a/tests/unit/benchmark/contexts/test_ovsdpdk.py b/tests/unit/benchmark/contexts/test_ovsdpdk.py
new file mode 100644
index 000000000..ac25ec877
--- /dev/null
+++ b/tests/unit/benchmark/contexts/test_ovsdpdk.py
@@ -0,0 +1,325 @@
+# Copyright (c) 2016-2017 Intel Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import absolute_import
+import os
+import mock
+import unittest
+
+from yardstick.benchmark.contexts import ovsdpdk
+
+NIC_INPUT = {
+ 'interface': {},
+ 'vports_mac': ['00:00:00:71:7d:25', '00:00:00:71:7d:26'],
+ 'pci': ['0000:06:00.0', '0000:06:00.1'],
+ 'phy_driver': 'i40e'}
+DRIVER = "i40e"
+NIC_DETAILS = {
+ 'interface': {0: 'enp6s0f0', 1: 'enp6s0f1'},
+ 'vports_mac': ['00:00:00:71:7d:25', '00:00:00:71:7d:26'],
+ 'pci': ['0000:06:00.0', '0000:06:00.1'],
+ 'phy_driver': 'i40e'}
+
+CORRECT_FILE_PATH = "/etc/yardstick/nodes/pod_ovs.yaml"
+WRONG_FILE_PATH = "/etc/yardstick/wrong.yaml"
+SAMPLE_FILE = "ovs_sample_write_to_file.txt"
+
+OVS = [{
+ 'auth_type': 'ssh_key',
+ 'name': 'ovs',
+ 'ssh_port': 22,
+ 'ip': '10.10.10.11',
+ 'key_filename': '/root/.ssh/id_rsa',
+ 'vports_mac': ['00:00:00:00:00:03', '00:00:00:00:00:04'],
+ 'vpath': '/usr/local/',
+ 'role': 'Ovsdpdk',
+ 'user': 'root',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'flow': ['ovs-ofctl add-flow br0 in_port=1,action=output:3',
+ 'ovs-ofctl add-flow br0 in_port=3,action=output:1',
+ 'ovs-ofctl add-flow br0 in_port=4,action=output:2',
+ 'ovs-ofctl add-flow br0 in_port=2,action=output:4'],
+ 'phy_driver': 'i40e',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+
+OVS_PASSWORD = [{
+ 'auth_type': 'password',
+ 'name': 'ovs',
+ 'vports_mac': ['00:00:00:00:00:03', '00:00:00:00:00:04'],
+ 'ip': '10.10.10.11',
+ 'role': 'Ovsdpdk',
+ 'user': 'root',
+ 'vpath': '/usr/local/',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'flow': ['ovs-ofctl add-flow br0 in_port=1,action=output:3',
+ 'ovs-ofctl add-flow br0 in_port=3,action=output:1',
+ 'ovs-ofctl add-flow br0 in_port=4,action=output:2',
+ 'ovs-ofctl add-flow br0 in_port=2,action=output:4'],
+ 'phy_driver': 'i40e',
+ 'password': 'password',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+
+#vfnic = "i40evf"
+PCIS = ['0000:06:00.0', '0000:06:00.1']
+
+
+class OvsdpdkTestCase(unittest.TestCase):
+
+ NODES_SAMPLE_SSH = "ovs_sample_ssh_key.yaml"
+ NODES_SAMPLE_PASSWORD = "ovs_sample_password.yaml"
+
+ def setUp(self):
+ self.test_context = ovsdpdk.Ovsdpdk()
+
+ def test_construct(self):
+ self.assertIsNone(self.test_context.name)
+ self.assertIsNone(self.test_context.file_path)
+ self.assertEqual(self.test_context.nodes, [])
+ self.assertEqual(self.test_context.ovs, [])
+ self.assertFalse(self.test_context.vm_deploy)
+ self.assertTrue(self.test_context.first_run)
+ self.assertEqual(self.test_context.user, "")
+ self.assertEqual(self.test_context.ssh_ip, "")
+ self.assertEqual(self.test_context.passwd, "")
+ self.assertEqual(self.test_context.ssh_port, "")
+ self.assertEqual(self.test_context.auth_type, "")
+
+ def test_init(self):
+ self.test_context.parse_pod_and_get_data = mock.Mock()
+ self.test_context.file_path = CORRECT_FILE_PATH
+ self.test_context.init()
+ self.assertIsNone(self.test_context.init())
+
+ def test_successful_init_with_ssh(self):
+ CORRECT_FILE_PATH = self._get_file_abspath(self.NODES_SAMPLE_SSH)
+ self.test_context.parse_pod_and_get_data(CORRECT_FILE_PATH)
+
+ def test_successful_init_with_password(self):
+ CORRECT_FILE_PATH = self._get_file_abspath(self.NODES_SAMPLE_PASSWORD)
+ self.test_context.parse_pod_and_get_data(CORRECT_FILE_PATH)
+
+ def test_unsuccessful_init(self):
+ self.assertRaises(
+ IOError,
+ lambda: self.test_context.parse_pod_and_get_data(WRONG_FILE_PATH))
+
+ def test_ssh_connection(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh.return_value = ssh_mock
+
+ @mock.patch('yardstick.network_services.utils.provision_tool', return_value="b")
+ def test_ssh_connection(self, mock_prov):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(1, "b", ""))
+ ssh.return_value = ssh_mock
+ mock_prov.provision_tool = mock.Mock()
+ ovs_obj = ovsdpdk.Ovsdpdk()
+ ovs_obj.connection = ssh_mock
+ ovs_obj.ovs = OVS_PASSWORD
+ self.assertIsNone(ovs_obj.ssh_remote_machine())
+
+ @mock.patch('yardstick.network_services.utils.provision_tool', return_value="b")
+ def test_ssh_connection_ssh_key(self, mock_prov):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(1, "b", ""))
+ ssh.return_value = ssh_mock
+ mock_prov.provision_tool = mock.Mock()
+ ovs_obj = ovsdpdk.Ovsdpdk()
+ ovs_obj.connection = ssh_mock
+ ovs_obj.ovs = OVS
+ ovs_obj.key_filename = '/root/.ssh/id_rsa'
+ self.assertIsNone(ovs_obj.ssh_remote_machine())
+
+ def test_get_nic_details(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, "eth0 eth1", ""))
+ ssh.return_value = ssh_mock
+ ovs_obj = ovsdpdk.Ovsdpdk()
+ ovs_obj.ovs = OVS
+ ovs_obj.connection = ssh_mock
+ self.assertIsNotNone(ovs_obj.get_nic_details())
+
+ def test_install_req_libs(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh.return_value = ssh_mock
+ ovs_obj = ovsdpdk.Ovsdpdk()
+ ovs_obj.first_run = True
+ ovs_obj.connection = ssh_mock
+ self.assertIsNone(ovs_obj.install_req_libs())
+
+ def test_setup_ovs(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh.return_value = ssh_mock
+ ovs_obj = ovsdpdk.Ovsdpdk()
+ ovs_obj.connection = ssh_mock
+ ovs_obj.ovs = OVS
+ self.assertIsNone(ovs_obj.setup_ovs({"eth0 eth1"}))
+
+ def test_start_ovs_serverswitch(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh.return_value = ssh_mock
+ ovs_obj = ovsdpdk.Ovsdpdk()
+ ovs_obj.connection = ssh_mock
+ ovs_obj.ovs = OVS
+ self.assertIsNone(ovs_obj.start_ovs_serverswitch())
+
+ def test_setup_ovs_bridge(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh.return_value = ssh_mock
+ ovs_obj = ovsdpdk.Ovsdpdk()
+ ovs_obj.connection = ssh_mock
+ ovs_obj.ovs = OVS
+ self.assertIsNone(ovs_obj.setup_ovs_bridge())
+
+ def test_add_oflows(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh.return_value = ssh_mock
+ ovs_obj = ovsdpdk.Ovsdpdk()
+ ovs_obj.connection = ssh_mock
+ ovs_obj.ovs = OVS
+ self.assertIsNone(ovs_obj.add_oflows())
+
+ def test_setup_ovs_context_vm_already_present(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh.return_value = ssh_mock
+ ovs_obj = ovsdpdk.Ovsdpdk()
+ ovs_obj.connection = ssh_mock
+ ovs_obj.ovs = OVS
+ mock_ovs = mock.Mock()
+ ssh_mock.put = mock.Mock()
+ ovs_obj.check_output = mock.Mock(return_value=(0, "vm1"))
+ with mock.patch("yardstick.benchmark.contexts.ovsdpdk.time"):
+ self.assertIsNone(ovs_obj.setup_ovs_context(PCIS, NIC_DETAILS, DRIVER))
+
+ @mock.patch(
+ 'yardstick.benchmark.contexts.ovsdpdk',
+ return_value="Domain vm1 created from /tmp/vm_ovs.xml")
+ def test_is_vm_created(self, NIC_INPUT):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh_mock.put = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh.return_value = ssh_mock
+ mock_ovs = mock.Mock()
+ ret_create = mock.Mock()
+ pcis = NIC_DETAILS['pci']
+ driver = NIC_DETAILS['phy_driver']
+ self.assertIsNotNone(
+ mock_ovs.ovs_obj.setup_ovs_context(
+ pcis,
+ NIC_DETAILS,
+ driver))
+
+ def test_check_output(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ cmd = "command"
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh.return_value = ssh_mock
+ ovs_obj = ovsdpdk.Ovsdpdk()
+ ovs_obj.connection = ssh_mock
+ self.assertIsNotNone(ovs_obj.check_output(cmd, None))
+
+ def test_split_cpu_list_available(self):
+ with mock.patch("itertools.chain") as iter1:
+ iter1 = mock.Mock()
+ print("{0}".format(iter1))
+ ovs_obj = ovsdpdk.Ovsdpdk()
+ self.assertIsNotNone(ovs_obj.split_cpu_list('0,5'))
+
+ def test_split_cpu_list_null(self):
+ with mock.patch("itertools.chain") as iter1:
+ iter1 = mock.Mock()
+ print("{0}".format(iter1))
+ ovs_obj = ovsdpdk.Ovsdpdk()
+ self.assertEqual(ovs_obj.split_cpu_list([]), [])
+
+ def test_destroy_vm_successful(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh.return_value = ssh_mock
+ ovs_obj = ovsdpdk.Ovsdpdk()
+ ovs_obj.connection = ssh_mock
+ ovs_obj.ovs = OVS
+ ovs_obj.check_output = mock.Mock(return_value=(0, "vm1"))
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, "0 i40e"))
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, "0 i40e"))
+ self.assertIsNone(ovs_obj.destroy_vm())
+
+ def test_destroy_vm_unsuccessful(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh.return_value = ssh_mock
+ ovs_obj = ovsdpdk.Ovsdpdk()
+ ovs_obj.connection = ssh_mock
+ ovs_obj.ovs = OVS
+ ovs_obj.check_output = mock.Mock(return_value=(1, {}))
+ self.assertIsNone(ovs_obj.destroy_vm())
+
+ def test_read_from_file(self):
+ CORRECT_FILE_PATH = self._get_file_abspath(self.NODES_SAMPLE_PASSWORD)
+ ovs_obj = ovsdpdk.Ovsdpdk()
+ self.assertIsNotNone(ovs_obj.read_from_file(CORRECT_FILE_PATH))
+
+ def test_write_to_file(self):
+ ovs_obj = ovsdpdk.Ovsdpdk()
+ self.assertIsNone(ovs_obj.write_to_file(SAMPLE_FILE, "some content"))
+
+ def _get_file_abspath(self, filename):
+ curr_path = os.path.dirname(os.path.abspath(__file__))
+ file_path = os.path.join(curr_path, filename)
+ return file_path
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/unit/benchmark/contexts/test_sriov.py b/tests/unit/benchmark/contexts/test_sriov.py
new file mode 100644
index 000000000..a8641a2eb
--- /dev/null
+++ b/tests/unit/benchmark/contexts/test_sriov.py
@@ -0,0 +1,421 @@
+# Copyright (c) 2016-2017 Intel Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import absolute_import
+import os
+import mock
+import unittest
+
+from yardstick.benchmark.contexts import sriov
+
+NIC_INPUT = {
+ 'interface': {},
+ 'vf_macs': ['00:00:00:71:7d:25', '00:00:00:71:7d:26'],
+ 'pci': ['0000:06:00.0', '0000:06:00.1'],
+ 'phy_driver': 'i40e'}
+DRIVER = "i40e"
+NIC_DETAILS = {
+ 'interface': {0: 'enp6s0f0', 1: 'enp6s0f1'},
+ 'vf_macs': ['00:00:00:71:7d:25', '00:00:00:71:7d:26'],
+ 'pci': ['0000:06:00.0', '0000:06:00.1'],
+ 'phy_driver': 'i40e'}
+
+CORRECT_FILE_PATH = "/etc/yardstick/nodes/pod_sriov.yaml"
+WRONG_FILE_PATH = "/etc/yardstick/wrong.yaml"
+SAMPLE_FILE = "sriov_sample_write_to_file.txt"
+
+SRIOV = [{
+ 'auth_type': 'ssh_key',
+ 'name': 'sriov',
+ 'ssh_port': 22,
+ 'ip': '10.10.10.11',
+ 'key_filename': '/root/.ssh/id_rsa',
+ 'vf_macs': ['00:00:00:71:7d:25', '00:00:00:71:7d:26'],
+ 'role': 'Sriov',
+ 'user': 'root',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+
+SRIOV_PASSWORD = [{
+ 'auth_type': 'password',
+ 'name': 'sriov',
+ 'vf_macs': ['00:00:00:71:7d:25', '00:00:00:71:7d:26'],
+ 'ip': '10.10.10.11',
+ 'role': 'Sriov',
+ 'user': 'root',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'password': 'password',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+
+vfnic = "i40evf"
+PCIS = ['0000:06:00.0', '0000:06:00.1']
+
+
+class SriovTestCase(unittest.TestCase):
+
+ NODES_SAMPLE_SSH = "sriov_sample_ssh_key.yaml"
+ NODES_SAMPLE_PASSWORD = "sriov_sample_password.yaml"
+
+ def setUp(self):
+ self.test_context = sriov.Sriov()
+
+ def test_construct(self):
+ self.assertIsNone(self.test_context.name)
+ self.assertIsNone(self.test_context.file_path)
+ self.assertEqual(self.test_context.nodes, [])
+ self.assertEqual(self.test_context.sriov, [])
+ self.assertFalse(self.test_context.vm_deploy)
+ self.assertTrue(self.test_context.first_run)
+ self.assertEqual(self.test_context.user, "")
+ self.assertEqual(self.test_context.ssh_ip, "")
+ self.assertEqual(self.test_context.passwd, "")
+ self.assertEqual(self.test_context.ssh_port, "")
+ self.assertEqual(self.test_context.auth_type, "")
+
+ def test_init(self):
+ self.test_context.parse_pod_and_get_data = mock.Mock()
+ self.test_context.file_path = CORRECT_FILE_PATH
+ self.test_context.init()
+ self.assertIsNone(self.test_context.init())
+
+ def test_successful_init_with_ssh(self):
+ CORRECT_FILE_PATH = self._get_file_abspath(self.NODES_SAMPLE_SSH)
+ self.test_context.parse_pod_and_get_data(CORRECT_FILE_PATH)
+
+ def test_successful_init_with_password(self):
+ CORRECT_FILE_PATH = self._get_file_abspath(self.NODES_SAMPLE_PASSWORD)
+ self.test_context.parse_pod_and_get_data(CORRECT_FILE_PATH)
+
+ def test_unsuccessful_init(self):
+ self.assertRaises(
+ IOError,
+ lambda: self.test_context.parse_pod_and_get_data(WRONG_FILE_PATH))
+
+ @mock.patch('yardstick.network_services.utils.provision_tool', return_value="a")
+ def test_ssh_connection(self, mock_prov):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(1, "a", ""))
+ ssh.return_value = ssh_mock
+ mock_prov.provision_tool = mock.Mock()
+ sriov_obj = sriov.Sriov()
+ sriov_obj.connection = ssh_mock
+ sriov_obj.sriov = SRIOV_PASSWORD
+ self.assertIsNone(sriov_obj.ssh_remote_machine())
+
+ @mock.patch('yardstick.network_services.utils.provision_tool', return_value="a")
+ def test_ssh_connection_ssh_key(self, mock_prov):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(1, "a", ""))
+ ssh.return_value = ssh_mock
+ mock_prov.provision_tool = mock.Mock()
+ sriov_obj = sriov.Sriov()
+ sriov_obj.connection = ssh_mock
+ sriov_obj.sriov = SRIOV
+ sriov_obj.key_filename = '/root/.ssh/id_rsa'
+ self.assertIsNone(sriov_obj.ssh_remote_machine())
+
+ def test_get_nic_details(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, "eth0 eth1", ""))
+ ssh.return_value = ssh_mock
+ sriov_obj = sriov.Sriov()
+ sriov_obj.sriov = SRIOV
+ sriov_obj.connection = ssh_mock
+ self.assertIsNotNone(sriov_obj.get_nic_details())
+
+ def test_install_req_libs(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh.return_value = ssh_mock
+ sriov_obj = sriov.Sriov()
+ sriov_obj.first_run = True
+ sriov_obj.connection = ssh_mock
+ self.assertIsNone(sriov_obj.install_req_libs())
+
+ def test_configure_nics_for_sriov(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ nic_details = {
+ 'interface': {0: 'enp6s0f0', 1: 'enp6s0f1'},
+ 'vf_macs': ['00:00:00:71:7d:25', '00:00:00:71:7d:26'],
+ 'pci': ['0000:06:00.0', '0000:06:00.1'],
+ 'phy_driver': 'i40e',
+ 'vf_pci': [{}, {}]}
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock((DRIVER), return_value=(0, "0 driver", ""))
+ ssh.return_value = ssh_mock
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh.return_value = ssh_mock
+ for i in range(len(NIC_DETAILS['pci'])):
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ sriov_obj = sriov.Sriov()
+ sriov_obj.connection = ssh_mock
+ ssh_mock.execute = \
+ mock.Mock(return_value=(
+ 0,
+ "{'0':'06:02:00','1':'06:06:00'}",
+ ""))
+ sriov_obj.get_vf_datas = mock.Mock(return_value={
+ '0000:06:00.0': '0000:06:02.0'})
+ nic_details['vf_pci'][i] = sriov_obj.get_vf_datas.return_value
+ vf_pci = [[], []]
+ vf_pci[i] = sriov_obj.get_vf_datas.return_value
+ with mock.patch("yardstick.benchmark.contexts.sriov.time"):
+ self.assertIsNotNone(sriov_obj.configure_nics_for_sriov(DRIVER, NIC_DETAILS))
+
+ def test_setup_sriov_context(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ nic_details = {
+ 'interface': {0: 'enp6s0f0', 1: 'enp6s0f1'},
+ 'vf_macs': ['00:00:00:71:7d:25', '00:00:00:71:7d:26'],
+ 'pci': ['0000:06:00.0', '0000:06:00.1'],
+ 'phy_driver': 'i40e',
+ 'vf_pci': [{'vf_pci': '06:02.00'}, {'vf_pci': '06:06.00'}]}
+ vf = [{'vf_pci': '06:02.00'}, {'vf_pci': '06:06.00'}]
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh.return_value = ssh_mock
+ sriov_obj = sriov.Sriov()
+ sriov_obj.connection = ssh_mock
+ sriov_obj.sriov = SRIOV
+ blacklist = "/etc/modprobe.d/blacklist.conf"
+ self.assertEqual(vfnic, "i40evf")
+ mock_sriov = mock.Mock()
+ mock_sriov.sriov_obj.read_from_file(blacklist)
+ sriov_obj.read_from_file = mock.Mock(
+ return_value="some random text")
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ sriov_obj.configure_nics_for_sriov = mock.Mock(
+ return_value=nic_details)
+ nic_details = sriov_obj.configure_nics_for_sriov.return_value
+ self.assertEqual(vf, nic_details['vf_pci'])
+ vf = [
+ {'vf_pci': '06:02.00', 'mac': '00:00:00:00:00:0a'},
+ {'vf_pci': '06:06.00', 'mac': '00:00:00:00:00:0b'}]
+ sriov_obj.add_sriov_interface = mock.Mock()
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh_mock.put = mock.Mock()
+ sriov_obj.check_output = mock.Mock(return_value=(1, {}))
+ with mock.patch("yardstick.benchmark.contexts.sriov.time"):
+ self.assertIsNone(sriov_obj.setup_sriov_context(PCIS, nic_details, DRIVER))
+
+ def test_setup_sriov_context_vm_already_present(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ nic_details = {
+ 'interface': {0: 'enp6s0f0', 1: 'enp6s0f1'},
+ 'vf_macs': ['00:00:00:71:7d:25', '00:00:00:71:7d:26'],
+ 'pci': ['0000:06:00.0', '0000:06:00.1'],
+ 'phy_driver': 'i40e',
+ 'vf_pci': [{'vf_pci': '06:02.00'}, {'vf_pci': '06:06.00'}]}
+ vf = [{'vf_pci': '06:02.00'}, {'vf_pci': '06:06.00'}]
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh.return_value = ssh_mock
+ sriov_obj = sriov.Sriov()
+ sriov_obj.connection = ssh_mock
+ sriov_obj.sriov = SRIOV
+ blacklist = "/etc/modprobe.d/blacklist.conf"
+ self.assertEqual(vfnic, "i40evf")
+ mock_sriov = mock.Mock()
+ mock_sriov.sriov_obj.read_from_file(blacklist)
+ sriov_obj.read_from_file = mock.Mock(
+ return_value="some random text")
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ sriov_obj.configure_nics_for_sriov = mock.Mock(
+ return_value=nic_details)
+ nic_details = sriov_obj.configure_nics_for_sriov.return_value
+ self.assertEqual(vf, nic_details['vf_pci'])
+ vf = [
+ {'vf_pci': '06:02.00', 'mac': '00:00:00:00:00:0a'},
+ {'vf_pci': '06:06.00', 'mac': '00:00:00:00:00:0b'}]
+ sriov_obj.add_sriov_interface = mock.Mock()
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh_mock.put = mock.Mock()
+ sriov_obj.check_output = mock.Mock(return_value=(0, "vm1"))
+ with mock.patch("yardstick.benchmark.contexts.sriov.time"):
+ self.assertIsNone(sriov_obj.setup_sriov_context(PCIS, nic_details, DRIVER))
+
+ @mock.patch(
+ 'yardstick.benchmark.contexts.sriov',
+ return_value="Domain vm1 created from /tmp/vm_sriov.xml")
+ def test_is_vm_created(self, NIC_INPUT):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh.return_value = ssh_mock
+ mock_sriov = mock.Mock()
+ pcis = NIC_DETAILS['pci']
+ driver = NIC_DETAILS['phy_driver']
+ self.assertIsNotNone(
+ mock_sriov.sriov_obj.setup_sriov_context(
+ pcis,
+ NIC_DETAILS,
+ driver))
+
+ def test_add_sriov_interface(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh.return_value = ssh_mock
+ sriov_obj = sriov.Sriov()
+ sriov_obj.connection = ssh_mock
+ with mock.patch("xml.etree.ElementTree.parse") as parse:
+ with mock.patch("re.search") as re:
+ with mock.patch("xml.etree.ElementTree.SubElement") \
+ as elem:
+ parse = mock.Mock(return_value="root")
+ re = mock.Mock()
+ elem = mock.Mock()
+ print("{0} {1} {2}".format(parse, re, elem))
+ self.assertIsNone(sriov_obj.add_sriov_interface(
+ 0,
+ "0000:06:02.0",
+ "00:00:00:00:00:0a",
+ "/tmp/vm_sriov.xml"))
+
+ def test_get_virtual_devices(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh.return_value = ssh_mock
+ sriov_obj = sriov.Sriov()
+ sriov_obj.connection = ssh_mock
+ pci_out = " \
+ PCI_CLASS=20000 \
+ PCI_ID=8086:154C \
+ PCI_SUBSYS_ID=8086:0000 \
+ PCI_SLOT_NAME=0000:06:02.0 \
+ MODALIAS= \
+ pci:v00008086d0000154Csv00008086sd00000000bc02sc00i00"
+ pci = "0000:06:00.0"
+ sriov_obj.check_output = mock.Mock(return_value=(0, pci_out))
+ with mock.patch("re.search") as re:
+ re = mock.Mock(return_value="a")
+ print("{0}".format(re))
+ self.assertIsNotNone(sriov_obj.get_virtual_devices(pci))
+
+ def test_get_vf_datas(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh.return_value = ssh_mock
+ sriov_obj = sriov.Sriov()
+ sriov_obj.connection = ssh_mock
+ sriov_obj.get_virtual_devices = mock.Mock(
+ return_value={'0000:06:00.0': '0000:06:02.0'})
+ with mock.patch("re.search") as re:
+ re = mock.Mock()
+ print("{0}".format(re))
+ self.assertIsNotNone(sriov_obj.get_vf_datas(
+ 'vf_pci',
+ {'0000:06:00.0': '0000:06:02.0'},
+ "00:00:00:00:00:0a"))
+
+ def test_check_output(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ cmd = "command"
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh.return_value = ssh_mock
+ sriov_obj = sriov.Sriov()
+ sriov_obj.connection = ssh_mock
+ self.assertIsNotNone(sriov_obj.check_output(cmd, None))
+
+ def test_split_cpu_list_available(self):
+ with mock.patch("itertools.chain") as iter1:
+ iter1 = mock.Mock()
+ print("{0}".format(iter1))
+ sriov_obj = sriov.Sriov()
+ self.assertIsNotNone(sriov_obj.split_cpu_list('0,5'))
+
+ def test_split_cpu_list_null(self):
+ with mock.patch("itertools.chain") as iter1:
+ iter1 = mock.Mock()
+ print("{0}".format(iter1))
+ sriov_obj = sriov.Sriov()
+ self.assertEqual(sriov_obj.split_cpu_list([]), [])
+
+ def test_destroy_vm_successful(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh.return_value = ssh_mock
+ sriov_obj = sriov.Sriov()
+ sriov_obj.connection = ssh_mock
+ sriov_obj.sriov = SRIOV
+ sriov_obj.check_output = mock.Mock(return_value=(0, "vm1"))
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, "0 i40e"))
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, "0 i40e"))
+ self.assertIsNone(sriov_obj.destroy_vm())
+
+ def test_destroy_vm_unsuccessful(self):
+ with mock.patch("yardstick.ssh.SSH") as ssh:
+ ssh_mock = mock.Mock(autospec=ssh.SSH)
+ ssh_mock.execute = \
+ mock.Mock(return_value=(0, {}, ""))
+ ssh.return_value = ssh_mock
+ sriov_obj = sriov.Sriov()
+ sriov_obj.connection = ssh_mock
+ sriov_obj.sriov = SRIOV
+ sriov_obj.check_output = mock.Mock(return_value=(1, {}))
+ self.assertIsNone(sriov_obj.destroy_vm())
+
+ def test_read_from_file(self):
+ CORRECT_FILE_PATH = self._get_file_abspath(self.NODES_SAMPLE_PASSWORD)
+ sriov_obj = sriov.Sriov()
+ self.assertIsNotNone(sriov_obj.read_from_file(CORRECT_FILE_PATH))
+
+ def test_write_to_file(self):
+ sriov_obj = sriov.Sriov()
+ self.assertIsNone(sriov_obj.write_to_file(SAMPLE_FILE, "some content"))
+
+ def _get_file_abspath(self, filename):
+ curr_path = os.path.dirname(os.path.abspath(__file__))
+ file_path = os.path.join(curr_path, filename)
+ return file_path
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/unit/benchmark/contexts/test_standalone.py b/tests/unit/benchmark/contexts/test_standalone.py
index a6fd776e8..1fc740393 100644
--- a/tests/unit/benchmark/contexts/test_standalone.py
+++ b/tests/unit/benchmark/contexts/test_standalone.py
@@ -20,117 +20,624 @@
from __future__ import absolute_import
import os
import unittest
+import mock
from yardstick.benchmark.contexts import standalone
+from yardstick.benchmark.contexts import sriov
+from yardstick.benchmark.contexts import ovsdpdk
+MOCKS = {
+ 'yardstick.benchmark.contexts': mock.MagicMock(),
+ 'yardstick.benchmark.contexts.sriov': mock.MagicMock(),
+ 'yardstick.benchmark.contexts.ovsdpdk': mock.MagicMock(),
+ 'yardstick.benchmark.contexts.standalone': mock.MagicMock(),
+}
+
+@mock.patch('yardstick.benchmark.contexts.ovsdpdk.time')
+@mock.patch('yardstick.benchmark.contexts.standalone.time')
+@mock.patch('yardstick.benchmark.contexts.sriov.time')
class StandaloneContextTestCase(unittest.TestCase):
+ NODES_SAMPLE = "nodes_sample_new.yaml"
+ NODES_SAMPLE_SRIOV = "nodes_sample_new_sriov.yaml"
+ NODES_DUPLICATE_SAMPLE = "nodes_duplicate_sample_new.yaml"
- NODES_SAMPLE = "standalone_sample.yaml"
- NODES_DUPLICATE_SAMPLE = "standalone_duplicate_sample.yaml"
+ NODES_SAMPLE_OVSDPDK = "nodes_sample_ovs.yaml"
+ NODES_SAMPLE_OVSDPDK_ROLE = "nodes_sample_ovsdpdk.yaml"
+ NODES_DUPLICATE_OVSDPDK = "nodes_duplicate_sample_ovs.yaml"
def setUp(self):
self.test_context = standalone.StandaloneContext()
- def test_construct(self):
-
+ def test_construct(self, mock_sriov_time, mock_standlalone_time, mock_ovsdpdk_time):
self.assertIsNone(self.test_context.name)
self.assertIsNone(self.test_context.file_path)
self.assertEqual(self.test_context.nodes, [])
self.assertEqual(self.test_context.nfvi_node, [])
- def test_unsuccessful_init(self):
-
+ def test_unsuccessful_init(self, mock_sriov_time, mock_standlalone_time, mock_ovsdpdk_time):
attrs = {
'name': 'foo',
'file': self._get_file_abspath("error_file")
}
-
self.assertRaises(IOError, self.test_context.init, attrs)
- def test_successful_init(self):
-
- attrs = {
+ def test_successful_init_sriov(self, mock_sriov_time, mock_standlalone_time,
+ mock_ovsdpdk_time):
+ attrs_sriov = {
+ 'name': 'sriov',
+ 'file': self._get_file_abspath(self.NODES_SAMPLE)
+ }
+ self.test_context.nfvi_node = [{
+ 'name': 'sriov',
+ 'vf_macs': ['00:00:00:71:7d:25', '00:00:00:71:7d:26'],
+ 'ip': '10.223.197.140',
+ 'role': 'Sriov',
+ 'user': 'root',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'password': 'intel123',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+ self.test_context.get_nfvi_obj = mock.Mock()
+ self.test_context.init(attrs_sriov)
+ self.assertEqual(self.test_context.name, "sriov")
+ self.assertEqual(len(self.test_context.nodes), 2)
+ self.assertEqual(len(self.test_context.nfvi_node), 2)
+ self.assertEqual(self.test_context.nfvi_node[0]["name"], "sriov")
+
+ def test_successful_init_ovs(self, mock_sriov_time, mock_standlalone_time, mock_ovsdpdk_time):
+ attrs_ovs = {
+ 'name': 'ovs',
+ 'file': self._get_file_abspath(self.NODES_SAMPLE_OVSDPDK)
+ }
+ self.test_context.nfvi_node = [{
+ 'name': 'ovs',
+ 'vports_mac': ['00:00:00:00:00:03', '00:00:00:00:00:04'],
+ 'ip': '10.223.197.140',
+ 'role': 'Ovsdpdk',
+ 'user': 'root',
+ 'vpath': '/usr/local/',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'password': 'password',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+ self.test_context.get_nfvi_obj = mock.Mock()
+ self.test_context.init(attrs_ovs)
+ self.assertEqual(self.test_context.name, "ovs")
+ self.assertEqual(len(self.test_context.nodes), 2)
+ self.assertEqual(len(self.test_context.nfvi_node), 2)
+ self.assertEqual(self.test_context.nfvi_node[0]["name"], "ovs")
+
+ def test__get_server_with_dic_attr_name_sriov(self, mock_sriov_time, mock_standlalone_time,
+ mock_ovsdpdk_time):
+ attrs_sriov = {
'name': 'foo',
'file': self._get_file_abspath(self.NODES_SAMPLE)
}
+ self.test_context.nfvi_node = [{
+ 'name': 'sriov',
+ 'vf_macs': ['00:00:00:71:7d:25', '00:00:00:71:7d:26'],
+ 'ip': '10.223.197.140',
+ 'role': 'Sriov',
+ 'user': 'root',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'password': 'intel123',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+ self.test_context.init(attrs_sriov)
+ attr_name = {'name': 'foo.bar'}
+ result = self.test_context._get_server(attr_name)
+ self.assertEqual(result, None)
- self.test_context.init(attrs)
-
- self.assertEqual(self.test_context.name, "foo")
- self.assertEqual(len(self.test_context.nodes), 3)
- self.assertEqual(len(self.test_context.nfvi_node), 1)
- self.assertEqual(self.test_context.nfvi_node[0]["name"], "node2")
-
- def test__get_server_with_dic_attr_name(self):
+ def test__get_server_with_dic_attr_name_ovs(self, mock_sriov_time, mock_standlalone_time,
+ mock_ovsdpdk_time):
+ attrs_ovs = {
+ 'name': 'foo',
+ 'file': self._get_file_abspath(self.NODES_SAMPLE_OVSDPDK)
+ }
+ self.test_context.nfvi_node = [{
+ 'name': 'ovs',
+ 'vports_mac': ['00:00:00:00:00:03', '00:00:00:00:00:04'],
+ 'ip': '10.223.197.140',
+ 'role': 'Ovsdpdk',
+ 'user': 'root',
+ 'vpath': '/usr/local/',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'password': 'intel123',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+ self.test_context.init(attrs_ovs)
+ attr_name = {'name': 'foo.bar'}
+ result = self.test_context._get_server(attr_name)
+ self.assertEqual(result, None)
+ def test__get_server_not_found_sriov(self, mock_sriov_time, mock_standlalone_time,
+ mock_ovsdpdk_time):
attrs = {
'name': 'foo',
'file': self._get_file_abspath(self.NODES_SAMPLE)
}
-
+ self.test_context.nfvi_node = [{
+ 'name': 'sriov',
+ 'vf_macs': ['00:00:00:71:7d:25', '00:00:00:71:7d:26'],
+ 'ip': '10.223.197.140',
+ 'role': 'Sriov',
+ 'user': 'root',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'password': 'password',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
self.test_context.init(attrs)
-
- attr_name = {'name': 'foo.bar'}
+ attr_name = 'bar.foo'
result = self.test_context._get_server(attr_name)
-
self.assertEqual(result, None)
- def test__get_server_not_found(self):
-
+ def test__get_server_not_found_ovs(self, mock_sriov_time, mock_standlalone_time,
+ mock_ovsdpdk_time):
attrs = {
'name': 'foo',
- 'file': self._get_file_abspath(self.NODES_SAMPLE)
+ 'file': self._get_file_abspath(self.NODES_SAMPLE_OVSDPDK)
}
-
+ self.test_context.nfvi_node = [{
+ 'name': 'ovs',
+ 'vports_mac': ['00:00:00:00:00:03', '00:00:00:00:00:04'],
+ 'ip': '10.223.197.140',
+ 'role': 'Ovsdpdk',
+ 'user': 'root',
+ 'vpath': '/usr/local/',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'password': 'password',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
self.test_context.init(attrs)
-
attr_name = 'bar.foo'
result = self.test_context._get_server(attr_name)
-
self.assertEqual(result, None)
- def test__get_server_duplicate(self):
+
+ def test__get_server_duplicate_sriov(self, mock_sriov_time, mock_standlalone_time,
+ mock_ovsdpdk_time):
attrs = {
'name': 'foo',
'file': self._get_file_abspath(self.NODES_DUPLICATE_SAMPLE)
}
+ self.test_context.nfvi_node = [{
+ 'name': 'sriov',
+ 'vf_macs': ['00:00:00:71:7d:25', '00:00:00:71:7d:26'],
+ 'ip': '10.223.197.140',
+ 'role': 'Sriov',
+ 'user': 'root',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'password': 'password',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+ self.test_context.get_nfvi_obj = mock.Mock(return_value="sriov")
+ self.test_context.init(attrs)
+ attr_name = 'sriov.foo'
+ # self.test_context.name = "sriov"
+ self.assertRaises(ValueError, self.test_context._get_server, attr_name)
+
+ def test__get_server_duplicate_ovs(self, mock_sriov_time, mock_standlalone_time,
+ mock_ovsdpdk_time):
+ attrs = {
+ 'name': 'foo',
+ 'file': self._get_file_abspath(self.NODES_DUPLICATE_OVSDPDK)
+ }
+ self.test_context.nfvi_node = [{
+ 'name': 'ovs',
+ 'vports_mac': ['00:00:00:00:00:03', '00:00:00:00:00:04'],
+ 'ip': '10.223.197.140',
+ 'role': 'Ovsdpdk',
+ 'user': 'root',
+ 'vpath': '/usr/local/',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'password': 'intel123',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+
+ self.test_context.get_nfvi_obj = mock.Mock(return_value="OvsDpdk")
+ self.test_context.init(attrs)
+ attr_name = 'ovs.foo'
+ self.assertRaises(
+ ValueError,
+ self.test_context._get_server,
+ attr_name)
+ def test__get_server_found_sriov(self, mock_sriov_time, mock_standlalone_time,
+ mock_ovsdpdk_time):
+ attrs = {
+ 'name': 'foo',
+ 'file': self._get_file_abspath(self.NODES_SAMPLE_SRIOV)
+ }
+ self.test_context.nfvi_node = [{
+ 'name': 'sriov',
+ 'vf_macs': ['00:00:00:71:7d:25', '00:00:00:71:7d:26'],
+ 'ip': '10.223.197.140',
+ 'role': 'Sriov',
+ 'user': 'root',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'password': 'intel123',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+
+ self.test_context.get_nfvi_obj = mock.Mock(return_value="OvsDpdk")
self.test_context.init(attrs)
+ attr_name = 'sriov.foo'
+ result = self.test_context._get_server(attr_name)
+ self.assertEqual(result['ip'], '10.123.123.122')
+ self.assertEqual(result['name'], 'sriov.foo')
+ self.assertEqual(result['user'], 'root')
- attr_name = 'node2.foo'
+ def test__get_server_found_ovs(self, mock_sriov_time, mock_standlalone_time,
+ mock_ovsdpdk_time):
+ attrs = {
+ 'name': 'foo',
+ 'file': self._get_file_abspath(self.NODES_SAMPLE_OVSDPDK_ROLE)
+ }
+ self.test_context.nfvi_node = [{
+ 'name': 'ovs',
+ 'vports_mac': ['00:00:00:00:00:03', '00:00:00:00:00:04'],
+ 'ip': '10.223.197.140',
+ 'role': 'Ovsdpdk',
+ 'user': 'root',
+ 'vpath': '/usr/local/',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'password': 'password',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+ self.test_context.get_nfvi_obj = mock.Mock(return_value="OvsDpdk")
+ self.test_context.init(attrs)
+ attr_name = 'ovs.foo'
+ result = self.test_context._get_server(attr_name)
+ self.assertEqual(result['ip'], '10.223.197.222')
+ self.assertEqual(result['name'], 'ovs.foo')
+ self.assertEqual(result['user'], 'root')
- self.assertRaises(ValueError, self.test_context._get_server, attr_name)
+ def test__deploy_unsuccessful(self, mock_sriov_time, mock_standlalone_time, mock_ovsdpdk_time):
+ self.test_context.vm_deploy = False
- def test__get_server_found(self):
+ def test__deploy_sriov_firsttime(self, mock_sriov_time, mock_standlalone_time,
+ mock_ovsdpdk_time):
+ attrs = {
+ 'name': 'foo',
+ 'file': self._get_file_abspath(self.NODES_SAMPLE)
+ }
+ self.test_context.nfvi_node = [{
+ 'name': 'sriov',
+ 'vf_macs': ['00:00:00:71:7d:25', '00:00:00:71:7d:26'],
+ 'ip': '10.223.197.140',
+ 'role': 'Sriov',
+ 'user': 'root',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'password': 'intel123',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+
+ MYSRIOV = [{
+ 'name': 'sriov',
+ 'vf_macs': ['00:00:00:71:7d:25', '00:00:00:71:7d:26'],
+ 'ip': '10.223.197.140',
+ 'role': 'Sriov',
+ 'user': 'root',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'password': 'intel123',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+
+ self.test_context.vm_deploy = True
+
+ self.test_context.get_nfvi_obj = mock.MagicMock()
+ self.test_context.init(attrs)
+ self.test_context.nfvi_obj.sriov = MYSRIOV
+ self.test_context.nfvi_obj.ssh_remote_machine = mock.Mock()
+ self.test_context.nfvi_obj.first_run = True
+ self.test_context.nfvi_obj.install_req_libs()
+ self.test_context.nfvi_obj.get_nic_details = mock.Mock()
+ PORTS = ['0000:06:00.0', '0000:06:00.1']
+ NIC_DETAILS = {
+ 'interface': {0: 'enp6s0f0', 1: 'enp6s0f1'},
+ 'vf_macs': ['00:00:00:71:7d:25', '00:00:00:71:7d:26'],
+ 'pci': ['0000:06:00.0', '0000:06:00.1'],
+ 'phy_driver': 'i40e'}
+ DRIVER = 'i40e'
+ result = self.test_context.nfvi_obj.setup_sriov_context(
+ PORTS,
+ NIC_DETAILS,
+ DRIVER)
+ print("{0}".format(result))
+ self.assertIsNone(self.test_context.deploy())
+ def test__deploy_sriov_notfirsttime(self, mock_sriov_time, mock_standlalone_time,
+ mock_ovsdpdk_time):
attrs = {
'name': 'foo',
'file': self._get_file_abspath(self.NODES_SAMPLE)
}
+ self.test_context.nfvi_node = [{
+ 'name': 'sriov',
+ 'vf_macs': ['00:00:00:71:7d:25', '00:00:00:71:7d:26'],
+ 'ip': '10.223.197.140',
+ 'role': 'Sriov',
+ 'user': 'root',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'password': 'intel123',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+ MYSRIOV = [{
+ 'name': 'sriov',
+ 'vf_macs': ['00:00:00:71:7d:25', '00:00:00:71:7d:26'],
+ 'ip': '10.223.197.140',
+ 'role': 'Sriov',
+ 'user': 'root',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'password': 'intel123',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+
+ self.test_context.vm_deploy = True
+ self.test_context.get_nfvi_obj = mock.MagicMock()
self.test_context.init(attrs)
+ self.test_context.nfvi_obj.sriov = MYSRIOV
+ self.test_context.nfvi_obj.ssh_remote_machine = mock.Mock()
+ self.test_context.nfvi_obj.first_run = False
+ self.test_context.nfvi_obj.get_nic_details = mock.Mock()
+ PORTS = ['0000:06:00.0', '0000:06:00.1']
+ NIC_DETAILS = {
+ 'interface': {0: 'enp6s0f0', 1: 'enp6s0f1'},
+ 'vf_macs': ['00:00:00:71:7d:25', '00:00:00:71:7d:26'],
+ 'pci': ['0000:06:00.0', '0000:06:00.1'],
+ 'phy_driver': 'i40e'}
+ DRIVER = 'i40e'
+ result = self.test_context.nfvi_obj.setup_sriov_context(
+ PORTS,
+ NIC_DETAILS,
+ DRIVER)
+ print("{0}".format(result))
+ self.assertIsNone(self.test_context.deploy())
- attr_name = 'node1.foo'
- result = self.test_context._get_server(attr_name)
+ def test__deploy_ovs_firsttime(self, mock_sriov_time, mock_standlalone_time,
+ mock_ovsdpdk_time):
+ attrs = {
+ 'name': 'foo',
+ 'file': self._get_file_abspath(self.NODES_SAMPLE_OVSDPDK)
+ }
- self.assertEqual(result['ip'], '1.1.1.1')
- self.assertEqual(result['name'], 'node1.foo')
- self.assertEqual(result['user'], 'root')
+ self.test_context.nfvi_node = [{
+ 'name': 'ovs',
+ 'vports_mac': ['00:00:00:00:00:03', '00:00:00:00:00:04'],
+ 'ip': '10.223.197.140',
+ 'role': 'Ovsdpdk',
+ 'user': 'root',
+ 'vpath': '/usr/local/',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'password': 'password',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+
+ MYOVS = [{
+ 'name': 'ovs',
+ 'vports_mac': ['00:00:00:00:00:03', '00:00:00:00:00:04'],
+ 'ip': '10.223.197.140',
+ 'role': 'Ovsdpdk',
+ 'user': 'root',
+ 'vpath': '/usr/local/',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'password': 'password',
+ 'flow': ['ovs-ofctl add-flow br0 in_port=1,action=output:3',
+ 'ovs-ofctl add-flow br0 in_port=3,action=output:1'
+ 'ovs-ofctl add-flow br0 in_port=4,action=output:2'
+ 'ovs-ofctl add-flow br0 in_port=2,action=output:4'],
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+
+ self.test_context.vm_deploy = True
+ self.test_context.get_nfvi_obj = mock.MagicMock()
+ self.test_context.init(attrs)
+ self.test_context.ovs = MYOVS
+ self.test_context.nfvi_obj.ssh_remote_machine = mock.Mock()
+ self.test_context.nfvi_obj.first_run = True
+ self.test_context.nfvi_obj.install_req_libs()
+ self.test_context.nfvi_obj.get_nic_details = mock.Mock()
+ PORTS = ['0000:06:00.0', '0000:06:00.1']
+ NIC_DETAILS = {
+ 'interface': {0: 'enp6s0f0', 1: 'enp6s0f1'},
+ 'vports_mac': ['00:00:00:00:00:05', '00:00:00:00:00:06'],
+ 'pci': ['0000:06:00.0', '0000:06:00.1'],
+ 'phy_driver': 'i40e'}
+ DRIVER = 'i40e'
+
+ self.test_context.nfvi_obj.setup_ovs = mock.Mock()
+ self.test_context.nfvi_obj.start_ovs_serverswitch = mock.Mock()
+ self.test_context.nfvi_obj.setup_ovs_bridge = mock.Mock()
+ self.test_context.nfvi_obj.add_oflows = mock.Mock()
+
+ # self.test_context.nfvi_obj.setup_ovs(PORTS)
+ # self.test_context.nfvi_obj.start_ovs_serverswitch()
+ # self.test_context.nfvi_obj.setup_ovs_bridge()
+ # self.test_context.nfvi_obj.add_oflows()
+
+ result = self.test_context.nfvi_obj.setup_ovs_context(
+ PORTS,
+ NIC_DETAILS,
+ DRIVER)
+ print("{0}".format(result))
+ self.assertIsNone(self.test_context.deploy())
- def test_deploy(self):
+ def test__deploy_ovs_notfirsttime(self, mock_sriov_time, mock_standlalone_time,
+ mock_ovsdpdk_time):
+ attrs = {
+ 'name': 'foo',
+ 'file': self._get_file_abspath(self.NODES_SAMPLE_OVSDPDK)
+ }
+ self.test_context.nfvi_node = [{
+ 'name': 'ovs',
+ 'vports_mac': ['00:00:00:00:00:03', '00:00:00:00:00:04'],
+ 'ip': '10.223.197.140',
+ 'role': 'Ovsdpdk',
+ 'user': 'root',
+ 'vpath': '/usr/local/',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'password': 'password',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+
+ MYOVS = [{
+ 'name': 'ovs',
+ 'vports_mac': ['00:00:00:00:00:03', '00:00:00:00:00:04'],
+ 'ip': '10.223.197.140',
+ 'role': 'Ovsdpdk',
+ 'user': 'root',
+ 'vpath': '/usr/local/',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'password': 'password',
+ 'flow': ['ovs-ofctl add-flow br0 in_port=1,action=output:3',
+ 'ovs-ofctl add-flow br0 in_port=3,action=output:1'
+ 'ovs-ofctl add-flow br0 in_port=4,action=output:2'
+ 'ovs-ofctl add-flow br0 in_port=2,action=output:4'],
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+
+ self.test_context.vm_deploy = True
+ self.test_context.get_nfvi_obj = mock.MagicMock()
+ self.test_context.init(attrs)
+ self.test_context.ovs = MYOVS
+ self.test_context.nfvi_obj.ssh_remote_machine = mock.Mock()
+ self.test_context.nfvi_obj.first_run = False
+ self.test_context.nfvi_obj.get_nic_details = mock.Mock()
+ PORTS = ['0000:06:00.0', '0000:06:00.1']
+ NIC_DETAILS = {
+ 'interface': {0: 'enp6s0f0', 1: 'enp6s0f1'},
+ 'vports_mac': ['00:00:00:00:00:05', '00:00:00:00:00:06'],
+ 'pci': ['0000:06:00.0', '0000:06:00.1'],
+ 'phy_driver': 'i40e'}
+ DRIVER = 'i40e'
+
+ self.test_context.nfvi_obj.setup_ovs(PORTS)
+ self.test_context.nfvi_obj.start_ovs_serverswitch()
+ self.test_context.nfvi_obj.setup_ovs_bridge()
+ self.test_context.nfvi_obj.add_oflows()
+
+ result = self.test_context.nfvi_obj.setup_ovs_context(
+ PORTS,
+ NIC_DETAILS,
+ DRIVER)
+ print("{0}".format(result))
self.assertIsNone(self.test_context.deploy())
- def test_undeploy(self):
+ def test_undeploy_sriov(self, mock_sriov_time, mock_standlalone_time, mock_ovsdpdk_time):
+ attrs = {
+ 'name': 'foo',
+ 'file': self._get_file_abspath(self.NODES_SAMPLE)
+ }
+ self.test_context.nfvi_node = [{
+ 'name': 'sriov',
+ 'vf_macs': ['00:00:00:71:7d:25', '00:00:00:71:7d:26'],
+ 'ip': '10.223.197.140',
+ 'role': 'Sriov',
+ 'user': 'root',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'password': 'intel123',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+ self.test_context.get_nfvi_obj = mock.MagicMock()
+ self.test_context.init(attrs)
+ self.test_context.nfvi_obj.destroy_vm = mock.Mock()
+ self.assertIsNone(self.test_context.undeploy())
+
+ def test_undeploy_ovs(self, mock_sriov_time, mock_standlalone_time, mock_ovsdpdk_time):
+ attrs = {
+ 'name': 'foo',
+ 'file': self._get_file_abspath(self.NODES_SAMPLE_OVSDPDK)
+ }
+
+ self.test_context.nfvi_node = [{
+ 'name': 'ovs',
+ 'vports_mac': ['00:00:00:00:00:03', '00:00:00:00:00:04'],
+ 'ip': '10.223.197.140',
+ 'role': 'Ovsdpdk',
+ 'user': 'root',
+ 'vpath': '/usr/local/',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'password': 'password',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+
+ self.test_context.get_nfvi_obj = mock.MagicMock()
+ self.test_context.init(attrs)
+ self.test_context.nfvi_obj.destroy_vm = mock.Mock()
self.assertIsNone(self.test_context.undeploy())
+ def test_get_nfvi_obj_sriov(self, mock_sriov_time, mock_standlalone_time, mock_ovsdpdk_time):
+ with mock.patch('yardstick.benchmark.contexts.sriov'):
+ attrs = {
+ 'name': 'sriov',
+ 'file': self._get_file_abspath(self.NODES_SAMPLE)
+ }
+ self.test_context.init(attrs)
+ self.test_context.nfvi_obj.file_path = self._get_file_abspath(
+ self.NODES_SAMPLE)
+ self.test_context.nfvi_node = [{
+ 'name': 'sriov',
+ 'vf_macs': ['00:00:00:71:7d:25', '00:00:00:71:7d:26'],
+ 'ip': '10.223.197.140',
+ 'role': 'Sriov',
+ 'user': 'root',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'password': 'intel123',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+ self.test_context.get_nfvi_obj = mock.MagicMock()
+ self.test_context.init(attrs)
+ self.test_context.get_context_impl = mock.Mock(
+ return_value=sriov.Sriov)
+ self.assertIsNotNone(self.test_context.get_nfvi_obj())
+
+ def test_get_nfvi_obj_ovs(self, mock_sriov_time, mock_standlalone_time, mock_ovsdpdk_time):
+ with mock.patch('yardstick.benchmark.contexts.ovsdpdk'):
+ attrs = {
+ 'name': 'ovs',
+ 'file': self._get_file_abspath(self.NODES_SAMPLE_OVSDPDK)
+ }
+ self.test_context.init(attrs)
+ self.test_context.nfvi_obj.file_path = self._get_file_abspath(
+ self.NODES_SAMPLE)
+ self.test_context.nfvi_node = [{
+ 'name': 'ovs',
+ 'vports_mac': ['00:00:00:00:00:03', '00:00:00:00:00:04'],
+ 'ip': '10.223.197.140',
+ 'role': 'Ovsdpdk',
+ 'user': 'root',
+ 'vpath': '/usr/local/',
+ 'images': '/var/lib/libvirt/images/ubuntu1.img',
+ 'phy_driver': 'i40e',
+ 'password': 'password',
+ 'phy_ports': ['0000:06:00.0', '0000:06:00.1']}]
+ self.test_context.get_nfvi_obj = mock.MagicMock()
+ self.test_context.init(attrs)
+ self.test_context.get_context_impl = mock.Mock(
+ return_value=ovsdpdk.Ovsdpdk)
+ self.assertIsNotNone(self.test_context.get_nfvi_obj())
+
+ def test_get_context_impl_correct_obj(self, mock_sriov_time, mock_standlalone_time,
+ mock_ovsdpdk_time):
+ with mock.patch.dict("sys.modules", MOCKS):
+ self.assertIsNotNone(self.test_context.get_context_impl('Sriov'))
+
+ def test_get_context_impl_wrong_obj(self, mock_sriov_time, mock_standlalone_time,
+ mock_ovsdpdk_time):
+ with mock.patch.dict("sys.modules", MOCKS):
+ self.assertRaises(
+ ValueError,
+ lambda: self.test_context.get_context_impl('wrong_object'))
+
def _get_file_abspath(self, filename):
curr_path = os.path.dirname(os.path.abspath(__file__))
file_path = os.path.join(curr_path, filename)
return file_path
- def test__get_network(self):
+ def test__get_network(self, mock_sriov_time, mock_standlalone_time, mock_ovsdpdk_time):
network1 = {
'name': 'net_1',
'vld_id': 'vld111',
@@ -174,3 +681,6 @@ class StandaloneContextTestCase(unittest.TestCase):
expected = network1
result = self.test_context._get_network(attr_name)
self.assertDictEqual(result, expected)
+if __name__ == '__main__':
+ unittest.main()
+
diff --git a/tests/unit/benchmark/core/test_task.py b/tests/unit/benchmark/core/test_task.py
index 8d6d963c3..7f617537e 100644
--- a/tests/unit/benchmark/core/test_task.py
+++ b/tests/unit/benchmark/core/test_task.py
@@ -47,6 +47,20 @@ class TaskTestCase(unittest.TestCase):
self.assertEqual(context_cfg["host"], server_info)
self.assertEqual(context_cfg["target"], server_info)
+ def test_set_dispatchers(self):
+ t = task.Task()
+ output_config = {"DEFAULT": {"dispatcher": "file, http"}}
+ t._set_dispatchers(output_config)
+ self.assertEqual(output_config, output_config)
+
+ @mock.patch('yardstick.benchmark.core.task.DispatcherBase')
+ def test__do_output(self, mock_dispatcher):
+ t = task.Task()
+ output_config = {"DEFAULT": {"dispatcher": "file, http"}}
+ mock_dispatcher.get = mock.MagicMock(return_value=[mock.MagicMock(),
+ mock.MagicMock()])
+ self.assertEqual(None, t._do_output(output_config, {}))
+
@mock.patch('yardstick.benchmark.core.task.Context')
def test_parse_networks_from_nodes(self, mock_context):
nodes = {
diff --git a/tests/unit/benchmark/scenarios/availability/test_util.py b/tests/unit/benchmark/scenarios/availability/test_util.py
index bb0e6bc79..2e4fff417 100644
--- a/tests/unit/benchmark/scenarios/availability/test_util.py
+++ b/tests/unit/benchmark/scenarios/availability/test_util.py
@@ -19,6 +19,25 @@ from yardstick.benchmark.scenarios.availability import util
@mock.patch('yardstick.benchmark.scenarios.availability.util.subprocess')
class ExecuteShellTestCase(unittest.TestCase):
+ def setUp(self):
+ self.param_config = {'serviceName': '$serviceName', 'value': 1}
+ self.intermediate_variables = {'$serviceName': 'nova-api'}
+ self.std_output = '| id | 1 |'
+ self.cmd_config = {'cmd':'ls','param':'-a'}
+
+ def test_util_build_command_shell(self,mock_subprocess):
+ result = util.build_shell_command(self.param_config, True,
+ self.intermediate_variables)
+ self.assertEqual("nova-api" in result, True)
+
+ def test_read_stdout_item(self,mock_subprocess):
+ result = util.read_stdout_item(self.std_output,'id')
+ self.assertEquals('1',result)
+
+ def test_buildshellparams(self,mock_subprocess):
+ result = util.buildshellparams(self.cmd_config,True)
+ self.assertEquals('/bin/bash -s {0} {1}', result)
+
def test__fun_execute_shell_command_successful(self, mock_subprocess):
cmd = "env"
mock_subprocess.check_output.return_value = (0, 'unittest')
diff --git a/tests/unit/benchmark/scenarios/networking/test_pktgen.py b/tests/unit/benchmark/scenarios/networking/test_pktgen.py
index d4eb1246f..2914c8e02 100644
--- a/tests/unit/benchmark/scenarios/networking/test_pktgen.py
+++ b/tests/unit/benchmark/scenarios/networking/test_pktgen.py
@@ -138,6 +138,7 @@ class PktgenTestCase(unittest.TestCase):
p.run(result)
expected_result = jsonutils.loads(sample_output)
expected_result["packets_received"] = 149300
+ expected_result["packetsize"] = 60
self.assertEqual(result, expected_result)
def test_pktgen_successful_sla(self, mock_ssh):
@@ -164,6 +165,7 @@ class PktgenTestCase(unittest.TestCase):
p.run(result)
expected_result = jsonutils.loads(sample_output)
expected_result["packets_received"] = 149300
+ expected_result["packetsize"] = 60
self.assertEqual(result, expected_result)
def test_pktgen_unsuccessful_sla(self, mock_ssh):
@@ -204,6 +206,538 @@ class PktgenTestCase(unittest.TestCase):
mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR')
self.assertRaises(RuntimeError, p.run, result)
+ def test_pktgen_get_vnic_driver_name(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (0, 'ixgbevf', '')
+
+ vnic_driver_name = p._get_vnic_driver_name()
+ self.assertEqual(vnic_driver_name, 'ixgbevf')
+
+ def test_pktgen_unsuccessful_get_vnic_driver_name(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (1, '', '')
+
+ self.assertRaises(RuntimeError, p._get_vnic_driver_name)
+
+ def test_pktgen_get_sriov_queue_number(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (0, '2', '')
+
+ p.queue_number = p._get_sriov_queue_number()
+ self.assertEqual(p.queue_number, 2)
+
+ def test_pktgen_unsuccessful_get_sriov_queue_number(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (1, '', '')
+
+ self.assertRaises(RuntimeError, p._get_sriov_queue_number)
+
+ def test_pktgen_get_available_queue_number(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (0, '4', '')
+
+ p._get_available_queue_number()
+
+ mock_ssh.SSH.from_node().execute.assert_called_with(
+ "sudo ethtool -l eth0 | grep Combined | head -1 |" \
+ "awk '{printf $2}'")
+
+ def test_pktgen_unsuccessful_get_available_queue_number(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (1, '', '')
+
+ self.assertRaises(RuntimeError, p._get_available_queue_number)
+
+ def test_pktgen_get_usable_queue_number(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (0, '1', '')
+
+ p._get_usable_queue_number()
+
+ mock_ssh.SSH.from_node().execute.assert_called_with(
+ "sudo ethtool -l eth0 | grep Combined | tail -1 |" \
+ "awk '{printf $2}'")
+
+ def test_pktgen_unsuccessful_get_usable_queue_number(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (1, '', '')
+
+ self.assertRaises(RuntimeError, p._get_usable_queue_number)
+
+ def test_pktgen_enable_ovs_multiqueue(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+ p.client = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (0, '4', '')
+
+ mock_result1 = mock.Mock()
+ mock_result1.return_value = 1
+ p._get_usable_queue_number = mock_result1
+
+ mock_result2 = mock.Mock()
+ mock_result2.return_value = 4
+ p._get_available_queue_number = mock_result2
+
+ p.queue_number = p._enable_ovs_multiqueue()
+ self.assertEqual(p.queue_number, 4)
+
+ def test_pktgen_enable_ovs_multiqueue_1q(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+ p.client = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (0, '1', '')
+
+ mock_result1 = mock.Mock()
+ mock_result1.return_value = 1
+ p._get_usable_queue_number = mock_result1
+
+ mock_result2 = mock.Mock()
+ mock_result2.return_value = 1
+ p._get_available_queue_number = mock_result2
+
+ p.queue_number = p._enable_ovs_multiqueue()
+ self.assertEqual(p.queue_number, 1)
+
+ def test_pktgen_unsuccessful_enable_ovs_multiqueue(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+ p.client = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (1, '', '')
+
+ mock_result1 = mock.Mock()
+ mock_result1.return_value = 1
+ p._get_usable_queue_number = mock_result1
+
+ mock_result2 = mock.Mock()
+ mock_result2.return_value = 4
+ p._get_available_queue_number = mock_result2
+
+ self.assertRaises(RuntimeError, p._enable_ovs_multiqueue)
+
+ def test_pktgen_setup_irqmapping_ovs(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+ p.client = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (0, '10', '')
+
+ p._setup_irqmapping_ovs(4)
+
+ mock_ssh.SSH.from_node().execute.assert_called_with(
+ "echo 8 | sudo tee /proc/irq/10/smp_affinity")
+
+ def test_pktgen_setup_irqmapping_ovs_1q(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+ p.client = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (0, '10', '')
+
+ p._setup_irqmapping_ovs(1)
+
+ mock_ssh.SSH.from_node().execute.assert_called_with(
+ "echo 1 | sudo tee /proc/irq/10/smp_affinity")
+
+ def test_pktgen_unsuccessful_setup_irqmapping_ovs(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+ p.client = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (1, '', '')
+
+ self.assertRaises(RuntimeError, p._setup_irqmapping_ovs, 4)
+
+ def test_pktgen_unsuccessful_setup_irqmapping_ovs_1q(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+ p.client = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (1, '', '')
+
+ self.assertRaises(RuntimeError, p._setup_irqmapping_ovs, 1)
+
+ def test_pktgen_setup_irqmapping_sriov(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+ p.client = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (0, '10', '')
+
+ p._setup_irqmapping_sriov(2)
+
+ mock_ssh.SSH.from_node().execute.assert_called_with(
+ "echo 2 | sudo tee /proc/irq/10/smp_affinity")
+
+ def test_pktgen_setup_irqmapping_sriov_1q(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+ p.client = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (0, '10', '')
+
+ p._setup_irqmapping_sriov(1)
+
+ mock_ssh.SSH.from_node().execute.assert_called_with(
+ "echo 1 | sudo tee /proc/irq/10/smp_affinity")
+
+ def test_pktgen_unsuccessful_setup_irqmapping_sriov(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+ p.client = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (1, '', '')
+
+ self.assertRaises(RuntimeError, p._setup_irqmapping_sriov, 2)
+
+ def test_pktgen_unsuccessful_setup_irqmapping_sriov_1q(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+ p.client = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (1, '', '')
+
+ self.assertRaises(RuntimeError, p._setup_irqmapping_sriov, 1)
+
+ def test_pktgen_is_irqbalance_disabled(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (0, '', '')
+
+ p._is_irqbalance_disabled()
+
+ mock_ssh.SSH.from_node().execute.assert_called_with(
+ "grep ENABLED /etc/default/irqbalance")
+
+ def test_pktgen_unsuccessful_is_irqbalance_disabled(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (1, '', '')
+
+ self.assertRaises(RuntimeError, p._is_irqbalance_disabled)
+
+ def test_pktgen_disable_irqbalance(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+ p.client = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (0, '', '')
+
+ p._disable_irqbalance()
+
+ mock_ssh.SSH.from_node().execute.assert_called_with(
+ "sudo service irqbalance disable")
+
+ def test_pktgen_unsuccessful_disable_irqbalance(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+ p.client = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (1, '', '')
+
+ self.assertRaises(RuntimeError, p._disable_irqbalance)
+
+ def test_pktgen_multiqueue_setup_ovs(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60, 'multiqueue': True},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+ p.client = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (0, '4', '')
+
+ mock_result1 = mock.Mock()
+ mock_result1.return_value = False
+ p._is_irqbalance_disabled = mock_result1
+
+ mock_result2 = mock.Mock()
+ mock_result2.return_value = "virtio_net"
+ p._get_vnic_driver_name = mock_result2
+
+ mock_result3 = mock.Mock()
+ mock_result3.return_value = 1
+ p._get_usable_queue_number = mock_result3
+
+ mock_result4 = mock.Mock()
+ mock_result4.return_value = 4
+ p._get_available_queue_number = mock_result4
+
+ p.multiqueue_setup()
+
+ self.assertEqual(p.queue_number, 4)
+
+ def test_pktgen_multiqueue_setup_ovs_1q(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60, 'multiqueue': True},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+ p.client = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (0, '1', '')
+
+ mock_result1 = mock.Mock()
+ mock_result1.return_value = False
+ p._is_irqbalance_disabled = mock_result1
+
+ mock_result2 = mock.Mock()
+ mock_result2.return_value = "virtio_net"
+ p._get_vnic_driver_name = mock_result2
+
+ mock_result3 = mock.Mock()
+ mock_result3.return_value = 1
+ p._get_usable_queue_number = mock_result3
+
+ mock_result4 = mock.Mock()
+ mock_result4.return_value = 1
+ p._get_available_queue_number = mock_result4
+
+ p.multiqueue_setup()
+
+ self.assertEqual(p.queue_number, 1)
+
+ def test_pktgen_multiqueue_setup_sriov(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60, 'multiqueue': True},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+ p.client = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (0, '2', '')
+
+ mock_result1 = mock.Mock()
+ mock_result1.return_value = False
+ p._is_irqbalance_disabled = mock_result1
+
+ mock_result2 = mock.Mock()
+ mock_result2.return_value = "ixgbevf"
+ p._get_vnic_driver_name = mock_result2
+
+ p.multiqueue_setup()
+
+ self.assertEqual(p.queue_number, 2)
+
+ def test_pktgen_multiqueue_setup_sriov_1q(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60, 'multiqueue': True},
+ }
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+ p.client = mock_ssh.SSH.from_node()
+
+ mock_ssh.SSH.from_node().execute.return_value = (0, '1', '')
+
+ mock_result1 = mock.Mock()
+ mock_result1.return_value = False
+ p._is_irqbalance_disabled = mock_result1
+
+ mock_result2 = mock.Mock()
+ mock_result2.return_value = "ixgbevf"
+ p._get_vnic_driver_name = mock_result2
+
+ p.multiqueue_setup()
+
+ self.assertEqual(p.queue_number, 1)
+
+ def test_pktgen_run_with_setup_done(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60, 'number_of_ports': 10, 'duration': 20, 'multiqueue': True},
+ 'sla': {'max_ppm': 1}
+ }
+ result = {}
+ p = pktgen.Pktgen(args, self.ctx)
+ p.server = mock_ssh.SSH.from_node()
+ p.client = mock_ssh.SSH.from_node()
+
+ p.setup_done = True
+ p.multiqueue_setup_done = True
+
+ mock_iptables_result = mock.Mock()
+ mock_iptables_result.return_value = 149300
+ p._iptables_get_result = mock_iptables_result
+
+ sample_output = '{"packets_per_second": 9753, "errors": 0, \
+ "packets_sent": 149300, "flows": 110}'
+ mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '')
+
+ p.run(result)
+ expected_result = jsonutils.loads(sample_output)
+ expected_result["packets_received"] = 149300
+ expected_result["packetsize"] = 60
+ self.assertEqual(result, expected_result)
+
+ def test_pktgen_run_with_ovs_multiqueque(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60, 'number_of_ports': 10, 'duration': 20, 'multiqueue': True},
+ 'sla': {'max_ppm': 1}
+ }
+ result = {}
+
+ p = pktgen.Pktgen(args, self.ctx)
+
+ p.server = mock_ssh.SSH.from_node()
+ p.client = mock_ssh.SSH.from_node()
+
+ mock_result = mock.Mock()
+ mock_result.return_value = "virtio_net"
+ p._get_vnic_driver_name = mock_result
+
+ mock_result1 = mock.Mock()
+ mock_result1.return_value = 1
+ p._get_usable_queue_number = mock_result1
+
+ mock_result2 = mock.Mock()
+ mock_result2.return_value = 4
+ p._get_available_queue_number = mock_result2
+
+ mock_result3 = mock.Mock()
+ mock_result3.return_value = 4
+ p._enable_ovs_multiqueue = mock_result3
+
+ mock_result4 = mock.Mock()
+ p._setup_irqmapping_ovs = mock_result4
+
+ mock_iptables_result = mock.Mock()
+ mock_iptables_result.return_value = 149300
+ p._iptables_get_result = mock_iptables_result
+
+ sample_output = '{"packets_per_second": 9753, "errors": 0, \
+ "packets_sent": 149300, "flows": 110}'
+ mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '')
+
+ p.run(result)
+ expected_result = jsonutils.loads(sample_output)
+ expected_result["packets_received"] = 149300
+ expected_result["packetsize"] = 60
+ self.assertEqual(result, expected_result)
+
+ def test_pktgen_run_with_sriov_multiqueque(self, mock_ssh):
+ args = {
+ 'options': {'packetsize': 60, 'number_of_ports': 10, 'duration': 20, 'multiqueue': True},
+ 'sla': {'max_ppm': 1}
+ }
+ result = {}
+
+ p = pktgen.Pktgen(args, self.ctx)
+
+ p.server = mock_ssh.SSH.from_node()
+ p.client = mock_ssh.SSH.from_node()
+
+ mock_result1 = mock.Mock()
+ mock_result1.return_value = "ixgbevf"
+ p._get_vnic_driver_name = mock_result1
+
+ mock_result2 = mock.Mock()
+ mock_result2.return_value = 2
+ p._get_sriov_queue_number = mock_result2
+
+ mock_result3 = mock.Mock()
+ p._setup_irqmapping_sriov = mock_result3
+
+ mock_iptables_result = mock.Mock()
+ mock_iptables_result.return_value = 149300
+ p._iptables_get_result = mock_iptables_result
+
+ sample_output = '{"packets_per_second": 9753, "errors": 0, \
+ "packets_sent": 149300, "flows": 110}'
+ mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '')
+
+ p.run(result)
+ expected_result = jsonutils.loads(sample_output)
+ expected_result["packets_received"] = 149300
+ expected_result["packetsize"] = 60
+ self.assertEqual(result, expected_result)
def main():
unittest.main()
diff --git a/yardstick/benchmark/contexts/model.py b/yardstick/benchmark/contexts/model.py
index 6601ecf3b..ec474321f 100644
--- a/yardstick/benchmark/contexts/model.py
+++ b/yardstick/benchmark/contexts/model.py
@@ -110,7 +110,7 @@ class Network(Object):
self.provider = attrs.get('provider')
self.segmentation_id = attrs.get('segmentation_id')
self.network_type = attrs.get('network_type')
- self.port_security_enabled = attrs.get('port_security_enabled', True)
+ self.port_security_enabled = attrs.get('port_security_enabled')
self.allowed_address_pairs = attrs.get('allowed_address_pairs', [])
try:
# we require 'null' or '' to disable setting gateway_ip
diff --git a/yardstick/benchmark/contexts/ovsdpdk.py b/yardstick/benchmark/contexts/ovsdpdk.py
new file mode 100644
index 000000000..cf5529d89
--- /dev/null
+++ b/yardstick/benchmark/contexts/ovsdpdk.py
@@ -0,0 +1,369 @@
+# Copyright (c) 2016-2017 Intel Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import absolute_import
+import os
+import yaml
+import time
+import glob
+import itertools
+import logging
+from yardstick import ssh
+from yardstick.benchmark.contexts.standalone import StandaloneContext
+
+BIN_PATH = "/opt/isb_bin/"
+DPDK_NIC_BIND = "dpdk_nic_bind.py"
+
+log = logging.getLogger(__name__)
+
+VM_TEMPLATE = """
+<domain type='kvm'>
+ <name>vm1</name>
+ <uuid>18230c0c-635d-4c50-b2dc-a213d30acb34</uuid>
+ <memory unit='KiB'>20971520</memory>
+ <currentMemory unit="KiB">20971520</currentMemory>
+ <memoryBacking>
+ <hugepages/>
+ </memoryBacking>
+ <vcpu placement='static'>20</vcpu>
+ <os>
+ <type arch='x86_64' machine='pc'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <features>
+ <acpi/>
+ <apic/>
+ </features>
+ <cpu match="exact" mode='host-model'>
+ <model fallback='allow'/>
+ <topology sockets='1' cores='10' threads='2'/>
+ </cpu>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu-system-x86_64</emulator>
+ <disk type='file' device='disk'>
+ <driver name='qemu' type='qcow2' cache='none'/>
+ <source file="{vm_image}"/>
+ <target dev='vda' bus='virtio'/>
+ <address bus="0x00" domain="0x0000"
+ function="0x0" slot="0x04" type="pci" />
+ </disk>
+ <!--disk type='dir' device='disk'>
+ <driver name='qemu' type='fat'/>
+ <source dir='/opt/isb_bin/dpdk'/>
+ <target dev='vdb' bus='virtio'/>
+ <readonly/>
+ </disk-->
+ <interface type="bridge">
+ <mac address="00:00:00:ab:cd:ef" />
+ <source bridge="br-int" />
+ </interface>
+ <interface type='vhostuser'>
+ <mac address='00:00:00:00:00:01'/>
+ <source type='unix' path='/usr/local/var/run/openvswitch/dpdkvhostuser0' mode='client'/>
+ <model type='virtio'/>
+ <driver queues='4'>
+ <host mrg_rxbuf='off'/>
+ </driver>
+ </interface>
+ <interface type='vhostuser'>
+ <mac address='00:00:00:00:00:02'/>
+ <source type='unix' path='/usr/local/var/run/openvswitch/dpdkvhostuser1' mode='client'/>
+ <model type='virtio'/>
+ <driver queues='4'>
+ <host mrg_rxbuf='off'/>
+ </driver>
+ </interface>
+ <serial type='pty'>
+ <target port='0'/>
+ </serial>
+ <console type='pty'>
+ <target type='serial' port='0'/>
+ </console>
+ <graphics autoport="yes" listen="0.0.0.0" port="1" type="vnc" />
+ </devices>
+</domain>
+"""
+
+
+class Ovsdpdk(StandaloneContext):
+ def __init__(self):
+ self.name = None
+ self.file_path = None
+ self.nodes = []
+ self.vm_deploy = False
+ self.ovs = []
+ self.first_run = True
+ self.dpdk_nic_bind = BIN_PATH + DPDK_NIC_BIND
+ self.user = ""
+ self.ssh_ip = ""
+ self.passwd = ""
+ self.ssh_port = ""
+ self.auth_type = ""
+
+ def init(self):
+ '''initializes itself'''
+ log.debug("In init")
+ self.parse_pod_and_get_data()
+
+ def parse_pod_and_get_data(self, file_path):
+ self.file_path = file_path
+ print("parsing pod file: {0}".format(self.file_path))
+ try:
+ with open(self.file_path) as stream:
+ cfg = yaml.load(stream)
+ except IOError:
+ print("File {0} does not exist".format(self.file_path))
+ raise
+
+ self.ovs.extend([node for node in cfg["nodes"]
+ if node["role"] == "Ovsdpdk"])
+ self.user = self.ovs[0]['user']
+ self.ssh_ip = self.ovs[0]['ip']
+ if self.ovs[0]['auth_type'] == "password":
+ self.passwd = self.ovs[0]['password']
+ else:
+ self.ssh_port = self.ovs[0]['ssh_port']
+ self.key_filename = self.ovs[0]['key_filename']
+
+ def ssh_remote_machine(self):
+ if self.ovs[0]['auth_type'] == "password":
+ self.connection = ssh.SSH(
+ self.user,
+ self.ssh_ip,
+ password=self.passwd)
+ self.connection.wait()
+ else:
+ if self.ssh_port is not None:
+ ssh_port = self.ssh_port
+ else:
+ ssh_port = ssh.DEFAULT_PORT
+ self.connection = ssh.SSH(
+ self.user,
+ self.ssh_ip,
+ port=ssh_port,
+ key_filename=self.key_filename)
+ self.connection.wait()
+
+ def get_nic_details(self):
+ nic_details = {}
+ nic_details['interface'] = {}
+ nic_details['pci'] = self.ovs[0]['phy_ports']
+ nic_details['phy_driver'] = self.ovs[0]['phy_driver']
+ nic_details['vports_mac'] = self.ovs[0]['vports_mac']
+ # Make sure that ports are bound to kernel drivers e.g. i40e/ixgbe
+ for i, _ in enumerate(nic_details['pci']):
+ err, out, _ = self.connection.execute(
+ "{dpdk_nic_bind} --force -b {driver} {port}".format(
+ dpdk_nic_bind=self.dpdk_nic_bind,
+ driver=self.ovs[0]['phy_driver'],
+ port=self.ovs[0]['phy_ports'][i]))
+ err, out, _ = self.connection.execute(
+ "lshw -c network -businfo | grep '{port}'".format(
+ port=self.ovs[0]['phy_ports'][i]))
+ a = out.split()[1]
+ err, out, _ = self.connection.execute(
+ "ip -s link show {interface}".format(
+ interface=out.split()[1]))
+ nic_details['interface'][i] = str(a)
+ print("{0}".format(nic_details))
+ return nic_details
+
+ def install_req_libs(self):
+ if self.first_run:
+ err, out, _ = self.connection.execute("apt-get update")
+ print("{0}".format(out))
+ err, out, _ = self.connection.execute(
+ "apt-get -y install qemu-kvm libvirt-bin")
+ print("{0}".format(out))
+ err, out, _ = self.connection.execute(
+ "apt-get -y install libvirt-dev bridge-utils numactl")
+ print("{0}".format(out))
+ self.first_run = False
+
+ def setup_ovs(self, vpcis):
+ self.connection.execute("/usr/bin/chmod 0666 /dev/vfio/*")
+ self.connection.execute("/usr/bin/chmod a+x /dev/vfio")
+ self.connection.execute("pkill -9 ovs")
+ self.connection.execute("ps -ef | grep ovs | grep -v grep | "
+ "awk '{print $2}' | xargs -r kill -9")
+ self.connection.execute("killall -r 'ovs*'")
+ self.connection.execute(
+ "mkdir -p {0}/etc/openvswitch".format(self.ovs[0]["vpath"]))
+ self.connection.execute(
+ "mkdir -p {0}/var/run/openvswitch".format(self.ovs[0]["vpath"]))
+ self.connection.execute(
+ "rm {0}/etc/openvswitch/conf.db".format(self.ovs[0]["vpath"]))
+ self.connection.execute(
+ "ovsdb-tool create {0}/etc/openvswitch/conf.db "
+ "{0}/share/openvswitch/"
+ "vswitch.ovsschema".format(self.ovs[0]["vpath"]))
+ self.connection.execute("modprobe vfio-pci")
+ self.connection.execute("chmod a+x /dev/vfio")
+ self.connection.execute("chmod 0666 /dev/vfio/*")
+ for vpci in vpcis:
+ self.connection.execute(
+ "/opt/isb_bin/dpdk_nic_bind.py "
+ "--bind=vfio-pci {0}".format(vpci))
+
+ def start_ovs_serverswitch(self):
+ self.connection.execute("mkdir -p /usr/local/var/run/openvswitch")
+ self.connection.execute(
+ "ovsdb-server --remote=punix:"
+ "/usr/local/var/run/openvswitch/db.sock --pidfile --detach")
+ self.connection.execute(
+ "ovs-vsctl --no-wait set "
+ "Open_vSwitch . other_config:dpdk-init=true")
+ self.connection.execute(
+ "ovs-vsctl --no-wait set "
+ "Open_vSwitch . other_config:dpdk-lcore-mask=0x3")
+ self.connection.execute(
+ "ovs-vsctl --no-wait set "
+ "Open_vSwitch . other_config:dpdk-socket-mem='2048,0'")
+ self.connection.execute(
+ "ovs-vswitchd unix:{0}/"
+ "var/run/openvswitch/db.sock --pidfile --detach "
+ "--log-file=/var/log/openvswitch/"
+ "ovs-vswitchd.log".format(
+ self.ovs[0]["vpath"]))
+ self.connection.execute(
+ "ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=2C")
+
+ def setup_ovs_bridge(self):
+ self.connection.execute("ovs-vsctl del-br br0")
+ self.connection.execute(
+ "rm -rf /usr/local/var/run/openvswitch/dpdkvhostuser*")
+ self.connection.execute(
+ "ovs-vsctl add-br br0 -- set bridge br0 datapath_type=netdev")
+ self.connection.execute(
+ "ovs-vsctl add-port br0 dpdk0 -- set Interface dpdk0 type=dpdk")
+ self.connection.execute(
+ "ovs-vsctl add-port br0 dpdk1 -- set Interface dpdk1 type=dpdk")
+ self.connection.execute(
+ "ovs-vsctl add-port br0 dpdkvhostuser0 -- set Interface "
+ "dpdkvhostuser0 type=dpdkvhostuser")
+ self.connection.execute("ovs-vsctl add-port br0 dpdkvhostuser1 "
+ "-- set Interface dpdkvhostuser1 "
+ "type=dpdkvhostuser")
+ self.connection.execute(
+ "chmod 0777 {0}/var/run/"
+ "openvswitch/dpdkvhostuser*".format(self.ovs[0]["vpath"]))
+
+ def add_oflows(self):
+ self.connection.execute("ovs-ofctl del-flows br0")
+ for flow in self.ovs[0]["flow"]:
+ self.connection.execute(flow)
+ self.connection.execute("ovs-ofctl dump-flows br0")
+ self.connection.execute(
+ "ovs-vsctl set Interface dpdk0 options:n_rxq=4")
+ self.connection.execute(
+ "ovs-vsctl set Interface dpdk1 options:n_rxq=4")
+
+ def setup_ovs_context(self, pcis, nic_details, host_driver):
+
+ ''' 1: Setup vm_ovs.xml to launch VM.'''
+ cfg_ovs = '/tmp/vm_ovs.xml'
+ vm_ovs_xml = VM_TEMPLATE.format(vm_image=self.ovs[0]["images"])
+ with open(cfg_ovs, 'w') as f:
+ f.write(vm_ovs_xml)
+
+ ''' 2: Create and start the VM'''
+ self.connection.put(cfg_ovs, cfg_ovs)
+ time.sleep(10)
+ err, out = self.check_output("virsh list --name | grep -i vm1")
+ if out == "vm1":
+ print("VM is already present")
+ else:
+ ''' FIXME: launch through libvirt'''
+ print("virsh create ...")
+ err, out, _ = self.connection.execute(
+ "virsh create /tmp/vm_ovs.xml")
+ time.sleep(10)
+ print("err : {0}".format(err))
+ print("{0}".format(_))
+ print("out : {0}".format(out))
+
+ ''' 3: Tuning for better performace.'''
+ self.pin_vcpu(pcis)
+ self.connection.execute(
+ "echo 1 > /sys/module/kvm/parameters/"
+ "allow_unsafe_assigned_interrupts")
+ self.connection.execute(
+ "echo never > /sys/kernel/mm/transparent_hugepage/enabled")
+ print("After tuning performance ...")
+
+ ''' This is roughly compatible with check_output function in subprocess
+ module which is only available in python 2.7.'''
+ def check_output(self, cmd, stderr=None):
+ '''Run a command and capture its output'''
+ err, out, _ = self.connection.execute(cmd)
+ return err, out
+
+ def read_from_file(self, filename):
+ data = ""
+ with open(filename, 'r') as the_file:
+ data = the_file.read()
+ return data
+
+ def write_to_file(self, filename, content):
+ with open(filename, 'w') as the_file:
+ the_file.write(content)
+
+ def pin_vcpu(self, pcis):
+ nodes = self.get_numa_nodes()
+ print("{0}".format(nodes))
+ num_nodes = len(nodes)
+ for i in range(0, 10):
+ self.connection.execute(
+ "virsh vcpupin vm1 {0} {1}".format(
+ i, nodes[str(num_nodes - 1)][i % len(nodes[str(num_nodes - 1)])]))
+
+ def get_numa_nodes(self):
+ nodes_sysfs = glob.iglob("/sys/devices/system/node/node*")
+ nodes = {}
+ for node_sysfs in nodes_sysfs:
+ num = os.path.basename(node_sysfs).replace("node", "")
+ with open(os.path.join(node_sysfs, "cpulist")) as cpulist_file:
+ cpulist = cpulist_file.read().strip()
+ print("cpulist: {0}".format(cpulist))
+ nodes[num] = self.split_cpu_list(cpulist)
+ print("nodes: {0}".format(nodes))
+ return nodes
+
+ def split_cpu_list(self, cpu_list):
+ if cpu_list:
+ ranges = cpu_list.split(',')
+ bounds = ([int(b) for b in r.split('-')] for r in ranges)
+ range_objects =\
+ (range(bound[0], bound[1] + 1 if len(bound) == 2
+ else bound[0] + 1) for bound in bounds)
+
+ return sorted(itertools.chain.from_iterable(range_objects))
+ else:
+ return []
+
+ def destroy_vm(self):
+ host_driver = self.ovs[0]['phy_driver']
+ err, out = self.check_output("virsh list --name | grep -i vm1")
+ print("{0}".format(out))
+ if err == 0:
+ self.connection.execute("virsh shutdown vm1")
+ self.connection.execute("virsh destroy vm1")
+ self.check_output("rmmod {0}".format(host_driver))[1].splitlines()
+ self.check_output("modprobe {0}".format(host_driver))[
+ 1].splitlines()
+ else:
+ print("error : ", err)
diff --git a/yardstick/benchmark/contexts/sriov.py b/yardstick/benchmark/contexts/sriov.py
new file mode 100644
index 000000000..fe27d2579
--- /dev/null
+++ b/yardstick/benchmark/contexts/sriov.py
@@ -0,0 +1,431 @@
+# Copyright (c) 2016-2017 Intel Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import absolute_import
+import os
+import yaml
+import re
+import time
+import glob
+import uuid
+import random
+import logging
+import itertools
+import xml.etree.ElementTree as ET
+from yardstick import ssh
+from yardstick.network_services.utils import get_nsb_option
+from yardstick.network_services.utils import provision_tool
+from yardstick.benchmark.contexts.standalone import StandaloneContext
+
+log = logging.getLogger(__name__)
+
+VM_TEMPLATE = """
+<domain type="kvm">
+ <name>vm1</name>
+ <uuid>{random_uuid}</uuid>
+ <memory unit="KiB">102400</memory>
+ <currentMemory unit="KiB">102400</currentMemory>
+ <memoryBacking>
+ <hugepages />
+ </memoryBacking>
+ <vcpu placement="static">20</vcpu>
+ <os>
+ <type arch="x86_64" machine="pc-i440fx-utopic">hvm</type>
+ <boot dev="hd" />
+ </os>
+ <features>
+ <acpi />
+ <apic />
+ <pae />
+ </features>
+ <cpu match="exact" mode="custom">
+ <model fallback="allow">SandyBridge</model>
+ <topology cores="10" sockets="1" threads="2" />
+ </cpu>
+ <clock offset="utc">
+ <timer name="rtc" tickpolicy="catchup" />
+ <timer name="pit" tickpolicy="delay" />
+ <timer name="hpet" present="no" />
+ </clock>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>restart</on_crash>
+ <devices>
+ <emulator>/usr/bin/kvm-spice</emulator>
+ <disk device="disk" type="file">
+ <driver name="qemu" type="qcow2" />
+ <source file="{vm_image}"/>
+ <target bus="virtio" dev="vda" />
+ <address bus="0x00" domain="0x0000"
+function="0x0" slot="0x04" type="pci" />
+ </disk>
+ <controller index="0" model="ich9-ehci1" type="usb">
+ <address bus="0x00" domain="0x0000"
+function="0x7" slot="0x05" type="pci" />
+ </controller>
+ <controller index="0" model="ich9-uhci1" type="usb">
+ <master startport="0" />
+ <address bus="0x00" domain="0x0000" function="0x0"
+multifunction="on" slot="0x05" type="pci" />
+ </controller>
+ <controller index="0" model="ich9-uhci2" type="usb">
+ <master startport="2" />
+ <address bus="0x00" domain="0x0000"
+function="0x1" slot="0x05" type="pci" />
+ </controller>
+ <controller index="0" model="ich9-uhci3" type="usb">
+ <master startport="4" />
+ <address bus="0x00" domain="0x0000"
+function="0x2" slot="0x05" type="pci" />
+ </controller>
+ <controller index="0" model="pci-root" type="pci" />
+ <serial type="pty">
+ <target port="0" />
+ </serial>
+ <console type="pty">
+ <target port="0" type="serial" />
+ </console>
+ <input bus="usb" type="tablet" />
+ <input bus="ps2" type="mouse" />
+ <input bus="ps2" type="keyboard" />
+ <graphics autoport="yes" listen="0.0.0.0" port="-1" type="vnc" />
+ <video>
+ <model heads="1" type="cirrus" vram="16384" />
+ <address bus="0x00" domain="0x0000"
+function="0x0" slot="0x02" type="pci" />
+ </video>
+ <memballoon model="virtio">
+ <address bus="0x00" domain="0x0000"
+function="0x0" slot="0x06" type="pci" />
+ </memballoon>
+ <interface type="bridge">
+ <mac address="{mac_addr}" />
+ <source bridge="virbr0" />
+ </interface>
+ </devices>
+</domain>
+"""
+
+
+class Sriov(StandaloneContext):
+ def __init__(self):
+ self.name = None
+ self.file_path = None
+ self.nodes = []
+ self.vm_deploy = False
+ self.sriov = []
+ self.first_run = True
+ self.dpdk_nic_bind = ""
+ self.user = ""
+ self.ssh_ip = ""
+ self.passwd = ""
+ self.ssh_port = ""
+ self.auth_type = ""
+
+ def init(self):
+ log.debug("In init")
+ self.parse_pod_and_get_data(self.file_path)
+
+ def parse_pod_and_get_data(self, file_path):
+ self.file_path = file_path
+ log.debug("parsing pod file: {0}".format(self.file_path))
+ try:
+ with open(self.file_path) as stream:
+ cfg = yaml.load(stream)
+ except IOError:
+ log.error("File {0} does not exist".format(self.file_path))
+ raise
+
+ self.sriov.extend([node for node in cfg["nodes"]
+ if node["role"] == "Sriov"])
+ self.user = self.sriov[0]['user']
+ self.ssh_ip = self.sriov[0]['ip']
+ if self.sriov[0]['auth_type'] == "password":
+ self.passwd = self.sriov[0]['password']
+ else:
+ self.ssh_port = self.sriov[0]['ssh_port']
+ self.key_filename = self.sriov[0]['key_filename']
+
+ def ssh_remote_machine(self):
+ if self.sriov[0]['auth_type'] == "password":
+ self.connection = ssh.SSH(
+ self.user,
+ self.ssh_ip,
+ password=self.passwd)
+ self.connection.wait()
+ else:
+ if self.ssh_port is not None:
+ ssh_port = self.ssh_port
+ else:
+ ssh_port = ssh.DEFAULT_PORT
+ self.connection = ssh.SSH(
+ self.user,
+ self.ssh_ip,
+ port=ssh_port,
+ key_filename=self.key_filename)
+ self.connection.wait()
+ self.dpdk_nic_bind = provision_tool(
+ self.connection,
+ os.path.join(get_nsb_option("bin_path"), "dpdk_nic_bind.py"))
+
+ def get_nic_details(self):
+ nic_details = {}
+ nic_details = {
+ 'interface': {},
+ 'pci': self.sriov[0]['phy_ports'],
+ 'phy_driver': self.sriov[0]['phy_driver'],
+ 'vf_macs': self.sriov[0]['vf_macs']
+ }
+ # Make sure that ports are bound to kernel drivers e.g. i40e/ixgbe
+ for i, _ in enumerate(nic_details['pci']):
+ err, out, _ = self.connection.execute(
+ "{dpdk_nic_bind} --force -b {driver} {port}".format(
+ dpdk_nic_bind=self.dpdk_nic_bind,
+ driver=self.sriov[0]['phy_driver'],
+ port=self.sriov[0]['phy_ports'][i]))
+ err, out, _ = self.connection.execute(
+ "lshw -c network -businfo | grep '{port}'".format(
+ port=self.sriov[0]['phy_ports'][i]))
+ a = out.split()[1]
+ err, out, _ = self.connection.execute(
+ "ip -s link show {interface}".format(
+ interface=out.split()[1]))
+ nic_details['interface'][i] = str(a)
+ log.info("{0}".format(nic_details))
+ return nic_details
+
+ def install_req_libs(self):
+ if self.first_run:
+ log.info("Installing required libraries...")
+ err, out, _ = self.connection.execute("apt-get update")
+ log.debug("{0}".format(out))
+ err, out, _ = self.connection.execute(
+ "apt-get -y install qemu-kvm libvirt-bin")
+ log.debug("{0}".format(out))
+ err, out, _ = self.connection.execute(
+ "apt-get -y install libvirt-dev bridge-utils numactl")
+ log.debug("{0}".format(out))
+ self.first_run = False
+
+ def configure_nics_for_sriov(self, host_driver, nic_details):
+ vf_pci = [[], []]
+ self.connection.execute(
+ "rmmod {0}".format(host_driver))[1].splitlines()
+ self.connection.execute(
+ "modprobe {0} num_vfs=1".format(host_driver))[1].splitlines()
+ nic_details['vf_pci'] = {}
+ for i in range(len(nic_details['pci'])):
+ self.connection.execute(
+ "echo 1 > /sys/bus/pci/devices/{0}/sriov_numvfs".format(
+ nic_details['pci'][i]))
+ err, out, _ = self.connection.execute(
+ "ip link set {interface} vf 0 mac {mac}".format(
+ interface=nic_details['interface'][i],
+ mac=nic_details['vf_macs'][i]))
+ time.sleep(3)
+ vf_pci[i] = self.get_vf_datas(
+ 'vf_pci',
+ nic_details['pci'][i],
+ nic_details['vf_macs'][i])
+ nic_details['vf_pci'][i] = vf_pci[i]
+ log.debug("NIC DETAILS : {0}".format(nic_details))
+ return nic_details
+
+ def setup_sriov_context(self, pcis, nic_details, host_driver):
+ blacklist = "/etc/modprobe.d/blacklist.conf"
+
+ # 1 : Blacklist the vf driver in /etc/modprobe.d/blacklist.conf
+ vfnic = "{0}vf".format(host_driver)
+ lines = self.read_from_file(blacklist)
+ if vfnic not in lines:
+ vfblacklist = "blacklist {vfnic}".format(vfnic=vfnic)
+ self.connection.execute(
+ "echo {vfblacklist} >> {blacklist}".format(
+ vfblacklist=vfblacklist,
+ blacklist=blacklist))
+
+ # 2 : modprobe host_driver with num_vfs
+ nic_details = self.configure_nics_for_sriov(host_driver, nic_details)
+
+ # 3: Setup vm_sriov.xml to launch VM
+ cfg_sriov = '/tmp/vm_sriov.xml'
+ mac = [0x00, 0x24, 0x81,
+ random.randint(0x00, 0x7f),
+ random.randint(0x00, 0xff),
+ random.randint(0x00, 0xff)]
+ mac_address = ':'.join(map(lambda x: "%02x" % x, mac))
+ vm_sriov_xml = VM_TEMPLATE.format(
+ random_uuid=uuid.uuid4(),
+ mac_addr=mac_address,
+ vm_image=self.sriov[0]["images"])
+ with open(cfg_sriov, 'w') as f:
+ f.write(vm_sriov_xml)
+
+ vf = nic_details['vf_pci']
+ for index in range(len(nic_details['vf_pci'])):
+ self.add_sriov_interface(
+ index,
+ vf[index]['vf_pci'],
+ mac_address,
+ "/tmp/vm_sriov.xml")
+ self.connection.execute(
+ "ifconfig {interface} up".format(
+ interface=nic_details['interface'][index]))
+
+ # 4: Create and start the VM
+ self.connection.put(cfg_sriov, cfg_sriov)
+ time.sleep(10)
+ err, out = self.check_output("virsh list --name | grep -i vm1")
+ try:
+ if out == "vm1":
+ log.info("VM is already present")
+ else:
+ # FIXME: launch through libvirt
+ log.info("virsh create ...")
+ err, out, _ = self.connection.execute(
+ "virsh create /tmp/vm_sriov.xml")
+ time.sleep(10)
+ log.error("err : {0}".format(err))
+ log.error("{0}".format(_))
+ log.debug("out : {0}".format(out))
+ except ValueError:
+ raise
+
+ # 5: Tunning for better performace
+ self.pin_vcpu(pcis)
+ self.connection.execute(
+ "echo 1 > /sys/module/kvm/parameters/"
+ "allow_unsafe_assigned_interrupts")
+ self.connection.execute(
+ "echo never > /sys/kernel/mm/transparent_hugepage/enabled")
+
+ def add_sriov_interface(self, index, vf_pci, vfmac, xml):
+ root = ET.parse(xml)
+ pattern = "0000:(\d+):(\d+).(\d+)"
+ m = re.search(pattern, vf_pci, re.MULTILINE)
+ device = root.find('devices')
+
+ interface = ET.SubElement(device, 'interface')
+ interface.set('managed', 'yes')
+ interface.set('type', 'hostdev')
+
+ mac = ET.SubElement(interface, 'mac')
+ mac.set('address', vfmac)
+ source = ET.SubElement(interface, 'source')
+
+ addr = ET.SubElement(source, "address")
+ addr.set('domain', "0x0")
+ addr.set('bus', "{0}".format(m.group(1)))
+ addr.set('function', "{0}".format(m.group(3)))
+ addr.set('slot', "{0}".format(m.group(2)))
+ addr.set('type', "pci")
+
+ vf_pci = ET.SubElement(interface, 'address')
+ vf_pci.set('type', 'pci')
+ vf_pci.set('domain', '0x0000')
+ vf_pci.set('bus', '0x00')
+ vf_pci.set('slot', '0x0{0}'.format(index + 7))
+ vf_pci.set('function', '0x00')
+
+ root.write(xml)
+
+ # This is roughly compatible with check_output function in subprocess
+ # module which is only available in python 2.7
+ def check_output(self, cmd, stderr=None):
+ # Run a command and capture its output
+ err, out, _ = self.connection.execute(cmd)
+ return err, out
+
+ def get_virtual_devices(self, pci):
+ pf_vfs = {}
+ err, extra_info = self.check_output(
+ "cat /sys/bus/pci/devices/{0}/virtfn0/uevent".format(pci))
+ pattern = "PCI_SLOT_NAME=(?P<name>[0-9:.\s.]+)"
+ m = re.search(pattern, extra_info, re.MULTILINE)
+
+ if m:
+ pf_vfs.update({pci: str(m.group(1).rstrip())})
+ log.info("pf_vfs : {0}".format(pf_vfs))
+ return pf_vfs
+
+ def get_vf_datas(self, key, value, vfmac):
+ vfret = {}
+ pattern = "0000:(\d+):(\d+).(\d+)"
+
+ vfret["mac"] = vfmac
+ vfs = self.get_virtual_devices(value)
+ log.info("vfs: {0}".format(vfs))
+ for k, v in vfs.items():
+ m = re.search(pattern, k, re.MULTILINE)
+ m1 = re.search(pattern, value, re.MULTILINE)
+ if m.group(1) == m1.group(1):
+ vfret["vf_pci"] = str(v)
+ break
+
+ return vfret
+
+ def read_from_file(self, filename):
+ data = ""
+ with open(filename, 'r') as the_file:
+ data = the_file.read()
+ return data
+
+ def write_to_file(self, filename, content):
+ with open(filename, 'w') as the_file:
+ the_file.write(content)
+
+ def pin_vcpu(self, pcis):
+ nodes = self.get_numa_nodes()
+ log.info("{0}".format(nodes))
+ num_nodes = len(nodes)
+ for i in range(0, 10):
+ self.connection.execute(
+ "virsh vcpupin vm1 {0} {1}".format(
+ i, nodes[str(num_nodes - 1)][i % len(nodes[str(num_nodes - 1)])]))
+
+ def get_numa_nodes(self):
+ nodes_sysfs = glob.iglob("/sys/devices/system/node/node*")
+ nodes = {}
+ for node_sysfs in nodes_sysfs:
+ num = os.path.basename(node_sysfs).replace("node", "")
+ with open(os.path.join(node_sysfs, "cpulist")) as cpulist_file:
+ cpulist = cpulist_file.read().strip()
+ nodes[num] = self.split_cpu_list(cpulist)
+ log.info("nodes: {0}".format(nodes))
+ return nodes
+
+ def split_cpu_list(self, cpu_list):
+ if cpu_list:
+ ranges = cpu_list.split(',')
+ bounds = ([int(b) for b in r.split('-')] for r in ranges)
+ range_objects =\
+ (range(bound[0], bound[1] + 1 if len(bound) == 2
+ else bound[0] + 1) for bound in bounds)
+
+ return sorted(itertools.chain.from_iterable(range_objects))
+ else:
+ return []
+
+ def destroy_vm(self):
+ host_driver = self.sriov[0]["phy_driver"]
+ err, out = self.check_output("virsh list --name | grep -i vm1")
+ log.info("{0}".format(out))
+ if err == 0:
+ self.connection.execute("virsh shutdown vm1")
+ self.connection.execute("virsh destroy vm1")
+ self.check_output("rmmod {0}".format(host_driver))[1].splitlines()
+ self.check_output("modprobe {0}".format(host_driver))[
+ 1].splitlines()
+ else:
+ log.error("error : {0}".format(err))
diff --git a/yardstick/benchmark/contexts/standalone.py b/yardstick/benchmark/contexts/standalone.py
index 8614f0cac..2bc1f3755 100644
--- a/yardstick/benchmark/contexts/standalone.py
+++ b/yardstick/benchmark/contexts/standalone.py
@@ -18,9 +18,11 @@ import logging
import errno
import collections
import yaml
+import time
from yardstick.benchmark.contexts.base import Context
from yardstick.common.constants import YARDSTICK_ROOT_PATH
+from yardstick.common.utils import import_modules_from_package, itersubclasses
LOG = logging.getLogger(__name__)
@@ -38,7 +40,8 @@ class StandaloneContext(Context):
self.nodes = []
self.networks = {}
self.nfvi_node = []
- super(StandaloneContext, self).__init__()
+ self.nfvi_obj = None
+ super(self.__class__, self).__init__()
def read_config_file(self):
"""Read from config file"""
@@ -48,6 +51,14 @@ class StandaloneContext(Context):
cfg = yaml.load(stream)
return cfg
+ def get_nfvi_obj(self):
+ print("{0}".format(self.nfvi_node[0]['role']))
+ context_type = self.get_context_impl(self.nfvi_node[0]['role'])
+ nfvi_obj = context_type()
+ nfvi_obj.__init__()
+ nfvi_obj.parse_pod_and_get_data(self.file_path)
+ return nfvi_obj
+
def init(self, attrs):
"""initializes itself from the supplied arguments"""
@@ -64,11 +75,24 @@ class StandaloneContext(Context):
else:
raise
- self.nodes.extend(cfg["nodes"])
- self.nfvi_node.extend([node for node in cfg["nodes"]
- if node["role"] == "nfvi_node"])
+ self.vm_deploy = attrs.get("vm_deploy", True)
+ self.nodes.extend([node for node in cfg["nodes"]
+ if str(node["role"]) != "Sriov" and
+ str(node["role"]) != "Ovsdpdk"])
+ for node in cfg["nodes"]:
+ if str(node["role"]) == "Sriov":
+ self.nfvi_node.extend([node for node in cfg["nodes"]
+ if str(node["role"]) == "Sriov"])
+ if str(node["role"]) == "Ovsdpdk":
+ self.nfvi_node.extend([node for node in cfg["nodes"]
+ if str(node["role"]) == "Ovsdpdk"])
+ LOG.info("{0}".format(node["role"]))
+ else:
+ LOG.debug("Node role is other than SRIOV and OVS")
+ self.nfvi_obj = self.get_nfvi_obj()
# add optional static network definition
self.networks.update(cfg.get("networks", {}))
+ self.nfvi_obj = self.get_nfvi_obj()
LOG.debug("Nodes: %r", self.nodes)
LOG.debug("NFVi Node: %r", self.nfvi_node)
LOG.debug("Networks: %r", self.networks)
@@ -77,13 +101,44 @@ class StandaloneContext(Context):
"""don't need to deploy"""
# Todo: NFVi deploy (sriov, vswitch, ovs etc) based on the config.
- pass
+ if not self.vm_deploy:
+ return
+
+ # Todo: NFVi deploy (sriov, vswitch, ovs etc) based on the config.
+ self.nfvi_obj.ssh_remote_machine()
+ if self.nfvi_obj.first_run is True:
+ self.nfvi_obj.install_req_libs()
+
+ nic_details = self.nfvi_obj.get_nic_details()
+ print("{0}".format(nic_details))
+
+ if self.nfvi_node[0]["role"] == "Sriov":
+ self.nfvi_obj.setup_sriov_context(
+ self.nfvi_obj.sriov[0]['phy_ports'],
+ nic_details,
+ self.nfvi_obj.sriov[0]['phy_driver'])
+ if self.nfvi_node[0]["role"] == "Ovsdpdk":
+ self.nfvi_obj.setup_ovs(self.nfvi_obj.ovs[0]["phy_ports"])
+ self.nfvi_obj.start_ovs_serverswitch()
+ time.sleep(5)
+ self.nfvi_obj.setup_ovs_bridge()
+ self.nfvi_obj.add_oflows()
+ self.nfvi_obj.setup_ovs_context(
+ self.nfvi_obj.ovs[0]['phy_ports'],
+ nic_details,
+ self.nfvi_obj.ovs[0]['phy_driver'])
+ pass
def undeploy(self):
"""don't need to undeploy"""
+ if not self.vm_deploy:
+ return
# Todo: NFVi undeploy (sriov, vswitch, ovs etc) based on the config.
- super(StandaloneContext, self).undeploy()
+ # self.nfvi_obj = self.get_nfvi_obj()
+ self.nfvi_obj.ssh_remote_machine()
+ self.nfvi_obj.destroy_vm()
+ pass
def _get_server(self, attr_name):
"""lookup server info by name from context
@@ -91,16 +146,12 @@ class StandaloneContext(Context):
Keyword arguments:
attr_name -- A name for a server listed in nodes config file
"""
-
if isinstance(attr_name, collections.Mapping):
return None
-
- if self.name.split("-")[0] != attr_name.split(".")[1]:
+ if self.name != attr_name.split(".")[1]:
return None
-
node_name = attr_name.split(".")[0]
matching_nodes = (n for n in self.nodes if n["name"] == node_name)
-
try:
# A clone is created in order to avoid affecting the
# original one.
@@ -115,7 +166,6 @@ class StandaloneContext(Context):
else:
raise ValueError("Duplicate nodes!!! Nodes: %s %s",
(matching_nodes, duplicate))
-
node["name"] = attr_name
return node
@@ -146,3 +196,19 @@ class StandaloneContext(Context):
"physical_network": network.get("physical_network"),
}
return result
+
+ def get_context_impl(self, nfvi_type):
+ """ Find the implementing class from vnf_model["vnf"]["name"] field
+
+ :param vnf_model: dictionary containing a parsed vnfd
+ :return: subclass of GenericVNF
+ """
+ import_modules_from_package(
+ "yardstick.benchmark.contexts")
+ expected_name = nfvi_type
+ impl = [c for c in itersubclasses(StandaloneContext)
+ if c.__name__ == expected_name]
+ try:
+ return next(iter(impl))
+ except StopIteration:
+ raise ValueError("No implementation for %s", expected_name)
diff --git a/yardstick/benchmark/core/plugin.py b/yardstick/benchmark/core/plugin.py
index 6c06767d5..c8d0865d1 100644
--- a/yardstick/benchmark/core/plugin.py
+++ b/yardstick/benchmark/core/plugin.py
@@ -107,8 +107,8 @@ class Plugin(object):
if deployment_ip == "local":
self.client = ssh.SSH.from_node(deployment, overrides={
- # host can't be None, fail if no INSTALLER_IP
- 'ip': os.environ["INSTALLER_IP"],
+ # host can't be None, fail if no JUMP_HOST_IP
+ 'ip': os.environ["JUMP_HOST_IP"],
})
else:
self.client = ssh.SSH.from_node(deployment)
diff --git a/yardstick/benchmark/core/task.py b/yardstick/benchmark/core/task.py
index b53d6446e..b2da7a2ee 100644
--- a/yardstick/benchmark/core/task.py
+++ b/yardstick/benchmark/core/task.py
@@ -48,6 +48,12 @@ class Task(object): # pragma: no cover
self.contexts = []
self.outputs = {}
+ def _set_dispatchers(self, output_config):
+ dispatchers = output_config.get('DEFAULT', {}).get('dispatcher',
+ 'file')
+ out_types = [s.strip() for s in dispatchers.split(',')]
+ output_config['DEFAULT']['dispatcher'] = out_types
+
def start(self, args, **kwargs):
"""Start a benchmark scenario."""
@@ -58,12 +64,20 @@ class Task(object): # pragma: no cover
check_environment()
- output_config = utils.parse_ini_file(config_file)
+ try:
+ output_config = utils.parse_ini_file(config_file)
+ except Exception:
+ # all error will be ignore, the default value is {}
+ output_config = {}
+
self._init_output_config(output_config)
self._set_output_config(output_config, args.output_file)
LOG.debug('Output configuration is: %s', output_config)
- if output_config['DEFAULT'].get('dispatcher') == 'file':
+ self._set_dispatchers(output_config)
+
+ # update dispatcher list
+ if 'file' in output_config['DEFAULT']['dispatcher']:
result = {'status': 0, 'result': {}}
utils.write_json_to_file(args.output_file, result)
@@ -193,9 +207,10 @@ class Task(object): # pragma: no cover
return 'PASS'
def _do_output(self, output_config, result):
+ dispatchers = DispatcherBase.get(output_config)
- dispatcher = DispatcherBase.get(output_config)
- dispatcher.flush_result_data(result)
+ for dispatcher in dispatchers:
+ dispatcher.flush_result_data(result)
def _run(self, scenarios, run_in_parallel, output_file):
"""Deploys context and calls runners"""
diff --git a/yardstick/benchmark/scenarios/availability/actionplayers.py b/yardstick/benchmark/scenarios/availability/actionplayers.py
index 420626413..c5e199ba6 100644
--- a/yardstick/benchmark/scenarios/availability/actionplayers.py
+++ b/yardstick/benchmark/scenarios/availability/actionplayers.py
@@ -29,8 +29,10 @@ class AttackerPlayer(ActionPlayer):
class OperationPlayer(ActionPlayer):
- def __init__(self, operation):
+ def __init__(self, operation, intermediate_variables):
self.underlyingOperation = operation
+ self.underlyingOperation.intermediate_variables \
+ = intermediate_variables
def action(self):
self.underlyingOperation.run()
diff --git a/yardstick/benchmark/scenarios/availability/director.py b/yardstick/benchmark/scenarios/availability/director.py
index e0d05ebf5..c9187c34d 100644
--- a/yardstick/benchmark/scenarios/availability/director.py
+++ b/yardstick/benchmark/scenarios/availability/director.py
@@ -65,7 +65,9 @@ class Director(object):
self.resultCheckerMgr = baseresultchecker.ResultCheckerMgr()
self.resultCheckerMgr.init_ResultChecker(result_check_cfgs, nodes)
- def createActionPlayer(self, type, key):
+ def createActionPlayer(self, type, key, intermediate_variables=None):
+ if intermediate_variables is None:
+ intermediate_variables = {}
LOG.debug(
"the type of current action is %s, the key is %s", type, key)
if type == ActionType.ATTACKER:
@@ -76,7 +78,8 @@ class Director(object):
return actionplayers.ResultCheckerPlayer(
self.resultCheckerMgr[key])
if type == ActionType.OPERATION:
- return actionplayers.OperationPlayer(self.operationMgr[key])
+ return actionplayers.OperationPlayer(self.operationMgr[key],
+ intermediate_variables)
LOG.debug("something run when creatactionplayer")
def createActionRollbacker(self, type, key):
diff --git a/yardstick/benchmark/scenarios/availability/operation/baseoperation.py b/yardstick/benchmark/scenarios/availability/operation/baseoperation.py
index be286b8fd..88ca9e2bb 100644
--- a/yardstick/benchmark/scenarios/availability/operation/baseoperation.py
+++ b/yardstick/benchmark/scenarios/availability/operation/baseoperation.py
@@ -58,6 +58,7 @@ class BaseOperation(object):
self.key = ''
self._config = config
self._context = context
+ self.intermediate_variables = {}
@staticmethod
def get_operation_cls(type):
diff --git a/yardstick/benchmark/scenarios/availability/operation/operation_general.py b/yardstick/benchmark/scenarios/availability/operation/operation_general.py
index 8fd387e47..af1ae7469 100644
--- a/yardstick/benchmark/scenarios/availability/operation/operation_general.py
+++ b/yardstick/benchmark/scenarios/availability/operation/operation_general.py
@@ -15,7 +15,8 @@ from yardstick.benchmark.scenarios.availability.operation.baseoperation \
import yardstick.ssh as ssh
from yardstick.benchmark.scenarios.availability.util \
- import buildshellparams, execute_shell_command
+ import buildshellparams, execute_shell_command, \
+ read_stdout_item, build_shell_command
LOG = logging.getLogger(__name__)
@@ -39,11 +40,7 @@ class GeneralOperaion(BaseOperation):
self.operation_key = self._config['operation_key']
if "action_parameter" in self._config:
- actionParameter = self._config['action_parameter']
- str = buildshellparams(
- actionParameter, True if self.connection else False)
- l = list(item for item in actionParameter.values())
- self.action_param = str.format(*l)
+ self.actionParameter_config = self._config['action_parameter']
if "rollback_parameter" in self._config:
rollbackParameter = self._config['rollback_parameter']
@@ -61,6 +58,11 @@ class GeneralOperaion(BaseOperation):
def run(self):
if "action_parameter" in self._config:
+ self.action_param = \
+ build_shell_command(
+ self.actionParameter_config,
+ True if self.connection else False,
+ self.intermediate_variables)
if self.connection:
with open(self.action_script, "r") as stdin_file:
exit_status, stdout, stderr = self.connection.execute(
@@ -83,6 +85,12 @@ class GeneralOperaion(BaseOperation):
if exit_status == 0:
LOG.debug("success,the operation's output is: %s", stdout)
+ if "return_parameter" in self._config:
+ returnParameter = self._config['return_parameter']
+ for key, item in returnParameter.items():
+ value = read_stdout_item(stdout, key)
+ LOG.debug("intermediate variables %s: %s", item, value)
+ self.intermediate_variables[item] = value
else:
LOG.error(
"the operation's error, stdout:%s, stderr:%s",
diff --git a/yardstick/benchmark/scenarios/availability/scenario_general.py b/yardstick/benchmark/scenarios/availability/scenario_general.py
index 28bec8aff..17ad79f29 100644
--- a/yardstick/benchmark/scenarios/availability/scenario_general.py
+++ b/yardstick/benchmark/scenarios/availability/scenario_general.py
@@ -25,6 +25,7 @@ class ScenarioGeneral(base.Scenario):
"scenario_cfg:%s context_cfg:%s", scenario_cfg, context_cfg)
self.scenario_cfg = scenario_cfg
self.context_cfg = context_cfg
+ self.intermediate_variables = {}
def setup(self):
self.director = Director(self.scenario_cfg, self.context_cfg)
@@ -38,7 +39,8 @@ class ScenarioGeneral(base.Scenario):
orderedSteps.index(step) + 1)
try:
actionPlayer = self.director.createActionPlayer(
- step['actionType'], step['actionKey'])
+ step['actionType'], step['actionKey'],
+ self.intermediate_variables)
actionPlayer.action()
actionRollbacker = self.director.createActionRollbacker(
step['actionType'], step['actionKey'])
diff --git a/yardstick/benchmark/scenarios/availability/util.py b/yardstick/benchmark/scenarios/availability/util.py
index eadbfa53b..6fef622bd 100644
--- a/yardstick/benchmark/scenarios/availability/util.py
+++ b/yardstick/benchmark/scenarios/availability/util.py
@@ -14,13 +14,8 @@ LOG = logging.getLogger(__name__)
def buildshellparams(param, remote=True):
- i = 0
- values = []
result = '/bin/bash -s' if remote else ''
- for key in param.keys():
- values.append(param[key])
- result += " {%d}" % i
- i = i + 1
+ result += "".join(" {%d}" % i for i in range(len(param)))
return result
@@ -36,5 +31,29 @@ def execute_shell_command(command):
output = traceback.format_exc()
LOG.error("exec command '%s' error:\n ", command)
LOG.error(traceback.format_exc())
-
return exitcode, output
+
+PREFIX = '$'
+
+
+def build_shell_command(param_config, remote=True, intermediate_variables=None):
+ param_template = '/bin/bash -s' if remote else ''
+ if intermediate_variables:
+ for key, val in param_config.items():
+ if str(val).startswith(PREFIX):
+ try:
+ param_config[key] = intermediate_variables[val]
+ except KeyError:
+ pass
+ result = param_template + "".join(" {}".format(v) for v in param_config.values())
+ LOG.debug("THE RESULT OF build_shell_command IS: %s", result)
+ return result
+
+
+def read_stdout_item(stdout, key):
+ for item in stdout.splitlines():
+ if key in item:
+ attributes = item.split("|")
+ if attributes[1].lstrip().startswith(key):
+ return attributes[2].strip()
+ return None
diff --git a/yardstick/benchmark/scenarios/networking/iperf3.py b/yardstick/benchmark/scenarios/networking/iperf3.py
index 3135af9bd..a3d273750 100644
--- a/yardstick/benchmark/scenarios/networking/iperf3.py
+++ b/yardstick/benchmark/scenarios/networking/iperf3.py
@@ -50,6 +50,17 @@ For more info see http://software.es.net/iperf
type: int
unit: bytes
default: -
+ length - length of buffer to read or write,
+ (default 128 KB for TCP, 8 KB for UDP)
+ type: int
+ unit: k
+ default: -
+ window - set window size / socket buffer size
+ set TCP windows size. for UDP way to test, this will set to accept UDP
+ packet buffer size, limit the max size of acceptable data packet.
+ type: int
+ unit: k
+ default: -
"""
__scenario_type__ = "Iperf3"
@@ -122,6 +133,12 @@ For more info see http://software.es.net/iperf
elif "blockcount" in options:
cmd += " --blockcount %d" % options["blockcount"]
+ if "length" in options:
+ cmd += " --length %s" % options["length"]
+
+ if "window" in options:
+ cmd += " --window %s" % options["window"]
+
LOG.debug("Executing command: %s", cmd)
status, stdout, stderr = self.host.execute(cmd)
diff --git a/yardstick/benchmark/scenarios/networking/pktgen.py b/yardstick/benchmark/scenarios/networking/pktgen.py
index e6aa7e5fb..8ca1ca60e 100644
--- a/yardstick/benchmark/scenarios/networking/pktgen.py
+++ b/yardstick/benchmark/scenarios/networking/pktgen.py
@@ -9,6 +9,7 @@
from __future__ import absolute_import
from __future__ import print_function
+import os
import logging
import pkg_resources
@@ -19,6 +20,9 @@ from yardstick.benchmark.scenarios import base
LOG = logging.getLogger(__name__)
+VNIC_TYPE_LIST = ["ovs", "sriov"]
+SRIOV_DRIVER_LIST = ["ixgbevf", "i40evf"]
+
class Pktgen(base.Scenario):
"""Execute pktgen between two hosts
@@ -44,7 +48,11 @@ class Pktgen(base.Scenario):
def __init__(self, scenario_cfg, context_cfg):
self.scenario_cfg = scenario_cfg
self.context_cfg = context_cfg
+ self.vnic_name = "eth0"
+ self.vnic_type = "ovs"
+ self.queue_number = 1
self.setup_done = False
+ self.multiqueue_setup_done = False
def setup(self):
"""scenario setup"""
@@ -67,6 +75,212 @@ class Pktgen(base.Scenario):
self.setup_done = True
+ def multiqueue_setup(self):
+ # one time setup stuff
+ cmd = "sudo sysctl -w net.core.netdev_budget=3000"
+ self.server.send_command(cmd)
+ self.client.send_command(cmd)
+
+ cmd = "sudo sysctl -w net.core.netdev_max_backlog=100000"
+ self.server.send_command(cmd)
+ self.client.send_command(cmd)
+
+ """multiqueue setup"""
+ if not self._is_irqbalance_disabled():
+ self._disable_irqbalance()
+
+ vnic_driver_name = self._get_vnic_driver_name()
+ if vnic_driver_name in SRIOV_DRIVER_LIST:
+ self.vnic_type = "sriov"
+
+ # one time setup stuff
+ cmd = "sudo ethtool -G %s rx 4096 tx 4096" % self.vnic_name
+ self.server.send_command(cmd)
+ self.client.send_command(cmd)
+
+ self.queue_number = self._get_sriov_queue_number()
+ self._setup_irqmapping_sriov(self.queue_number)
+ else:
+ self.vnic_type = "ovs"
+ self.queue_number = self._enable_ovs_multiqueue()
+ self._setup_irqmapping_ovs(self.queue_number)
+
+ self.multiqueue_setup_done = True
+
+ def _get_vnic_driver_name(self):
+ cmd = "readlink /sys/class/net/%s/device/driver" % self.vnic_name
+ LOG.debug("Executing command: %s", cmd)
+ status, stdout, stderr = self.server.execute(cmd)
+ if status:
+ raise RuntimeError(stderr)
+ return os.path.basename(stdout.strip())
+
+ def _is_irqbalance_disabled(self):
+ """Did we disable irqbalance already in the guest?"""
+ is_disabled = False
+ cmd = "grep ENABLED /etc/default/irqbalance"
+ status, stdout, stderr = self.server.execute(cmd)
+ if status:
+ raise RuntimeError(stderr)
+ if "0" in stdout:
+ is_disabled = True
+
+ return is_disabled
+
+ def _disable_irqbalance(self):
+ cmd = "sudo sed -i -e 's/ENABLED=\"1\"/ENABLED=\"0\"/g' " \
+ "/etc/default/irqbalance"
+ status, stdout, stderr = self.server.execute(cmd)
+ status, stdout, stderr = self.client.execute(cmd)
+ if status:
+ raise RuntimeError(stderr)
+
+ cmd = "sudo service irqbalance stop"
+ status, stdout, stderr = self.server.execute(cmd)
+ status, stdout, stderr = self.client.execute(cmd)
+ if status:
+ raise RuntimeError(stderr)
+
+ cmd = "sudo service irqbalance disable"
+ status, stdout, stderr = self.server.execute(cmd)
+ status, stdout, stderr = self.client.execute(cmd)
+ if status:
+ raise RuntimeError(stderr)
+
+ def _setup_irqmapping_ovs(self, queue_number):
+ cmd = "grep 'virtio0-input.0' /proc/interrupts |" \
+ "awk '{match($0,/ +[0-9]+/)} " \
+ "{print substr($1,RSTART,RLENGTH-1)}'"
+ status, stdout, stderr = self.server.execute(cmd)
+ if status:
+ raise RuntimeError(stderr)
+
+ cmd = "echo 1 | sudo tee /proc/irq/%s/smp_affinity" % (int(stdout))
+ status, stdout, stderr = self.server.execute(cmd)
+ status, stdout, stderr = self.client.execute(cmd)
+ if status:
+ raise RuntimeError(stderr)
+
+ cmd = "grep 'virtio0-output.0' /proc/interrupts |" \
+ "awk '{match($0,/ +[0-9]+/)} " \
+ "{print substr($1,RSTART,RLENGTH-1)}'"
+ status, stdout, stderr = self.server.execute(cmd)
+ if status:
+ raise RuntimeError(stderr)
+
+ cmd = "echo 1 | sudo tee /proc/irq/%s/smp_affinity" % (int(stdout))
+ status, stdout, stderr = self.server.execute(cmd)
+ status, stdout, stderr = self.client.execute(cmd)
+ if status:
+ raise RuntimeError(stderr)
+
+ if queue_number == 1:
+ return
+
+ for i in range(1, queue_number):
+ cmd = "grep 'virtio0-input.%s' /proc/interrupts |" \
+ "awk '{match($0,/ +[0-9]+/)} " \
+ "{print substr($1,RSTART,RLENGTH-1)}'" % (i)
+ status, stdout, stderr = self.server.execute(cmd)
+ if status:
+ raise RuntimeError(stderr)
+
+ cmd = "echo %s | sudo tee /proc/irq/%s/smp_affinity" \
+ % (1 << i, int(stdout))
+ status, stdout, stderr = self.server.execute(cmd)
+ status, stdout, stderr = self.client.execute(cmd)
+ if status:
+ raise RuntimeError(stderr)
+
+ cmd = "grep 'virtio0-output.%s' /proc/interrupts |" \
+ "awk '{match($0,/ +[0-9]+/)} " \
+ "{print substr($1,RSTART,RLENGTH-1)}'" % (i)
+ status, stdout, stderr = self.server.execute(cmd)
+ if status:
+ raise RuntimeError(stderr)
+
+ cmd = "echo %s | sudo tee /proc/irq/%s/smp_affinity" \
+ % (1 << i, int(stdout))
+ status, stdout, stderr = self.server.execute(cmd)
+ status, stdout, stderr = self.client.execute(cmd)
+ if status:
+ raise RuntimeError(stderr)
+
+ def _setup_irqmapping_sriov(self, queue_number):
+ cmd = "grep '%s-TxRx-0' /proc/interrupts |" \
+ "awk '{match($0,/ +[0-9]+/)} " \
+ "{print substr($1,RSTART,RLENGTH-1)}'" % self.vnic_name
+ status, stdout, stderr = self.server.execute(cmd)
+ if status:
+ raise RuntimeError(stderr)
+
+ cmd = "echo 1 | sudo tee /proc/irq/%s/smp_affinity" % (int(stdout))
+ status, stdout, stderr = self.server.execute(cmd)
+ status, stdout, stderr = self.client.execute(cmd)
+ if status:
+ raise RuntimeError(stderr)
+
+ if queue_number == 1:
+ return
+
+ for i in range(1, queue_number):
+ cmd = "grep '%s-TxRx-%s' /proc/interrupts |" \
+ "awk '{match($0,/ +[0-9]+/)} " \
+ "{print substr($1,RSTART,RLENGTH-1)}'" % (self.vnic_name, i)
+ status, stdout, stderr = self.server.execute(cmd)
+ if status:
+ raise RuntimeError(stderr)
+
+ cmd = "echo %s | sudo tee /proc/irq/%s/smp_affinity" \
+ % (1 << i, int(stdout))
+ status, stdout, stderr = self.server.execute(cmd)
+ status, stdout, stderr = self.client.execute(cmd)
+ if status:
+ raise RuntimeError(stderr)
+
+ def _get_sriov_queue_number(self):
+ """Get queue number from server as both VMs are the same"""
+ cmd = "grep %s-TxRx- /proc/interrupts | wc -l" % self.vnic_name
+ LOG.debug("Executing command: %s", cmd)
+ status, stdout, stderr = self.server.execute(cmd)
+ if status:
+ raise RuntimeError(stderr)
+ return int(stdout)
+
+ def _get_available_queue_number(self):
+ """Get queue number from client as both VMs are the same"""
+ cmd = "sudo ethtool -l %s | grep Combined | head -1 |" \
+ "awk '{printf $2}'" % self.vnic_name
+ LOG.debug("Executing command: %s", cmd)
+ status, stdout, stderr = self.server.execute(cmd)
+ if status:
+ raise RuntimeError(stderr)
+ return int(stdout)
+
+ def _get_usable_queue_number(self):
+ """Get queue number from client as both VMs are the same"""
+ cmd = "sudo ethtool -l %s | grep Combined | tail -1 |" \
+ "awk '{printf $2}'" % self.vnic_name
+ LOG.debug("Executing command: %s", cmd)
+ status, stdout, stderr = self.server.execute(cmd)
+ if status:
+ raise RuntimeError(stderr)
+ return int(stdout)
+
+ def _enable_ovs_multiqueue(self):
+ available_queue_number = self._get_available_queue_number()
+ usable_queue_number = self._get_usable_queue_number()
+ if available_queue_number > 1 and \
+ available_queue_number != usable_queue_number:
+ cmd = "sudo ethtool -L %s combined %s" % \
+ (self.vnic_name, available_queue_number)
+ LOG.debug("Executing command: %s", cmd)
+ status, stdout, stderr = self.server.execute(cmd)
+ status, stdout, stderr = self.client.execute(cmd)
+ if status:
+ raise RuntimeError(stderr)
+ return available_queue_number
+
def _iptables_setup(self):
"""Setup iptables on server to monitor for received packets"""
cmd = "sudo iptables -F; " \
@@ -99,6 +313,14 @@ class Pktgen(base.Scenario):
options = self.scenario_cfg['options']
packetsize = options.get("packetsize", 60)
self.number_of_ports = options.get("number_of_ports", 10)
+ self.vnic_name = options.get("vnic_name", "eth0")
+ ovs_dpdk = options.get("ovs_dpdk", False)
+ pps = options.get("pps", 1000000)
+ multiqueue = options.get("multiqueue", False)
+
+ if multiqueue and not self.multiqueue_setup_done:
+ self.multiqueue_setup()
+
# if run by a duration runner
duration_time = self.scenario_cfg["runner"].get("duration", None) \
if "runner" in self.scenario_cfg else None
@@ -114,8 +336,18 @@ class Pktgen(base.Scenario):
self._iptables_setup()
- cmd = "sudo bash pktgen.sh %s %s %s %s" \
- % (ipaddr, self.number_of_ports, packetsize, duration)
+ queue_number = self.queue_number
+
+ # For native OVS, half of vCPUs are used by vhost kernel threads
+ # hence set the queue_number to half number of vCPUs
+ # e.g. set queue_number to 2 if there are 4 vCPUs
+ if self.vnic_type == "ovs" and not ovs_dpdk and self.queue_number > 1:
+ queue_number = self.queue_number / 2
+
+ cmd = "sudo bash pktgen.sh %s %s %s %s %s %s" \
+ % (ipaddr, self.number_of_ports, packetsize,
+ duration, queue_number, pps)
+
LOG.debug("Executing command: %s", cmd)
status, stdout, stderr = self.client.execute(cmd)
@@ -131,12 +363,15 @@ class Pktgen(base.Scenario):
sent = result['packets_sent']
received = result['packets_received']
ppm = 1000000 * (sent - received) / sent
+ # if ppm is 1, then 11 out of 10 million is no pass
+ ppm += (sent - received) % sent > 0
+ LOG.debug("Lost packets %d - Lost ppm %d", (sent - received), ppm)
sla_max_ppm = int(self.scenario_cfg["sla"]["max_ppm"])
assert ppm <= sla_max_ppm, "ppm %d > sla_max_ppm %d; " \
% (ppm, sla_max_ppm)
-def _test():
+def _test(): # pragma: no cover
"""internal test function"""
key_filename = pkg_resources.resource_filename('yardstick.resources',
'files/yardstick_key')
@@ -165,6 +400,5 @@ def _test():
p.run(result)
print(result)
-
if __name__ == '__main__':
_test()
diff --git a/yardstick/benchmark/scenarios/networking/pktgen_benchmark.bash b/yardstick/benchmark/scenarios/networking/pktgen_benchmark.bash
index 4224c5abf..e338a1b09 100644
--- a/yardstick/benchmark/scenarios/networking/pktgen_benchmark.bash
+++ b/yardstick/benchmark/scenarios/networking/pktgen_benchmark.bash
@@ -16,6 +16,8 @@ DST_IP=$1 # destination IP address
NUM_PORTS=$2 # number of source ports
PKT_SIZE=$3 # packet size
DURATION=$4 # test duration (seconds)
+TRXQUEUE=$5 # number of RX/TX queues to use
+PPS=$6 # packets per second to send
# Configuration
UDP_SRC_MIN=1000 # UDP source port min
@@ -37,62 +39,100 @@ pgset()
fi
}
+# remove all devices from thread
+pgclean()
+{
+ COUNTER=0
+ while [ ${COUNTER} -lt ${TRXQUEUE} ]; do
+ #
+ # Thread commands
+ #
+
+ PGDEV=/proc/net/pktgen/kpktgend_${COUNTER}
+
+ # Remove all devices from this thread
+ pgset "rem_device_all"
+ let COUNTER=COUNTER+1
+ done
+}
+
# configure pktgen (see pktgen doc for details)
pgconfig()
{
- #
- # Thread commands
- #
+ pps=$(( PPS / TRXQUEUE ))
+ COUNTER=0
+ while [ ${COUNTER} -lt ${TRXQUEUE} ]; do
+ #
+ # Thread commands
+ #
- PGDEV=/proc/net/pktgen/kpktgend_0
+ PGDEV=/proc/net/pktgen/kpktgend_${COUNTER}
- # Remove all devices from this thread
- pgset "rem_device_all"
+ # Add device to thread
+ pgset "add_device $DEV@${COUNTER}"
- # Add device to thread
- pgset "add_device $DEV"
+ #
+ # Device commands
+ #
- #
- # Device commands
- #
+ PGDEV=/proc/net/pktgen/$DEV@${COUNTER}
- PGDEV=/proc/net/pktgen/$DEV
+ # 0 means continious sends untill explicitly stopped
+ pgset "count 0"
- # 0 means continious sends untill explicitly stopped
- pgset "count 0"
+ # set pps count to test with an explicit number. if 0 will try with bandwidth
+ if [ ${pps} -gt 0 ]
+ then
+ pgset "ratep ${pps}"
+ fi
- # use single SKB for all transmits
- pgset "clone_skb 0"
+ pgset "clone_skb 10"
- # packet size, NIC adds 4 bytes CRC
- pgset "pkt_size $PKT_SIZE"
+ # use different queue per thread
+ pgset "queue_map_min ${COUNTER}"
+ pgset "queue_map_max ${COUNTER}"
- # random address within the min-max range
- pgset "flag IPDST_RND UDPSRC_RND UDPDST_RND"
+ # packet size, NIC adds 4 bytes CRC
+ pgset "pkt_size $PKT_SIZE"
- # destination IP
- pgset "dst_min $DST_IP"
- pgset "dst_max $DST_IP"
+ # random address within the min-max range
+ pgset "flag UDPDST_RND"
+ pgset "flag UDPSRC_RND"
+ pgset "flag IPDST_RND"
- # destination MAC address
- pgset "dst_mac $MAC"
+ # destination IP
+ pgset "dst_min $DST_IP"
+ pgset "dst_max $DST_IP"
+
+ # destination MAC address
+ pgset "dst_mac $MAC"
+
+ # source UDP port range
+ pgset "udp_src_min $UDP_SRC_MIN"
+ pgset "udp_src_max $UDP_SRC_MAX"
- # source UDP port range
- pgset "udp_src_min $UDP_SRC_MIN"
- pgset "udp_src_max $UDP_SRC_MAX"
+ # destination UDP port range
+ pgset "udp_dst_min $UDP_DST_MIN"
+ pgset "udp_dst_max $UDP_DST_MAX"
- # destination UDP port range
- pgset "udp_dst_min $UDP_DST_MIN"
- pgset "udp_dst_max $UDP_DST_MAX"
+ let COUNTER=COUNTER+1
+
+ done
}
# run pktgen
pgrun()
{
- # Time to run, result can be vieved in /proc/net/pktgen/$DEV
+ # Time to run, result can be viewed in /proc/net/pktgen/$DEV
PGDEV=/proc/net/pktgen/pgctrl
# Will hang, Ctrl-C or SIGINT to stop
pgset "start" start
+
+ COUNTER=0
+ while [ ${COUNTER} -lt ${TRXQUEUE} ]; do
+ taskset -c ${COUNTER} kpktgend_${COUNTER}
+ let COUNTER=COUNTER+1
+ done
}
# run pktgen for ${DURATION} seconds
@@ -111,19 +151,28 @@ run_test()
# write the result to stdout in json format
output_json()
{
- sent=$(awk '/^Result:/{print $5}' <$PGDEV)
- pps=$(awk 'match($0,/'\([0-9]+\)pps'/, a) {print a[1]}' <$PGDEV)
- errors=$(awk '/errors:/{print $5}' <$PGDEV)
+ sent=0
+ result_pps=0
+ errors=0
+ PGDEV=/proc/net/pktgen/$DEV@
+ COUNTER=0
+ while [ ${COUNTER} -lt ${TRXQUEUE} ]; do
+ sent=$(($sent + $(awk '/^Result:/{print $5}' <$PGDEV${COUNTER})))
+ result_pps=$(($result_pps + $(awk 'match($0,/'\([0-9]+\)pps'/, a) {print a[1]}' <$PGDEV${COUNTER})))
+ errors=$(($errors + $(awk '/errors:/{print $5}' <$PGDEV${COUNTER})))
+ let COUNTER=COUNTER+1
+ done
flows=$(( NUM_PORTS * (NUM_PORTS + 1) ))
- echo { '"packets_sent"':$sent , '"packets_per_second"':$pps, '"flows"':$flows, '"errors"':$errors }
+ echo '{ "packets_sent"':${sent} , '"packets_per_second"':${result_pps}, '"flows"':${flows}, '"errors"':${errors} '}'
}
# main entry
main()
{
modprobe pktgen
+ pgclean
ping -c 3 $DST_IP >/dev/null
@@ -137,16 +186,20 @@ main()
pgconfig
# run the test
- run_test >/dev/null
+ run_test
- PGDEV=/proc/net/pktgen/$DEV
+ PGDEV=/proc/net/pktgen/$DEV@
# check result
- result=$(cat $PGDEV | fgrep "Result: OK:")
- if [ "$result" = "" ]; then
- cat $PGDEV | fgrep Result: >/dev/stderr
- exit 1
- fi
+ COUNTER=0
+ while [ ${COUNTER} -lt ${TRXQUEUE} ]; do
+ result=$(cat $PGDEV${COUNTER} | fgrep "Result: OK:")
+ if [ "$result" = "" ]; then
+ cat $PGDEV${COUNTER} | fgrep Result: >/dev/stderr
+ exit 1
+ fi
+ let COUNTER=COUNTER+1
+ done
# output result
output_json
diff --git a/yardstick/common/constants.py b/yardstick/common/constants.py
index 69485a4e4..8e8114fbb 100644
--- a/yardstick/common/constants.py
+++ b/yardstick/common/constants.py
@@ -77,6 +77,7 @@ INFLUXDB_PASS = get_param('influxdb.password', 'root')
INFLUXDB_DB_NAME = get_param('influxdb.db_name', 'yardstick')
INFLUXDB_IMAGE = get_param('influxdb.image', 'tutum/influxdb')
INFLUXDB_TAG = get_param('influxdb.tag', '0.13')
+INFLUXDB_DASHBOARD_PORT = 8083
# grafana
GRAFANA_IP = get_param('grafana.ip', SERVER_IP)
@@ -85,6 +86,7 @@ GRAFANA_USER = get_param('grafana.username', 'admin')
GRAFANA_PASS = get_param('grafana.password', 'admin')
GRAFANA_IMAGE = get_param('grafana.image', 'grafana/grafana')
GRAFANA_TAG = get_param('grafana.tag', '3.1.1')
+GRAFANA_MAPPING_PORT = 1948
# api
API_PORT = 5000
diff --git a/yardstick/common/utils.py b/yardstick/common/utils.py
index 92bb7b7d3..7a64b8ca2 100644
--- a/yardstick/common/utils.py
+++ b/yardstick/common/utils.py
@@ -172,7 +172,15 @@ def write_file(path, data, mode='w'):
def parse_ini_file(path):
parser = configparser.ConfigParser()
- parser.read(path)
+
+ try:
+ files = parser.read(path)
+ except configparser.MissingSectionHeaderError:
+ logger.exception('invalid file type')
+ raise
+ else:
+ if not files:
+ raise RuntimeError('file not exist')
try:
default = {k: v for k, v in parser.items('DEFAULT')}
diff --git a/yardstick/dispatcher/base.py b/yardstick/dispatcher/base.py
index e77249c54..1fc0a2f31 100644
--- a/yardstick/dispatcher/base.py
+++ b/yardstick/dispatcher/base.py
@@ -41,9 +41,11 @@ class Base(object):
def get(config):
"""Returns instance of a dispatcher for dispatcher type.
"""
- out_type = config['DEFAULT']['dispatcher']
+ list_dispatcher = \
+ [Base.get_cls(out_type.capitalize())(config)
+ for out_type in config['DEFAULT']['dispatcher']]
- return Base.get_cls(out_type.capitalize())(config)
+ return list_dispatcher
@abc.abstractmethod
def flush_result_data(self, data):
diff --git a/yardstick/network_services/vnf_generic/vnfdgen.py b/yardstick/network_services/vnf_generic/vnfdgen.py
index 40cc14a49..b56a91915 100644
--- a/yardstick/network_services/vnf_generic/vnfdgen.py
+++ b/yardstick/network_services/vnf_generic/vnfdgen.py
@@ -48,7 +48,7 @@ def generate_vnfd(vnf_model, node):
rendered_vnfd = render(vnf_model, **node)
# This is done to get rid of issues with serializing node
del node["get"]
- filled_vnfd = yaml.load(rendered_vnfd)
+ filled_vnfd = yaml.safe_load(rendered_vnfd)
return filled_vnfd
diff --git a/yardstick/orchestrator/heat.py b/yardstick/orchestrator/heat.py
index 57b23d393..95ca0ad2e 100644
--- a/yardstick/orchestrator/heat.py
+++ b/yardstick/orchestrator/heat.py
@@ -231,7 +231,7 @@ name (i.e. %s).\
}
def add_network(self, name, physical_network='physnet1', provider=None,
- segmentation_id=None, port_security_enabled=True):
+ segmentation_id=None, port_security_enabled=None):
"""add to the template a Neutron Net"""
log.debug("adding Neutron::Net '%s'", name)
if provider is None:
@@ -239,7 +239,6 @@ name (i.e. %s).\
'type': 'OS::Neutron::Net',
'properties': {
'name': name,
- 'port_security_enabled': port_security_enabled,
}
}
else:
@@ -249,11 +248,14 @@ name (i.e. %s).\
'name': name,
'network_type': 'vlan',
'physical_network': physical_network,
- 'port_security_enabled': port_security_enabled,
},
}
if segmentation_id:
self.resources[name]['properties']['segmentation_id'] = segmentation_id
+ # if port security is not defined then don't add to template:
+ # some deployments don't have port security plugin installed
+ if port_security_enabled is not None:
+ self.resources[name]['properties']['port_security_enabled'] = port_security_enabled
def add_server_group(self, name, policies): # pragma: no cover
"""add to the template a ServerGroup"""