diff options
32 files changed, 806 insertions, 75 deletions
diff --git a/gui/app/images/logo.png b/gui/app/images/logo.png Binary files differnew file mode 100644 index 000000000..c67c0d635 --- /dev/null +++ b/gui/app/images/logo.png diff --git a/gui/app/scripts/controllers/report.controller.js b/gui/app/scripts/controllers/report.controller.js index 9b6b5958b..6a62cf8ea 100644 --- a/gui/app/scripts/controllers/report.controller.js +++ b/gui/app/scripts/controllers/report.controller.js @@ -54,7 +54,7 @@ angular.module('yardStickGui2App') } $scope.goToExternal = function goToExternal(id) { - var url = External_URL + ':' + $scope.jumpPort + '/dashboard/db' + '/' + id; + var url = Grafana_URL +':'+$scope.jumpPort+'/dashboard/db'+ '/' + id; window.open(url, '_blank'); } diff --git a/gui/app/views/environmentList.html b/gui/app/views/environmentList.html index 29273a724..1b00b1cc6 100644 --- a/gui/app/views/environmentList.html +++ b/gui/app/views/environmentList.html @@ -22,21 +22,18 @@ <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> <a style="color:#4dc5cf" 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> + delete <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> --> + <li role="menuitem"><a ng-click="openDeleteEnv(env.uuid,'environment')">delete</a></li> + </ul> </div> </div> diff --git a/gui/app/views/layout/header.html b/gui/app/views/layout/header.html index 033322a62..ad90de952 100644 --- a/gui/app/views/layout/header.html +++ b/gui/app/views/layout/header.html @@ -4,8 +4,9 @@ <div class="navbar-header"> - + <img src="images/logo.png" style="width:50px;height:50px;float:left;margin-left:20px;" /> <a class="navbar-brand" href="#/">Yardstick</a> + </div> @@ -33,11 +34,11 @@ .navbar { border-radius: 0px; - background-color: #e95420; + background-color: #CAEEF1; color: #fff; } .navbar-default .navbar-brand { - color: #fff; + color: #333; } -</style> +</style>
\ No newline at end of file diff --git a/gui/app/views/layout/sideNav.html b/gui/app/views/layout/sideNav.html index 42dcbbc6e..4fc99cd4f 100644 --- a/gui/app/views/layout/sideNav.html +++ b/gui/app/views/layout/sideNav.html @@ -90,7 +90,7 @@ <style> .bs-sidenav { - margin-top: 40px; + margin-top: 21px; margin-bottom: 20px; width: 124px; } diff --git a/gui/app/views/layout/sideNav2.html b/gui/app/views/layout/sideNav2.html index 104a9c6cf..93e0de4be 100644 --- a/gui/app/views/layout/sideNav2.html +++ b/gui/app/views/layout/sideNav2.html @@ -66,7 +66,7 @@ <style> .bs-sidenav { - margin-top: 40px; + margin-top: 21px; margin-bottom: 20px; width: 124px; } diff --git a/gui/app/views/main.html b/gui/app/views/main.html index d5f7a3af3..36bcbbd3c 100644 --- a/gui/app/views/main.html +++ b/gui/app/views/main.html @@ -105,13 +105,13 @@ } .progressDefine>li.is-complete { - color: #e95420; + color: #4dc5cf; } .progressDefine>li.is-complete:before, .progressDefine>li.is-complete:after { color: #FFF; - background: #e95420; + background: #4dc5cf; } .progressDefine>li.is-active { diff --git a/gui/app/views/main2.html b/gui/app/views/main2.html index 661d604c6..3f49e82e0 100644 --- a/gui/app/views/main2.html +++ b/gui/app/views/main2.html @@ -105,13 +105,13 @@ } .progressDefine>li.is-complete { - color: #e95420; + color: #4dc5cf; } .progressDefine>li.is-complete:before, .progressDefine>li.is-complete:after { color: #FFF; - background: #e95420; + background: #4dc5cf; } .progressDefine>li.is-active { diff --git a/gui/app/views/modal/environmentDialog.html b/gui/app/views/modal/environmentDialog.html index a5b88d240..389de8340 100644 --- a/gui/app/views/modal/environmentDialog.html +++ b/gui/app/views/modal/environmentDialog.html @@ -104,7 +104,7 @@ Next </button> <button class="btn btn-default" ng-click="goToPodPrev()" style="margin-right:5px;float:right"> - Previous + Back </button> </h3> @@ -165,7 +165,7 @@ <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="skipPodPrev()">Back</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> @@ -234,7 +234,7 @@ <h3>{{name}} -- Container <div style="float:right"> - <button class="btn btn-default" ng-click="skipContainerPrev()">Previous</button> + <button class="btn btn-default" ng-click="skipContainerPrev()">Back</button> <button class="btn btn-default" ng-click="skipContainer()" ng-show="ifskipOrClose!=1"> Skip </button> diff --git a/gui/app/views/projectList.html b/gui/app/views/projectList.html index ea6e63d6b..6edc32fc1 100644 --- a/gui/app/views/projectList.html +++ b/gui/app/views/projectList.html @@ -11,21 +11,21 @@ <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 style="font-weight:600;margin-right:20px;">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> + <a ng-click="gotoDetail(project.uuid)" style="color:#4dc5cf"> {{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;"> + <div class="btn-group" uib-dropdown is-open="status.isopen" > <button id="single-button" type="button" class="btn btn-default btn-sm" uib-dropdown-toggle> - Modify <span class="caret"></span> + delete <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> diff --git a/gui/app/views/projectdetail.html b/gui/app/views/projectdetail.html index ff61c5fed..357a26add 100644 --- a/gui/app/views/projectdetail.html +++ b/gui/app/views/projectdetail.html @@ -24,8 +24,8 @@ </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%"> + <td width="20%"> <a ng-click="gotoDetail(task.uuid)" style="color:#4dc5cf"> {{task.name}} </a></td> + <td width="70%"> <div class="progree-parent" ng-show="task.status!=2"> <div class="progree-child" ng-style="{'width':task.stausWidth}"> </div> @@ -37,11 +37,11 @@ </td> - <td width="50%"> + <td width="10%"> - <div class="btn-group" uib-dropdown is-open="status.isopen" style="margin-right:20px;"> + <div class="btn-group" uib-dropdown is-open="status.isopen"> <button id="single-button" type="button" class="btn btn-default btn-sm" uib-dropdown-toggle> - Modify <span class="caret"></span> + 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> diff --git a/gui/app/views/suite.html b/gui/app/views/suite.html index 8e1348333..652cf1e0e 100644 --- a/gui/app/views/suite.html +++ b/gui/app/views/suite.html @@ -9,39 +9,44 @@ </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 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:20px;">Action</div> - </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}} + <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:#4dc5cf" 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> + <!-- <button class="btn btn-default btn-sm" ng-click="openDeleteEnv(suite,'test suite')">Delete</button> --> + <div class="btn-group" uib-dropdown is-open="status.isopen"> + <button id="single-button" type="button" class="btn btn-default btn-sm" uib-dropdown-toggle> + delete <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(suite,'test suite')">delete</a></li> + + </ul> + </div> + + </div> + </div> </div> - - </div> - <center> - <dir-pagination-controls></dir-pagination-controls> - </center> + <center> + <dir-pagination-controls></dir-pagination-controls> + </center> </div> @@ -146,4 +151,4 @@ background-color: #EEEEEE; border-radius: 5px; } -</style> +</style>
\ No newline at end of file diff --git a/gui/app/views/testcaselist.html b/gui/app/views/testcaselist.html index 3e8cfccf9..62237faa8 100644 --- a/gui/app/views/testcaselist.html +++ b/gui/app/views/testcaselist.html @@ -20,7 +20,7 @@ <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 style="font-weight:600;margin-right:20px;">Action</div> </div> @@ -28,14 +28,22 @@ <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)"> + <a style="color:#4dc5cf" 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> + <!-- <button class="btn btn-default btn-sm" ng-click="openDeleteEnv(test.Name,'test case')">Delete</button> --> + <div class="btn-group" uib-dropdown is-open="status.isopen" > + <button id="single-button" type="button" class="btn btn-default btn-sm" uib-dropdown-toggle> + delete <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(test.Name,'test case')">delete</a></li> + + </ul> + </div> </div> </div> diff --git a/gui/bower.json b/gui/bower.json index 6da3bee3c..d1d934f64 100644 --- a/gui/bower.json +++ b/gui/bower.json @@ -21,6 +21,9 @@ "angular-bootstrap": "^2.5.0", "angular-sanitize": "^1.6.5" }, + "resolutions": { + "angular": "~1.6.x" + }, "devDependencies": { "angular-mocks": "^1.4.0" }, diff --git a/install.sh b/install.sh index e82ae0233..8a5050a61 100755 --- a/install.sh +++ b/install.sh @@ -89,6 +89,7 @@ pip install -e . /bin/bash "${PWD}/docker/uwsgi.sh" /bin/bash "${PWD}/docker/nginx.sh" cd "${PWD}/gui" && /bin/bash gui.sh +mkdir -p /etc/nginx/yardstick mv dist /etc/nginx/yardstick/gui service nginx restart diff --git a/run_tests.sh b/run_tests.sh index 2519d94f6..2cf54c708 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -13,6 +13,9 @@ getopts ":f" FILE_OPTION +# don't write .pyc files this can cause odd unittest results +export PYTHONDONTWRITEBYTECODE=1 + run_flake8() { echo "Running flake8 ... " logfile=test_results.log diff --git a/samples/container_ping_vm.yaml b/samples/container_ping_vm.yaml new file mode 100644 index 000000000..4b7b64f68 --- /dev/null +++ b/samples/container_ping_vm.yaml @@ -0,0 +1,57 @@ +############################################################################## +# Copyright (c) 2017 Huawei AB 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 benchmark task config file +# measure network latency using ping in container + +schema: "yardstick:task:0.1" + +scenarios: +- + type: Ping + options: + packetsize: 200 + + host: host-k8s + target: target.openstack + + runner: + type: Duration + duration: 60 + interval: 1 + + sla: + max_rtt: 10 + action: monitor + +contexts: +- + type: Kubernetes + name: k8s + + servers: + host: + image: openretriever/yardstick + command: /bin/bash + args: ['-c', 'chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; service ssh restart;while true ; do sleep 10000; done'] +- + type: Heat + name: openstack + image: cirros-0.3.5 + flavor: yardstick-flavor + user: cirros + + servers: + target: + floating_ip: true + + networks: + test: + cidr: '10.0.1.0/24' diff --git a/samples/fio_volume.yaml b/samples/fio_volume.yaml new file mode 100644 index 000000000..edb3837e9 --- /dev/null +++ b/samples/fio_volume.yaml @@ -0,0 +1,74 @@ +############################################################################## +# 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 +############################################################################## +--- +# Sample benchmark task config file +# measure storage performance using fio +# +# For this sample just like running the command below on the test vm and +# getting benchmark info back to the yardstick. +# +# sudo fio -filename=/home/ubuntu/data.raw -bs=4k -ipdepth=1 -rw=rw \ +# -ramp_time=10 -runtime=60 -name=yardstick-fio -ioengine=libaio \ +# -direct=1 -group_reporting -numjobs=1 -time_based \ +# --output-format=json + +schema: "yardstick:task:0.1" + +{% set rw = rw or "randrw" %} +{% set bs = bs or "8k" %} +{% set size = size or "100g" %} +{% set rwmixwrite = rwmixwrite or "50" %} +{% set numjobs = numjobs or "1" %} +{% set direct = direct or "1" %} + +scenarios: +- + type: Fio + options: + filename: /dev/vdb + bs: {{bs}} + rw: {{rw}} + size: {{size}} + rwmixwrite: {{rwmixwrite}} + numjobs: {{numjobs}} + direct: {{direct}} + ramp_time: 10 + + host: fio.fio_volume + + runner: + type: Duration + duration: 60 + interval: 1 + + sla: + read_bw: 6000 + read_iops: 1500 + read_lat: 500.1 + write_bw: 6000 + write_iops: 1500 + write_lat: 500.1 + action: monitor + +context: + name: fio_volume + image: yardstick-image + flavor: yardstick-flavor + user: ubuntu + servers: + fio: + volume: + name: fio-volume + size: 200 + volume_mountpoint: "/dev/vdb" + floating_ip: true + networks: + test: + cidr: "10.0.1.0/24" + port_security_enabled: true diff --git a/samples/migrate-node-context.yaml b/samples/migrate-node-context.yaml new file mode 100644 index 000000000..9fe1acf78 --- /dev/null +++ b/samples/migrate-node-context.yaml @@ -0,0 +1,40 @@ +--- + +schema: "yardstick:task:0.1" + +scenarios: +- + type: QemuMigrate + options: + smp: 2 + migrate_to_port: 4444 + incoming_ip: 0 + qmp_src_path: "/tmp/qmp-sock-src" + qmp_dst_path: "/tmp/qmp-sock-dst" + max_down_time: "0.10" + host: kvm.LF + runner: + type: Duration + duration: 1 + interval: 1 + sla: + max_totaltime: 10 + max_downtime: 0.10 + max_setuptime: 0.50 + action: monitor + setup_options: + rpm_dir: "/opt/rpm" + script_dir: "/opt/scripts" + image_dir: "/opt/image" + host_setup_seqs: + - "host-setup0.sh" + - "reboot" + - "host-setup1.sh" + - "setup-ovsdpdk.sh" + - "host-install-qemu.sh" + - "host-run-qemu4lm.sh" + +context: + type: Node + name: LF + file: /root/yardstick/pod.yaml diff --git a/tests/ci/cover.sh b/tests/ci/cover.sh index 71833757a..822ed2ff2 100644 --- a/tests/ci/cover.sh +++ b/tests/ci/cover.sh @@ -34,7 +34,10 @@ run_coverage_test() { git checkout HEAD^ baseline_report=$(mktemp -t yardstick_coverageXXXXXXX) - find . -type f -name "*.pyc" -delete && python setup.py testr --coverage --testr-args="$*" + # workaround 'db type could not be determined' bug + # https://bugs.launchpad.net/testrepository/+bug/1229445 + rm -rf .testrepository + find . -type f -name "*.pyc" -delete && python setup.py testr --coverage --slowest --testr-args="$*" coverage report > $baseline_report baseline_missing=$(awk 'END { print $3 }' $baseline_report) @@ -44,7 +47,10 @@ run_coverage_test() { # Generate and save coverage report current_report=$(mktemp -t yardstick_coverageXXXXXXX) - find . -type f -name "*.pyc" -delete && python setup.py testr --coverage --testr-args="$*" + # workaround 'db type could not be determined' bug + # https://bugs.launchpad.net/testrepository/+bug/1229445 + rm -rf .testrepository + find . -type f -name "*.pyc" -delete && python setup.py testr --coverage --slowest --testr-args="$*" coverage report > $current_report current_missing=$(awk 'END { print $3 }' $current_report) diff --git a/tests/opnfv/test_cases/opnfv_yardstick_tc011.yaml b/tests/opnfv/test_cases/opnfv_yardstick_tc011.yaml index 1a3dd1489..eef1a7a62 100644 --- a/tests/opnfv/test_cases/opnfv_yardstick_tc011.yaml +++ b/tests/opnfv/test_cases/opnfv_yardstick_tc011.yaml @@ -23,6 +23,8 @@ scenarios: options: udp: udp bandwidth: 20m + length: 8K + window: 29200 host: zeus.demo target: hera.demo diff --git a/tests/opnfv/test_suites/opnfv_os-odl-sfc-ha_daily.yaml b/tests/opnfv/test_suites/opnfv_os-odl-sfc-ha_daily.yaml new file mode 100644 index 000000000..b464bfeae --- /dev/null +++ b/tests/opnfv/test_suites/opnfv_os-odl-sfc-ha_daily.yaml @@ -0,0 +1,62 @@ +############################################################################## +# 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 +############################################################################## +--- +# os-odl-sfc-ha daily task suite + +schema: "yardstick:suite:0.1" + +name: "os-odl-sfc-ha" +test_cases_dir: "tests/opnfv/test_cases/" +test_cases: +- + file_name: opnfv_yardstick_tc002.yaml +- + file_name: opnfv_yardstick_tc005.yaml +- + file_name: opnfv_yardstick_tc010.yaml +- + file_name: opnfv_yardstick_tc011.yaml +- + file_name: opnfv_yardstick_tc012.yaml +- + file_name: opnfv_yardstick_tc014.yaml +- + file_name: opnfv_yardstick_tc037.yaml +- + file_name: opnfv_yardstick_tc055.yaml + constraint: + installer: compass + pod: huawei-pod1 + task_args: + huawei-pod1: '{"file": "etc/yardstick/nodes/compass_sclab_physical/pod.yaml", + "host": "node5.yardstick-TC055"}' +- + file_name: opnfv_yardstick_tc063.yaml + constraint: + installer: compass + pod: huawei-pod1 + task_args: + huawei-pod1: '{"file": "etc/yardstick/nodes/compass_sclab_physical/pod.yaml", + "host": "node5.yardstick-TC063"}' +- + file_name: opnfv_yardstick_tc069.yaml +- + file_name: opnfv_yardstick_tc070.yaml +- + file_name: opnfv_yardstick_tc071.yaml +- + file_name: opnfv_yardstick_tc072.yaml +- + file_name: opnfv_yardstick_tc075.yaml + constraint: + installer: compass + pod: huawei-pod1 + task_args: + huawei-pod1: '{"file": "etc/yardstick/nodes/compass_sclab_physical/pod.yaml", + "host": "node1.LF"}' diff --git a/tests/unit/benchmark/contexts/test_model.py b/tests/unit/benchmark/contexts/test_model.py index 1ce550306..5444c2bc8 100644 --- a/tests/unit/benchmark/contexts/test_model.py +++ b/tests/unit/benchmark/contexts/test_model.py @@ -237,6 +237,7 @@ class ServerTestCase(unittest.TestCase): mock_network.name = 'some-network' mock_network.stack_name = 'some-network-stack' mock_network.allowed_address_pairs = ["1", "2"] + mock_network.vnic_type = 'normal' mock_network.subnet_stack_name = 'some-network-stack-subnet' mock_network.provider = 'sriov' mock_network.external_network = 'ext_net' @@ -249,6 +250,7 @@ class ServerTestCase(unittest.TestCase): 'some-server-some-network-port', mock_network.stack_name, mock_network.subnet_stack_name, + mock_network.vnic_type, sec_group_id=self.mock_context.secgroup_name, provider=mock_network.provider, allowed_address_pairs=mock_network.allowed_address_pairs) @@ -312,6 +314,7 @@ class ServerTestCase(unittest.TestCase): self.mock_context.flavors = ['flavor2'] mock_network = mock.Mock() mock_network.allowed_address_pairs = ["1", "2"] + mock_network.vnic_type = 'normal' mock_network.configure_mock(name='some-network', stack_name='some-network-stack', subnet_stack_name='some-network-stack-subnet', provider='some-provider') @@ -323,6 +326,7 @@ class ServerTestCase(unittest.TestCase): 'ServerFlavor-2-some-network-port', mock_network.stack_name, mock_network.subnet_stack_name, + mock_network.vnic_type, provider=mock_network.provider, sec_group_id=self.mock_context.secgroup_name, allowed_address_pairs=mock_network.allowed_address_pairs) diff --git a/tests/unit/benchmark/scenarios/compute/test_qemumigrate.py b/tests/unit/benchmark/scenarios/compute/test_qemumigrate.py new file mode 100644 index 000000000..9514729ba --- /dev/null +++ b/tests/unit/benchmark/scenarios/compute/test_qemumigrate.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python + +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and other. +# +# 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 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.compute.qemu_migrate.QemuMigrate + +from __future__ import absolute_import + +import unittest + +import mock +from oslo_serialization import jsonutils + +from yardstick.benchmark.scenarios.compute import qemu_migrate + + +@mock.patch('yardstick.benchmark.scenarios.compute.qemu_migrate.ssh') +class QemuMigrateTestCase(unittest.TestCase): + + def setUp(self): + self.scenario_cfg = { + "host": "kvm.LF", + "setup_options": { + "rpm_dir": "/opt/rpm", + "script_dir": "/opt/scripts", + "image_dir": "/opt/image", + "host_setup_seqs": [ + "host-setup0.sh", + "host-setup1.sh", + "setup-ovsdpdk.sh", + "host-install-qemu.sh", + "host-run-qemu4lm.sh" + ] + }, + "sla": { + "action": "monitor", + "max_totaltime": 10, + "max_downtime": 0.10, + "max_setuptime": 0.50 + }, + "options": { + "smp": 99, + "migrate_to_port": 4444, + "incoming_ip": 0, + "qmp_src_path": "/tmp/qmp-sock-src", + "qmp_dst_path": "/tmp/qmp-sock-dst", + "max_down_time": "0.10" + } + } + self.context_cfg = { + "host": { + "ip": "10.229.43.154", + "key_filename": "/yardstick/resources/files/yardstick_key", + "role": "BareMetal", + "name": "kvm.LF", + "user": "root" + } + } + + def test_qemu_migrate_successful_setup(self, mock_ssh): + + q = qemu_migrate.QemuMigrate(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + q.setup() + self.assertIsNotNone(q.host) + self.assertEqual(q.setup_done, True) + + def test_qemu_migrate_successful_no_sla(self, mock_ssh): + result = {} + self.scenario_cfg.pop("sla", None) + q = qemu_migrate.QemuMigrate(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + q.setup() + + sample_output = '{"totaltime": 15, "downtime": 2, "setuptime": 1}' + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + + q.run(result) + expected_result = jsonutils.loads(sample_output) + self.assertEqual(result, expected_result) + + def test_qemu_migrate_successful_sla(self, mock_ssh): + result = {} + self.scenario_cfg.update({"sla": { + "action": "monitor", + "max_totaltime": 15, + "max_downtime": 2, + "max_setuptime": 1 + } + }) + q = qemu_migrate.QemuMigrate(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + q.setup() + + sample_output = '{"totaltime": 15, "downtime": 2, "setuptime": 1}' + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + + q.run(result) + expected_result = jsonutils.loads(sample_output) + self.assertEqual(result, expected_result) + + def test_qemu_migrate_unsuccessful_sla_totaltime(self, mock_ssh): + + result = {} + self.scenario_cfg.update({"sla": {"max_totaltime": 10}}) + q = qemu_migrate.QemuMigrate(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + q.setup() + + sample_output = '{"totaltime": 15, "downtime": 2, "setuptime": 1}' + + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(AssertionError, q.run, result) + + def test_qemu_migrate_unsuccessful_sla_downtime(self, mock_ssh): + + result = {} + self.scenario_cfg.update({"sla": {"max_downtime": 0.10}}) + q = qemu_migrate.QemuMigrate(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + q.setup() + + sample_output = '{"totaltime": 15, "downtime": 2, "setuptime": 1}' + + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(AssertionError, q.run, result) + + def test_qemu_migrate_unsuccessful_sla_setuptime(self, mock_ssh): + + result = {} + self.scenario_cfg.update({"sla": {"max_setuptime": 0.50}}) + q = qemu_migrate.QemuMigrate(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + q.setup() + + sample_output = '{"totaltime": 15, "downtime": 2, "setuptime": 1}' + + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(AssertionError, q.run, result) + + def test_qemu_migrate_unsuccessful_script_error(self, mock_ssh): + + result = {} + self.scenario_cfg.update({"sla": {"max_totaltime": 10}}) + q = qemu_migrate.QemuMigrate(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + q.setup() + + + mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') + self.assertRaises(RuntimeError, q.run, result) + + +def main(): + unittest.main() + +if __name__ == '__main__': + main() diff --git a/tests/unit/orchestrator/test_heat.py b/tests/unit/orchestrator/test_heat.py index c127dd0c9..151070423 100644 --- a/tests/unit/orchestrator/test_heat.py +++ b/tests/unit/orchestrator/test_heat.py @@ -135,9 +135,9 @@ class HeatTemplateTestCase(unittest.TestCase): heat_template.add_subnet("subnet2", "network2", "cidr2") heat_template.add_router("router1", "gw1", "subnet1") heat_template.add_router_interface("router_if1", "router1", "subnet1") - heat_template.add_port("port1", "network1", "subnet1") - heat_template.add_port("port2", "network2", "subnet2", sec_group_id="sec_group1",provider="not-sriov") - heat_template.add_port("port3", "network2", "subnet2", sec_group_id="sec_group1",provider="sriov") + heat_template.add_port("port1", "network1", "subnet1", "normal") + heat_template.add_port("port2", "network2", "subnet2", "normal", sec_group_id="sec_group1",provider="not-sriov") + heat_template.add_port("port3", "network2", "subnet2", "normal", sec_group_id="sec_group1",provider="sriov") heat_template.add_floating_ip("floating_ip1", "network1", "port1", "router_if1") heat_template.add_floating_ip("floating_ip2", "network2", "port2", "router_if2", "foo-secgroup") heat_template.add_floating_ip_association("floating_ip1_association", "floating_ip1", "port1") diff --git a/yardstick/benchmark/contexts/model.py b/yardstick/benchmark/contexts/model.py index ec474321f..aed1a3f60 100644 --- a/yardstick/benchmark/contexts/model.py +++ b/yardstick/benchmark/contexts/model.py @@ -111,6 +111,7 @@ class Network(Object): self.segmentation_id = attrs.get('segmentation_id') self.network_type = attrs.get('network_type') self.port_security_enabled = attrs.get('port_security_enabled') + self.vnic_type = attrs.get('vnic_type', 'normal') self.allowed_address_pairs = attrs.get('allowed_address_pairs', []) try: # we require 'null' or '' to disable setting gateway_ip @@ -184,6 +185,14 @@ class Server(Object): # pragma: no cover self.placement_groups.append(pg) pg.add_member(self.stack_name) + self.volume = None + if "volume" in attrs: + self.volume = attrs.get("volume") + + self.volume_mountpoint = None + if "volume_mountpoint" in attrs: + self.volume_mountpoint = attrs.get("volume_mountpoint") + # support servergroup attr self.server_group = None sg = attrs.get("server_group") @@ -255,7 +264,8 @@ class Server(Object): # pragma: no cover # don't refactor to pass in network object, that causes JSON # circular ref encode errors template.add_port(port_name, network.stack_name, network.subnet_stack_name, - sec_group_id=sec_group_id, provider=network.provider, + network.vnic_type, sec_group_id=sec_group_id, + provider=network.provider, allowed_address_pairs=network.allowed_address_pairs) port_name_list.append(port_name) @@ -283,6 +293,17 @@ class Server(Object): # pragma: no cover else: self.flavor_name = self.flavor + if self.volume: + if isinstance(self.volume, dict): + self.volume["name"] = \ + self.volume.setdefault("name", server_name + "-volume") + template.add_volume(**self.volume) + template.add_volume_attachment(server_name, self.volume["name"], + mountpoint=self.volume_mountpoint) + else: + template.add_volume_attachment(server_name, self.volume, + mountpoint=self.volume_mountpoint) + template.add_server(server_name, self.image, flavor=self.flavor_name, flavors=self.context.flavors, ports=port_name_list, diff --git a/yardstick/benchmark/scenarios/compute/qemu_migrate.py b/yardstick/benchmark/scenarios/compute/qemu_migrate.py new file mode 100644 index 000000000..cee87a545 --- /dev/null +++ b/yardstick/benchmark/scenarios/compute/qemu_migrate.py @@ -0,0 +1,155 @@ +from __future__ import absolute_import +from __future__ import print_function + +import logging +import os +import re +import time + + +import pkg_resources +from oslo_serialization import jsonutils + +import yardstick.ssh as ssh +from yardstick.benchmark.scenarios import base + +LOG = logging.getLogger(__name__) +LOG.setLevel(logging.DEBUG) + + +class QemuMigrate(base.Scenario): + """ + Execute a live migration for two host using qemu + + """ + + __scenario_type__ = "QemuMigrate" + + TARGET_SCRIPT = "qemu_migrate_benchmark.bash" + WORKSPACE = "/root/workspace" + REBOOT_CMD_PATTERN = r";\s*reboot\b" + + def __init__(self, scenario_cfg, context_cfg): + self.scenario_cfg = scenario_cfg + self.context_cfg = context_cfg + self.setup_done = False + + def _connect_host(self): + host = self.context_cfg["host"] + self.host = ssh.SSH.from_node(host, defaults={"user": "root"}) + self.host.wait(timeout=600) + + def _put_files(self, client): + setup_options = self.scenario_cfg["setup_options"] + script_dir = setup_options["script_dir"] + LOG.debug("Send scripts from %s to workspace %s", + script_dir, self.WORKSPACE) + client.put(script_dir, self.WORKSPACE, recursive=True) + + def _run_setup_cmd(self, client, cmd): + LOG.debug("Run cmd: %s", cmd) + status, stdout, stderr = client.execute(cmd) + if status: + if re.search(self.REBOOT_CMD_PATTERN, cmd): + LOG.debug("Error on reboot") + else: + raise RuntimeError(stderr) + + def _run_host_setup_scripts(self, scripts): + setup_options = self.scenario_cfg["setup_options"] + script_dir = os.path.basename(setup_options["script_dir"]) + + for script in scripts: + cmd = "cd %s/%s; export PATH=./:$PATH; %s" %\ + (self.WORKSPACE, script_dir, script) + self._run_setup_cmd(self.host, cmd) + + if re.search(self.REBOOT_CMD_PATTERN, cmd): + time.sleep(3) + self._connect_host() + + def setup(self): + """scenario setup""" + setup_options = self.scenario_cfg["setup_options"] + host_setup_seqs = setup_options["host_setup_seqs"] + + self._connect_host() + self._put_files(self.host) + self._run_host_setup_scripts(host_setup_seqs) + + # copy script to host + self.target_script = pkg_resources.resource_filename( + "yardstick.benchmark.scenarios.compute", + QemuMigrate.TARGET_SCRIPT) + self.host.put_file(self.target_script, "~/qemu_migrate_benchmark.sh") + + self.setup_done = True + + def run(self, result): + """execute the benchmark""" + + options = self.scenario_cfg["options"] + smp = options.get("smp", 2) + qmp_sock_src = options.get("qmp_src_path", "/tmp/qmp-sock-src") + qmp_sock_dst = options.get("qmp_dst_path", "/tmp/qmp-sock-dst") + incoming_ip = options.get("incoming_ip", 0) + migrate_to_port = options.get("migrate_to_port", 4444) + max_down_time = options.get("max_down_time", 0.10) + cmd_args = " %s %s %s %s %s %s" %\ + (smp, qmp_sock_src, qmp_sock_dst, incoming_ip, + migrate_to_port, max_down_time) + cmd = "bash migrate_benchmark.sh %s" % (cmd_args) + LOG.debug("Executing command: %s", cmd) + status, stdout, stderr = self.host.execute(cmd) + if status: + raise RuntimeError(stderr) + + result.update(jsonutils.loads(stdout)) + + if "sla" in self.scenario_cfg: + sla_error = "" + for t, timevalue in result.items(): + if 'max_%s' % t not in self.scenario_cfg['sla']: + continue + + sla_time = int(self.scenario_cfg['sla'][ + 'max_%s' % t]) + timevalue = int(timevalue) + if timevalue > sla_time: + sla_error += "%s timevalue %d > sla:max_%s(%d); " % \ + (t, timevalue, t, sla_time) + assert sla_error == "", sla_error + + +def _test(): # pragma: no cover + """internal test function""" + key_filename = pkg_resources.resource_filename("yardstick.resources", + "files/yardstick_key") + ctx = { + "host": { + "ip": "10.229.47.137", + "user": "root", + "key_filename": key_filename + } + } + + logger = logging.getLogger("yardstick") + logger.setLevel(logging.DEBUG) + options = { + "smp": 2, + "migrate_to_port": 4444, + "incoming_ip": 0, + "qmp_sock_src": "/tmp/qmp-sock-src", + "qmp_sock_dst": "/tmp/qmp-sock-dst", + "max_down_time": 0.10 + } + args = { + "options": options + } + result = {} + migrate = QemuMigrate(args, ctx) + migrate.run(result) + print(result) + +if __name__ == '__main__': # pragma: no cover + _test() diff --git a/yardstick/benchmark/scenarios/compute/qemu_migrate_benchmark.bash b/yardstick/benchmark/scenarios/compute/qemu_migrate_benchmark.bash new file mode 100644 index 000000000..552098103 --- /dev/null +++ b/yardstick/benchmark/scenarios/compute/qemu_migrate_benchmark.bash @@ -0,0 +1,68 @@ +#!/bin/bash + +############################################################################# +#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 +############################################################################## + +set -e + +# Commandline arguments + +src=$2 +dst_ip=$4 +migrate_to_port=$5 +max_down_time=$6 + +OUTPUT_FILE=/tmp/output-qemu.log + +do_migrate() +{ +# local src=`echo $OPTIONS | cut -d ':' -f 2 | cut -d ',' -f 1` + echo "info status" | nc -U $src + # with no speed limit + echo "migrate_set_speed 0" |nc -U $src + # set the expected max downtime + echo "migrate_set_downtime ${max_down_time}" |nc -U $src + # start live migration + echo "migrate -d tcp:${dst_ip}:$migrate_to_port" |nc -U $src + # wait until live migration completed + status="" + while [ "${status}" == "" ] + do + status=`echo "info migrate" | nc -U $src |grep completed | cut -d: -f2` + echo ${status} + sleep 1; + done +} >/dev/null + +output_qemu() +{ + # print detail information + echo "info migrate" | nc -U $src + echo "quit" | nc -U $src + sleep 5 + +} > $OUTPUT_FILE + +output_json() +{ +totaltime=$(grep "total time" $OUTPUT_FILE | cut -d' ' -f3) +downtime=$(grep "downtime" $OUTPUT_FILE | cut -d' ' -f2) +setuptime=$(grep "setup" $OUTPUT_FILE | cut -d' ' -f2) +echo -e "{ \ + \"totaltime\":\"$totaltime\", \ + \"downtime\":\"$downtime\", \ + \"setuptime\":\"$setuptime\" \ + }" +} +# main entry +main() +{ + do_migrate +} +main diff --git a/yardstick/cmd/NSBperf.py b/yardstick/cmd/NSBperf.py index 4e7590ea5..2dc0f65e7 100755 --- a/yardstick/cmd/NSBperf.py +++ b/yardstick/cmd/NSBperf.py @@ -30,13 +30,6 @@ from six.moves import input CLI_PATH = os.path.dirname(os.path.realpath(__file__)) REPO_PATH = os.path.abspath(os.path.join(CLI_PATH, os.pardir)) -PYTHONPATH = os.environ.get("PYTHONPATH", False) -VIRTUAL_ENV = os.environ.get("VIRTUAL_ENV", False) - - -if not PYTHONPATH or not VIRTUAL_ENV: - print("Please setup env PYTHONPATH & VIRTUAL_ENV environment varaible.") - raise SystemExit(1) def sigint_handler(*args, **kwargs): diff --git a/yardstick/cmd/commands/task.py b/yardstick/cmd/commands/task.py index 03f6b1b1e..8d8ea2b3c 100644 --- a/yardstick/cmd/commands/task.py +++ b/yardstick/cmd/commands/task.py @@ -51,11 +51,17 @@ class TaskCommands(object): # pragma: no cover self.output_file = param.output_file try: - Task().start(param, **kwargs) + result = Task().start(param, **kwargs) except Exception as e: self._write_error_data(e) LOG.exception("") + if result.get('result', {}).get('criteria') == 'PASS': + LOG.info('Task Success') + else: + LOG.info('Task Failed') + raise RuntimeError('Task Failed') + def _write_error_data(self, error): data = {'status': 2, 'result': str(error)} write_json_to_file(self.output_file, data) diff --git a/yardstick/common/openstack_utils.py b/yardstick/common/openstack_utils.py index 8787e605a..f027b7922 100644 --- a/yardstick/common/openstack_utils.py +++ b/yardstick/common/openstack_utils.py @@ -15,6 +15,7 @@ import logging from keystoneauth1 import loading from keystoneauth1 import session +from cinderclient import client as cinderclient from novaclient import client as novaclient from glanceclient import client as glanceclient from neutronclient.neutron import client as neutronclient @@ -108,6 +109,21 @@ def get_heat_api_version(): # pragma: no cover return api_version +def get_cinder_client_version(): # pragma: no cover + try: + api_version = os.environ['OS_VOLUME_API_VERSION'] + except KeyError: + return DEFAULT_API_VERSION + else: + log.info("OS_VOLUME_API_VERSION is set in env as '%s'", api_version) + return api_version + + +def get_cinder_client(): # pragma: no cover + sess = get_session() + return cinderclient.Client(get_cinder_client_version(), session=sess) + + def get_nova_client_version(): # pragma: no cover try: api_version = os.environ['OS_COMPUTE_API_VERSION'] @@ -430,3 +446,11 @@ def get_port_id_by_ip(neutron_client, ip_address): # pragma: no cover def get_image_id(glance_client, image_name): # pragma: no cover images = glance_client.images.list() return next((i.id for i in images if i.name == image_name), None) + + +# ********************************************* +# CINDER +# ********************************************* +def get_volume_id(volume_name): # pragma: no cover + volumes = get_cinder_client().volumes.list() + return next((v.id for v in volumes if v.name == volume_name), None) diff --git a/yardstick/orchestrator/heat.py b/yardstick/orchestrator/heat.py index 95ca0ad2e..beb63b421 100644 --- a/yardstick/orchestrator/heat.py +++ b/yardstick/orchestrator/heat.py @@ -230,6 +230,40 @@ name (i.e. %s).\ 'value': {'get_resource': name} } + def add_volume(self, name, size=10): + """add to the template a volume description""" + log.debug("adding Cinder::Volume '%s' size '%d' ", name, size) + + self.resources[name] = { + 'type': 'OS::Cinder::Volume', + 'properties': {'name': name, + 'size': size} + } + + self._template['outputs'][name] = { + 'description': 'Volume %s ID' % name, + 'value': {'get_resource': name} + } + + def add_volume_attachment(self, server_name, volume_name, mountpoint=None): + """add to the template an association of volume to instance""" + log.debug("adding Cinder::VolumeAttachment server '%s' volume '%s' ", server_name, + volume_name) + + name = "%s-%s" % (server_name, volume_name) + + volume_id = op_utils.get_volume_id(volume_name) + if not volume_id: + volume_id = {'get_resource': volume_name} + self.resources[name] = { + 'type': 'OS::Cinder::VolumeAttachment', + 'properties': {'instance_uuid': {'get_resource': server_name}, + 'volume_id': volume_id} + } + + if mountpoint: + self.resources[name]['properties']['mountpoint'] = mountpoint + def add_network(self, name, physical_network='physnet1', provider=None, segmentation_id=None, port_security_enabled=None): """add to the template a Neutron Net""" @@ -325,17 +359,18 @@ name (i.e. %s).\ } } - def add_port(self, name, network_name, subnet_name, sec_group_id=None, provider=None, - allowed_address_pairs=None): + def add_port(self, name, network_name, subnet_name, vnic_type, sec_group_id=None, + provider=None, allowed_address_pairs=None): """add to the template a named Neutron Port """ - log.debug("adding Neutron::Port '%s', network:'%s', subnet:'%s', " - "secgroup:%s", name, network_name, subnet_name, sec_group_id) + log.debug("adding Neutron::Port '%s', network:'%s', subnet:'%s', vnic_type:'%s', " + "secgroup:%s", name, network_name, subnet_name, vnic_type, sec_group_id) self.resources[name] = { 'type': 'OS::Neutron::Port', 'depends_on': [subnet_name], 'properties': { 'name': name, + 'binding:vnic_type': vnic_type, 'fixed_ips': [{'subnet': {'get_resource': subnet_name}}], 'network_id': {'get_resource': network_name}, 'replacement_policy': 'AUTO', |