From c850beb64083c730301188ade4cbba0f2a329a92 Mon Sep 17 00:00:00 2001 From: Morgan Richomme Date: Wed, 16 Nov 2016 18:16:43 +0100 Subject: [PATCH] Refactor reporting dir - dockerization - create util function for all the test projects - create a common config files - add unit tests - pep8 JIRA: RELENG-162 Change-Id: Ib209be4233084100ff238a7aeb2843ccc24a6f1e Signed-off-by: Morgan Richomme --- .../{assets => 3rd_party}/css/font-awesome.min.css | 0 .../reporting/{assets => 3rd_party}/css/ie8.css | 0 .../reporting/{assets => 3rd_party}/css/ie9.css | 0 .../reporting/{assets => 3rd_party}/css/main.css | 0 .../{assets => 3rd_party}/fonts/FontAwesome.otf | Bin .../fonts/fontawesome-webfont.eot | Bin .../fonts/fontawesome-webfont.svg | 0 .../fonts/fontawesome-webfont.ttf | Bin .../fonts/fontawesome-webfont.woff | Bin .../fonts/fontawesome-webfont.woff2 | Bin .../{assets => 3rd_party}/js/ie/html5shiv.js | 0 .../{assets => 3rd_party}/js/ie/respond.min.js | 0 .../{assets => 3rd_party}/js/jquery.min.js | 0 .../reporting/{assets => 3rd_party}/js/main.js | 0 .../reporting/{assets => 3rd_party}/js/skel.min.js | 0 .../reporting/{assets => 3rd_party}/js/util.js | 0 .../{assets => 3rd_party}/sass/base/_page.scss | 0 .../sass/base/_typography.scss | 0 .../sass/components/_box.scss | 0 .../sass/components/_button.scss | 0 .../sass/components/_form.scss | 0 .../sass/components/_icon.scss | 0 .../sass/components/_image.scss | 0 .../sass/components/_list.scss | 0 .../sass/components/_section.scss | 0 .../sass/components/_table.scss | 0 .../sass/components/_tiles.scss | 0 .../reporting/{assets => 3rd_party}/sass/ie8.scss | 0 .../reporting/{assets => 3rd_party}/sass/ie9.scss | 0 .../{assets => 3rd_party}/sass/layout/_footer.scss | 0 .../{assets => 3rd_party}/sass/layout/_header.scss | 0 .../{assets => 3rd_party}/sass/layout/_main.scss | 0 .../{assets => 3rd_party}/sass/layout/_menu.scss | 0 .../sass/layout/_wrapper.scss | 0 .../sass/libs/_functions.scss | 0 .../{assets => 3rd_party}/sass/libs/_mixins.scss | 0 .../{assets => 3rd_party}/sass/libs/_skel.scss | 0 .../{assets => 3rd_party}/sass/libs/_vars.scss | 0 .../reporting/{assets => 3rd_party}/sass/main.scss | 0 utils/test/reporting/docker/Dockerfile | 43 +++ utils/test/reporting/docker/reporting.sh | 65 ++++ utils/test/reporting/docker/requirements.pip | 12 + utils/test/reporting/functest/reporting-status.py | 118 ++++--- utils/test/reporting/functest/reporting-tempest.py | 37 ++- utils/test/reporting/functest/reporting-vims.py | 35 +- utils/test/reporting/functest/reportingConf.py | 26 -- utils/test/reporting/functest/reportingUtils.py | 186 ----------- .../functest/template/index-status-tmpl.html | 26 +- .../functest/template/index-tempest-tmpl.html | 12 +- .../functest/template/index-vims-tmpl.html | 12 +- utils/test/reporting/functest/testCase.py | 4 +- utils/test/reporting/{ => html}/colorado.html | 26 +- utils/test/reporting/{ => html}/danube.html | 26 +- utils/test/reporting/{ => html}/elements.html | 46 +-- .../reporting/{ => html}/functest-colorado.html | 32 +- .../test/reporting/{ => html}/functest-master.html | 32 +- utils/test/reporting/{ => html}/generic.html | 24 +- utils/test/reporting/{ => html}/index.html | 24 +- utils/test/reporting/{images => img}/colorado.jpg | Bin utils/test/reporting/{images => img}/danube.jpg | Bin utils/test/reporting/{images => img}/functest.jpg | Bin .../reporting/{images => img}/functest.jpg.old | Bin utils/test/reporting/img/gauge_0.png | Bin 0 -> 3644 bytes utils/test/reporting/img/gauge_100.png | Bin 0 -> 3191 bytes utils/test/reporting/img/gauge_16.7.png | Bin 0 -> 3170 bytes utils/test/reporting/img/gauge_25.png | Bin 0 -> 3108 bytes utils/test/reporting/img/gauge_33.3.png | Bin 0 -> 3081 bytes utils/test/reporting/img/gauge_41.7.png | Bin 0 -> 3169 bytes utils/test/reporting/img/gauge_50.png | Bin 0 -> 3123 bytes utils/test/reporting/img/gauge_58.3.png | Bin 0 -> 3161 bytes utils/test/reporting/img/gauge_66.7.png | Bin 0 -> 3069 bytes utils/test/reporting/img/gauge_75.png | Bin 0 -> 3030 bytes utils/test/reporting/img/gauge_8.3.png | Bin 0 -> 2993 bytes utils/test/reporting/img/gauge_83.3.png | Bin 0 -> 3122 bytes utils/test/reporting/img/gauge_91.7.png | Bin 0 -> 3008 bytes utils/test/reporting/img/icon-nok.png | Bin 0 -> 2317 bytes utils/test/reporting/img/icon-ok.png | Bin 0 -> 4063 bytes utils/test/reporting/{images => img}/logo.svg | 0 utils/test/reporting/{images => img}/pic01.jpg | Bin utils/test/reporting/{images => img}/pic02.jpg | Bin utils/test/reporting/{images => img}/pic03.jpg | Bin utils/test/reporting/{images => img}/pic04.jpg | Bin utils/test/reporting/{images => img}/pic05.jpg | Bin utils/test/reporting/{images => img}/pic06.jpg | Bin utils/test/reporting/{images => img}/pic07.jpg | Bin utils/test/reporting/{images => img}/pic08.jpg | Bin utils/test/reporting/{images => img}/pic09.jpg | Bin utils/test/reporting/{images => img}/pic10.jpg | Bin utils/test/reporting/{images => img}/pic11.jpg | Bin utils/test/reporting/{images => img}/pic12.jpg | Bin utils/test/reporting/{images => img}/pic13.jpg | Bin utils/test/reporting/{images => img}/pic14.jpg | Bin utils/test/reporting/{images => img}/pic15.jpg | Bin utils/test/reporting/img/weather-clear.png | Bin 0 -> 1560 bytes utils/test/reporting/img/weather-few-clouds.png | Bin 0 -> 1927 bytes utils/test/reporting/img/weather-overcast.png | Bin 0 -> 1588 bytes utils/test/reporting/img/weather-storm.png | Bin 0 -> 2137 bytes utils/test/reporting/{images => img}/yardstick.jpg | Bin utils/test/reporting/reporting.yaml | 49 +++ utils/test/reporting/run_unit_tests.sh | 43 +++ utils/test/reporting/setup.py | 22 ++ utils/test/reporting/tests/__init__.py | 0 utils/test/reporting/tests/unit/__init__.py | 0 utils/test/reporting/tests/unit/utils/__init__.py | 0 .../test/reporting/tests/unit/utils/test_utils.py | 29 ++ utils/test/reporting/utils/__init__.py | 0 utils/test/reporting/utils/reporting_utils.py | 355 +++++++++++++++++++++ utils/test/reporting/yardstick/reporting-status.py | 52 +-- utils/test/reporting/yardstick/reportingConf.py | 29 -- utils/test/reporting/yardstick/reportingUtils.py | 115 ------- utils/test/reporting/yardstick/scenarios.py | 19 +- .../yardstick/template/index-status-tmpl.html | 9 +- 112 files changed, 897 insertions(+), 611 deletions(-) rename utils/test/reporting/{assets => 3rd_party}/css/font-awesome.min.css (100%) rename utils/test/reporting/{assets => 3rd_party}/css/ie8.css (100%) rename utils/test/reporting/{assets => 3rd_party}/css/ie9.css (100%) rename utils/test/reporting/{assets => 3rd_party}/css/main.css (100%) rename utils/test/reporting/{assets => 3rd_party}/fonts/FontAwesome.otf (100%) rename utils/test/reporting/{assets => 3rd_party}/fonts/fontawesome-webfont.eot (100%) rename utils/test/reporting/{assets => 3rd_party}/fonts/fontawesome-webfont.svg (100%) rename utils/test/reporting/{assets => 3rd_party}/fonts/fontawesome-webfont.ttf (100%) rename utils/test/reporting/{assets => 3rd_party}/fonts/fontawesome-webfont.woff (100%) rename utils/test/reporting/{assets => 3rd_party}/fonts/fontawesome-webfont.woff2 (100%) rename utils/test/reporting/{assets => 3rd_party}/js/ie/html5shiv.js (100%) rename utils/test/reporting/{assets => 3rd_party}/js/ie/respond.min.js (100%) rename utils/test/reporting/{assets => 3rd_party}/js/jquery.min.js (100%) rename utils/test/reporting/{assets => 3rd_party}/js/main.js (100%) rename utils/test/reporting/{assets => 3rd_party}/js/skel.min.js (100%) rename utils/test/reporting/{assets => 3rd_party}/js/util.js (100%) rename utils/test/reporting/{assets => 3rd_party}/sass/base/_page.scss (100%) rename utils/test/reporting/{assets => 3rd_party}/sass/base/_typography.scss (100%) rename utils/test/reporting/{assets => 3rd_party}/sass/components/_box.scss (100%) rename utils/test/reporting/{assets => 3rd_party}/sass/components/_button.scss (100%) rename utils/test/reporting/{assets => 3rd_party}/sass/components/_form.scss (100%) rename utils/test/reporting/{assets => 3rd_party}/sass/components/_icon.scss (100%) rename utils/test/reporting/{assets => 3rd_party}/sass/components/_image.scss (100%) rename utils/test/reporting/{assets => 3rd_party}/sass/components/_list.scss (100%) rename utils/test/reporting/{assets => 3rd_party}/sass/components/_section.scss (100%) rename utils/test/reporting/{assets => 3rd_party}/sass/components/_table.scss (100%) rename utils/test/reporting/{assets => 3rd_party}/sass/components/_tiles.scss (100%) rename utils/test/reporting/{assets => 3rd_party}/sass/ie8.scss (100%) rename utils/test/reporting/{assets => 3rd_party}/sass/ie9.scss (100%) rename utils/test/reporting/{assets => 3rd_party}/sass/layout/_footer.scss (100%) rename utils/test/reporting/{assets => 3rd_party}/sass/layout/_header.scss (100%) rename utils/test/reporting/{assets => 3rd_party}/sass/layout/_main.scss (100%) rename utils/test/reporting/{assets => 3rd_party}/sass/layout/_menu.scss (100%) rename utils/test/reporting/{assets => 3rd_party}/sass/layout/_wrapper.scss (100%) rename utils/test/reporting/{assets => 3rd_party}/sass/libs/_functions.scss (100%) rename utils/test/reporting/{assets => 3rd_party}/sass/libs/_mixins.scss (100%) rename utils/test/reporting/{assets => 3rd_party}/sass/libs/_skel.scss (100%) rename utils/test/reporting/{assets => 3rd_party}/sass/libs/_vars.scss (100%) rename utils/test/reporting/{assets => 3rd_party}/sass/main.scss (100%) create mode 100644 utils/test/reporting/docker/Dockerfile create mode 100755 utils/test/reporting/docker/reporting.sh create mode 100644 utils/test/reporting/docker/requirements.pip delete mode 100644 utils/test/reporting/functest/reportingConf.py delete mode 100644 utils/test/reporting/functest/reportingUtils.py rename utils/test/reporting/{ => html}/colorado.html (72%) rename utils/test/reporting/{ => html}/danube.html (72%) rename utils/test/reporting/{ => html}/elements.html (76%) rename utils/test/reporting/{ => html}/functest-colorado.html (68%) rename utils/test/reporting/{ => html}/functest-master.html (68%) rename utils/test/reporting/{ => html}/generic.html (83%) rename utils/test/reporting/{ => html}/index.html (74%) rename utils/test/reporting/{images => img}/colorado.jpg (100%) rename utils/test/reporting/{images => img}/danube.jpg (100%) rename utils/test/reporting/{images => img}/functest.jpg (100%) rename utils/test/reporting/{images => img}/functest.jpg.old (100%) create mode 100644 utils/test/reporting/img/gauge_0.png create mode 100644 utils/test/reporting/img/gauge_100.png create mode 100644 utils/test/reporting/img/gauge_16.7.png create mode 100644 utils/test/reporting/img/gauge_25.png create mode 100644 utils/test/reporting/img/gauge_33.3.png create mode 100644 utils/test/reporting/img/gauge_41.7.png create mode 100644 utils/test/reporting/img/gauge_50.png create mode 100644 utils/test/reporting/img/gauge_58.3.png create mode 100644 utils/test/reporting/img/gauge_66.7.png create mode 100644 utils/test/reporting/img/gauge_75.png create mode 100644 utils/test/reporting/img/gauge_8.3.png create mode 100644 utils/test/reporting/img/gauge_83.3.png create mode 100644 utils/test/reporting/img/gauge_91.7.png create mode 100644 utils/test/reporting/img/icon-nok.png create mode 100644 utils/test/reporting/img/icon-ok.png rename utils/test/reporting/{images => img}/logo.svg (100%) rename utils/test/reporting/{images => img}/pic01.jpg (100%) rename utils/test/reporting/{images => img}/pic02.jpg (100%) rename utils/test/reporting/{images => img}/pic03.jpg (100%) rename utils/test/reporting/{images => img}/pic04.jpg (100%) rename utils/test/reporting/{images => img}/pic05.jpg (100%) rename utils/test/reporting/{images => img}/pic06.jpg (100%) rename utils/test/reporting/{images => img}/pic07.jpg (100%) rename utils/test/reporting/{images => img}/pic08.jpg (100%) rename utils/test/reporting/{images => img}/pic09.jpg (100%) rename utils/test/reporting/{images => img}/pic10.jpg (100%) rename utils/test/reporting/{images => img}/pic11.jpg (100%) rename utils/test/reporting/{images => img}/pic12.jpg (100%) rename utils/test/reporting/{images => img}/pic13.jpg (100%) rename utils/test/reporting/{images => img}/pic14.jpg (100%) rename utils/test/reporting/{images => img}/pic15.jpg (100%) create mode 100644 utils/test/reporting/img/weather-clear.png create mode 100644 utils/test/reporting/img/weather-few-clouds.png create mode 100644 utils/test/reporting/img/weather-overcast.png create mode 100644 utils/test/reporting/img/weather-storm.png rename utils/test/reporting/{images => img}/yardstick.jpg (100%) create mode 100644 utils/test/reporting/reporting.yaml create mode 100755 utils/test/reporting/run_unit_tests.sh create mode 100644 utils/test/reporting/setup.py create mode 100644 utils/test/reporting/tests/__init__.py create mode 100644 utils/test/reporting/tests/unit/__init__.py create mode 100644 utils/test/reporting/tests/unit/utils/__init__.py create mode 100644 utils/test/reporting/tests/unit/utils/test_utils.py create mode 100644 utils/test/reporting/utils/__init__.py create mode 100644 utils/test/reporting/utils/reporting_utils.py delete mode 100644 utils/test/reporting/yardstick/reportingConf.py delete mode 100644 utils/test/reporting/yardstick/reportingUtils.py diff --git a/utils/test/reporting/assets/css/font-awesome.min.css b/utils/test/reporting/3rd_party/css/font-awesome.min.css similarity index 100% rename from utils/test/reporting/assets/css/font-awesome.min.css rename to utils/test/reporting/3rd_party/css/font-awesome.min.css diff --git a/utils/test/reporting/assets/css/ie8.css b/utils/test/reporting/3rd_party/css/ie8.css similarity index 100% rename from utils/test/reporting/assets/css/ie8.css rename to utils/test/reporting/3rd_party/css/ie8.css diff --git a/utils/test/reporting/assets/css/ie9.css b/utils/test/reporting/3rd_party/css/ie9.css similarity index 100% rename from utils/test/reporting/assets/css/ie9.css rename to utils/test/reporting/3rd_party/css/ie9.css diff --git a/utils/test/reporting/assets/css/main.css b/utils/test/reporting/3rd_party/css/main.css similarity index 100% rename from utils/test/reporting/assets/css/main.css rename to utils/test/reporting/3rd_party/css/main.css diff --git a/utils/test/reporting/assets/fonts/FontAwesome.otf b/utils/test/reporting/3rd_party/fonts/FontAwesome.otf similarity index 100% rename from utils/test/reporting/assets/fonts/FontAwesome.otf rename to utils/test/reporting/3rd_party/fonts/FontAwesome.otf diff --git a/utils/test/reporting/assets/fonts/fontawesome-webfont.eot b/utils/test/reporting/3rd_party/fonts/fontawesome-webfont.eot similarity index 100% rename from utils/test/reporting/assets/fonts/fontawesome-webfont.eot rename to utils/test/reporting/3rd_party/fonts/fontawesome-webfont.eot diff --git a/utils/test/reporting/assets/fonts/fontawesome-webfont.svg b/utils/test/reporting/3rd_party/fonts/fontawesome-webfont.svg similarity index 100% rename from utils/test/reporting/assets/fonts/fontawesome-webfont.svg rename to utils/test/reporting/3rd_party/fonts/fontawesome-webfont.svg diff --git a/utils/test/reporting/assets/fonts/fontawesome-webfont.ttf b/utils/test/reporting/3rd_party/fonts/fontawesome-webfont.ttf similarity index 100% rename from utils/test/reporting/assets/fonts/fontawesome-webfont.ttf rename to utils/test/reporting/3rd_party/fonts/fontawesome-webfont.ttf diff --git a/utils/test/reporting/assets/fonts/fontawesome-webfont.woff b/utils/test/reporting/3rd_party/fonts/fontawesome-webfont.woff similarity index 100% rename from utils/test/reporting/assets/fonts/fontawesome-webfont.woff rename to utils/test/reporting/3rd_party/fonts/fontawesome-webfont.woff diff --git a/utils/test/reporting/assets/fonts/fontawesome-webfont.woff2 b/utils/test/reporting/3rd_party/fonts/fontawesome-webfont.woff2 similarity index 100% rename from utils/test/reporting/assets/fonts/fontawesome-webfont.woff2 rename to utils/test/reporting/3rd_party/fonts/fontawesome-webfont.woff2 diff --git a/utils/test/reporting/assets/js/ie/html5shiv.js b/utils/test/reporting/3rd_party/js/ie/html5shiv.js similarity index 100% rename from utils/test/reporting/assets/js/ie/html5shiv.js rename to utils/test/reporting/3rd_party/js/ie/html5shiv.js diff --git a/utils/test/reporting/assets/js/ie/respond.min.js b/utils/test/reporting/3rd_party/js/ie/respond.min.js similarity index 100% rename from utils/test/reporting/assets/js/ie/respond.min.js rename to utils/test/reporting/3rd_party/js/ie/respond.min.js diff --git a/utils/test/reporting/assets/js/jquery.min.js b/utils/test/reporting/3rd_party/js/jquery.min.js similarity index 100% rename from utils/test/reporting/assets/js/jquery.min.js rename to utils/test/reporting/3rd_party/js/jquery.min.js diff --git a/utils/test/reporting/assets/js/main.js b/utils/test/reporting/3rd_party/js/main.js similarity index 100% rename from utils/test/reporting/assets/js/main.js rename to utils/test/reporting/3rd_party/js/main.js diff --git a/utils/test/reporting/assets/js/skel.min.js b/utils/test/reporting/3rd_party/js/skel.min.js similarity index 100% rename from utils/test/reporting/assets/js/skel.min.js rename to utils/test/reporting/3rd_party/js/skel.min.js diff --git a/utils/test/reporting/assets/js/util.js b/utils/test/reporting/3rd_party/js/util.js similarity index 100% rename from utils/test/reporting/assets/js/util.js rename to utils/test/reporting/3rd_party/js/util.js diff --git a/utils/test/reporting/assets/sass/base/_page.scss b/utils/test/reporting/3rd_party/sass/base/_page.scss similarity index 100% rename from utils/test/reporting/assets/sass/base/_page.scss rename to utils/test/reporting/3rd_party/sass/base/_page.scss diff --git a/utils/test/reporting/assets/sass/base/_typography.scss b/utils/test/reporting/3rd_party/sass/base/_typography.scss similarity index 100% rename from utils/test/reporting/assets/sass/base/_typography.scss rename to utils/test/reporting/3rd_party/sass/base/_typography.scss diff --git a/utils/test/reporting/assets/sass/components/_box.scss b/utils/test/reporting/3rd_party/sass/components/_box.scss similarity index 100% rename from utils/test/reporting/assets/sass/components/_box.scss rename to utils/test/reporting/3rd_party/sass/components/_box.scss diff --git a/utils/test/reporting/assets/sass/components/_button.scss b/utils/test/reporting/3rd_party/sass/components/_button.scss similarity index 100% rename from utils/test/reporting/assets/sass/components/_button.scss rename to utils/test/reporting/3rd_party/sass/components/_button.scss diff --git a/utils/test/reporting/assets/sass/components/_form.scss b/utils/test/reporting/3rd_party/sass/components/_form.scss similarity index 100% rename from utils/test/reporting/assets/sass/components/_form.scss rename to utils/test/reporting/3rd_party/sass/components/_form.scss diff --git a/utils/test/reporting/assets/sass/components/_icon.scss b/utils/test/reporting/3rd_party/sass/components/_icon.scss similarity index 100% rename from utils/test/reporting/assets/sass/components/_icon.scss rename to utils/test/reporting/3rd_party/sass/components/_icon.scss diff --git a/utils/test/reporting/assets/sass/components/_image.scss b/utils/test/reporting/3rd_party/sass/components/_image.scss similarity index 100% rename from utils/test/reporting/assets/sass/components/_image.scss rename to utils/test/reporting/3rd_party/sass/components/_image.scss diff --git a/utils/test/reporting/assets/sass/components/_list.scss b/utils/test/reporting/3rd_party/sass/components/_list.scss similarity index 100% rename from utils/test/reporting/assets/sass/components/_list.scss rename to utils/test/reporting/3rd_party/sass/components/_list.scss diff --git a/utils/test/reporting/assets/sass/components/_section.scss b/utils/test/reporting/3rd_party/sass/components/_section.scss similarity index 100% rename from utils/test/reporting/assets/sass/components/_section.scss rename to utils/test/reporting/3rd_party/sass/components/_section.scss diff --git a/utils/test/reporting/assets/sass/components/_table.scss b/utils/test/reporting/3rd_party/sass/components/_table.scss similarity index 100% rename from utils/test/reporting/assets/sass/components/_table.scss rename to utils/test/reporting/3rd_party/sass/components/_table.scss diff --git a/utils/test/reporting/assets/sass/components/_tiles.scss b/utils/test/reporting/3rd_party/sass/components/_tiles.scss similarity index 100% rename from utils/test/reporting/assets/sass/components/_tiles.scss rename to utils/test/reporting/3rd_party/sass/components/_tiles.scss diff --git a/utils/test/reporting/assets/sass/ie8.scss b/utils/test/reporting/3rd_party/sass/ie8.scss similarity index 100% rename from utils/test/reporting/assets/sass/ie8.scss rename to utils/test/reporting/3rd_party/sass/ie8.scss diff --git a/utils/test/reporting/assets/sass/ie9.scss b/utils/test/reporting/3rd_party/sass/ie9.scss similarity index 100% rename from utils/test/reporting/assets/sass/ie9.scss rename to utils/test/reporting/3rd_party/sass/ie9.scss diff --git a/utils/test/reporting/assets/sass/layout/_footer.scss b/utils/test/reporting/3rd_party/sass/layout/_footer.scss similarity index 100% rename from utils/test/reporting/assets/sass/layout/_footer.scss rename to utils/test/reporting/3rd_party/sass/layout/_footer.scss diff --git a/utils/test/reporting/assets/sass/layout/_header.scss b/utils/test/reporting/3rd_party/sass/layout/_header.scss similarity index 100% rename from utils/test/reporting/assets/sass/layout/_header.scss rename to utils/test/reporting/3rd_party/sass/layout/_header.scss diff --git a/utils/test/reporting/assets/sass/layout/_main.scss b/utils/test/reporting/3rd_party/sass/layout/_main.scss similarity index 100% rename from utils/test/reporting/assets/sass/layout/_main.scss rename to utils/test/reporting/3rd_party/sass/layout/_main.scss diff --git a/utils/test/reporting/assets/sass/layout/_menu.scss b/utils/test/reporting/3rd_party/sass/layout/_menu.scss similarity index 100% rename from utils/test/reporting/assets/sass/layout/_menu.scss rename to utils/test/reporting/3rd_party/sass/layout/_menu.scss diff --git a/utils/test/reporting/assets/sass/layout/_wrapper.scss b/utils/test/reporting/3rd_party/sass/layout/_wrapper.scss similarity index 100% rename from utils/test/reporting/assets/sass/layout/_wrapper.scss rename to utils/test/reporting/3rd_party/sass/layout/_wrapper.scss diff --git a/utils/test/reporting/assets/sass/libs/_functions.scss b/utils/test/reporting/3rd_party/sass/libs/_functions.scss similarity index 100% rename from utils/test/reporting/assets/sass/libs/_functions.scss rename to utils/test/reporting/3rd_party/sass/libs/_functions.scss diff --git a/utils/test/reporting/assets/sass/libs/_mixins.scss b/utils/test/reporting/3rd_party/sass/libs/_mixins.scss similarity index 100% rename from utils/test/reporting/assets/sass/libs/_mixins.scss rename to utils/test/reporting/3rd_party/sass/libs/_mixins.scss diff --git a/utils/test/reporting/assets/sass/libs/_skel.scss b/utils/test/reporting/3rd_party/sass/libs/_skel.scss similarity index 100% rename from utils/test/reporting/assets/sass/libs/_skel.scss rename to utils/test/reporting/3rd_party/sass/libs/_skel.scss diff --git a/utils/test/reporting/assets/sass/libs/_vars.scss b/utils/test/reporting/3rd_party/sass/libs/_vars.scss similarity index 100% rename from utils/test/reporting/assets/sass/libs/_vars.scss rename to utils/test/reporting/3rd_party/sass/libs/_vars.scss diff --git a/utils/test/reporting/assets/sass/main.scss b/utils/test/reporting/3rd_party/sass/main.scss similarity index 100% rename from utils/test/reporting/assets/sass/main.scss rename to utils/test/reporting/3rd_party/sass/main.scss diff --git a/utils/test/reporting/docker/Dockerfile b/utils/test/reporting/docker/Dockerfile new file mode 100644 index 000000000..789df9115 --- /dev/null +++ b/utils/test/reporting/docker/Dockerfile @@ -0,0 +1,43 @@ +######################################## +# Docker container for OPNFV-REPORTING +######################################## +# Purpose: run opnfv-reporting to provide consistent Testing reporting +# +# Maintained by Morgan Richomme +# Build: +# $ docker build -t opnfv/testreporting:tag . +## +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# + +FROM nginx:stable + +MAINTAINER Morgan Richomme +LABEL version="danube.1.0" description="OPNFV Test Reporting Docker container" + +ARG BRANCH=master + +ENV HOME /home/opnfv +ENV working_dir /home/opnfv/utils/test/reporting +ENV TERM xterm +ENV COLORTERM gnome-terminal +ENV CONFIG_REPORTING_YAML /home/opnfv/utils/test/reporting/reporting.yaml + +# Packaged dependencies +RUN apt-get update && apt-get install -y \ +ssh \ +git-core \ +wkhtmltopdf \ +--no-install-recommends + +RUN pip install --upgrade pip + +RUN pip install -r ${working_dir}/docker/requirements.txt +RUN git clone --depth 1 https://gerrit.opnfv.org/gerrit/releng /home/opnfv + +WORKDIR ${working_dir} +CMD ["bash", "./docker/reporting.sh"] +CMD ["bash", "mv display /usr/share/nginx/html"] diff --git a/utils/test/reporting/docker/reporting.sh b/utils/test/reporting/docker/reporting.sh new file mode 100755 index 000000000..5d4ea11f4 --- /dev/null +++ b/utils/test/reporting/docker/reporting.sh @@ -0,0 +1,65 @@ +#!/bin/bash +cd .. + +export PYTHONPATH="${PYTHONPATH}:." +export CONFIG_REPORTING_YAML=./reporting.yaml + +declare -a versions=(colorado master) +declare -a projects=(functest yardstick) + +project=$1 +reporting_type=$2 + +# create Directories if needed +for i in "${versions[@]}" +do + for j in "${projects[@]}" + do + mkdir -p display/$i/$j + done +done + +# copy images, js, css, 3rd_party +cp -Rf 3rd_party display +cp -Rf css display +cp -Rf html/* display +cp -Rf img display +cp -Rf js display + +# if nothing is precised run all the reporting generation +# projet | option +# $1 | $2 +# functest | status, vims, tempest +# yardstick | + +if [ -z "$1" ]; then + echo "********************************" + echo " Functest reporting " + echo "********************************" + echo "reporting vIMS..." + python ./functest/reporting-vims.py + echo "reporting vIMS...OK" + sleep 10 + echo "reporting Tempest..." + python ./functest/reporting-tempest.py + echo "reporting Tempest...OK" + sleep 10 + echo "reporting status..." + python ./functest/reporting-status.py + echo "Functest reporting status...OK" + + echo "********************************" + echo " Yardstick reporting " + echo "********************************" + python ./yardstick/reporting-status.py + echo "Yardstick reporting status...OK" +else + if [ -z "$2" ]; then + reporting_type="status" + fi + echo "********************************" + echo " $project/$reporting_type reporting " + echo "********************************" + python ./$project/reporting-$reporting_type.py +fi + diff --git a/utils/test/reporting/docker/requirements.pip b/utils/test/reporting/docker/requirements.pip new file mode 100644 index 000000000..21d5ba97a --- /dev/null +++ b/utils/test/reporting/docker/requirements.pip @@ -0,0 +1,12 @@ +# +# +# author: Morgan Richomme (morgan.richomme@orange.com) +# +# 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 +# +pdfkit==0.5.0 +PyYAML==3.11 +simplejson==3.8.1 diff --git a/utils/test/reporting/functest/reporting-status.py b/utils/test/reporting/functest/reporting-status.py index 653448eaf..cb13b099c 100755 --- a/utils/test/reporting/functest/reporting-status.py +++ b/utils/test/reporting/functest/reporting-status.py @@ -8,19 +8,20 @@ # import datetime import jinja2 -import pdfkit +import os import requests import sys import time import yaml -import reportingUtils as utils -import reportingConf as conf import testCase as tc import scenarioResult as sr +# manage conf +import utils.reporting_utils as rp_utils + # Logger -logger = utils.getLogger("Status") +logger = rp_utils.getLogger("Functest-Status") # Initialization testValid = [] @@ -33,14 +34,21 @@ tempest = tc.TestCase("tempest_smoke_serial", "functest", -1) # Retrieve the Functest configuration to detect which tests are relevant # according to the installer, scenario -cf = conf.TEST_CONF +cf = rp_utils.get_config('functest.test_conf') +period = rp_utils.get_config('general.period') +versions = rp_utils.get_config('general.versions') +installers = rp_utils.get_config('general.installers') +blacklist = rp_utils.get_config('functest.blacklist') +log_level = rp_utils.get_config('general.log.log_level') response = requests.get(cf) functest_yaml_config = yaml.safe_load(response.text) logger.info("*******************************************") +logger.info("* *") logger.info("* Generating reporting scenario status *") -logger.info("* Data retention = %s days *" % conf.PERIOD) +logger.info("* Data retention: %s days *" % period) +logger.info("* Log level: %s *" % log_level) logger.info("* *") logger.info("*******************************************") @@ -55,36 +63,41 @@ config_tiers = functest_yaml_config.get("tiers") for tier in config_tiers: if tier['order'] > 0 and tier['order'] < 3: for case in tier['testcases']: - if case['name'] not in conf.blacklist: + if case['name'] not in blacklist: testValid.append(tc.TestCase(case['name'], "functest", case['dependencies'])) elif tier['order'] == 3: for case in tier['testcases']: - if case['name'] not in conf.blacklist: + if case['name'] not in blacklist: testValid.append(tc.TestCase(case['name'], case['name'], case['dependencies'])) elif tier['order'] > 3: for case in tier['testcases']: - if case['name'] not in conf.blacklist: + if case['name'] not in blacklist: otherTestCases.append(tc.TestCase(case['name'], "functest", case['dependencies'])) +logger.debug("Functest reporting start") # For all the versions -for version in conf.versions: +for version in versions: # For all the installers - for installer in conf.installers: + for installer in installers: # get scenarios - scenario_results = utils.getScenarios(tempest, installer, version) - scenario_stats = utils.getScenarioStats(scenario_results) + scenario_results = rp_utils.getScenarios(tempest, installer, version) + scenario_stats = rp_utils.getScenarioStats(scenario_results) items = {} scenario_result_criteria = {} - scenario_file_name = (conf.REPORTING_PATH + - "/functest/release/" + version + - "/scenario_history.txt") + scenario_file_name = ("./display/" + version + + "/functest/scenario_history.txt") + # initiate scenario file if it does not exist + if not os.path.isfile(scenario_file_name): + with open(scenario_file_name, "a") as my_file: + logger.debug("Create scenario file: %s" % scenario_file_name) + my_file.write("date,scenario,installer,detail,score\n") # For all the scenarios get results for s, s_result in scenario_results.items(): @@ -102,7 +115,7 @@ for version in conf.versions: if len(s_result) > 0: build_tag = s_result[len(s_result)-1]['build_tag'] logger.debug("Build tag: %s" % build_tag) - s_url = s_url = utils.getJenkinsUrl(build_tag) + s_url = s_url = rp_utils.getJenkinsUrl(build_tag) logger.info("last jenkins url: %s" % s_url) testCases2BeDisplayed = [] # Check if test case is runnable / installer, scenario @@ -126,7 +139,8 @@ for version in conf.versions: nb_test_runnable_for_this_scenario += 1 logger.info(" Searching results for case %s " % (displayName)) - result = utils.getResult(dbName, installer, s, version) + result = rp_utils.getResult(dbName, installer, + s, version) # if no result set the value to 0 if result < 0: result = 0 @@ -158,7 +172,8 @@ for version in conf.versions: project = test_case.getProject() logger.info(" Searching results for case %s " % (displayName)) - result = utils.getResult(dbName, installer, s, version) + result = rp_utils.getResult(dbName, installer, + s, version) # at least 1 result for the test if result > -1: test_case.setCriteria(result) @@ -186,11 +201,11 @@ for version in conf.versions: scenario_criteria = nb_test_runnable_for_this_scenario * 3 # if 0 runnable tests set criteria at a high value if scenario_criteria < 1: - scenario_criteria = conf.MAX_SCENARIO_CRITERIA + scenario_criteria = 50 # conf.MAX_SCENARIO_CRITERIA s_score = str(scenario_score) + "/" + str(scenario_criteria) - s_score_percent = utils.getScenarioPercent(scenario_score, - scenario_criteria) + s_score_percent = rp_utils.getScenarioPercent(scenario_score, + scenario_criteria) s_status = "KO" if scenario_score < scenario_criteria: @@ -200,9 +215,9 @@ for version in conf.versions: else: logger.info(">>>>> scenario OK, save the information") s_status = "OK" - path_validation_file = (conf.REPORTING_PATH + - "/functest/release/" + version + - "/validated_scenario_history.txt") + path_validation_file = ("./display/" + version + + "/functest/" + + "validated_scenario_history.txt") with open(path_validation_file, "a") as f: time_format = "%Y-%m-%d %H:%M" info = (datetime.datetime.now().strftime(time_format) + @@ -222,54 +237,37 @@ for version in conf.versions: s_url) logger.info("--------------------------") - templateLoader = jinja2.FileSystemLoader(conf.REPORTING_PATH) + templateLoader = jinja2.FileSystemLoader(".") templateEnv = jinja2.Environment( loader=templateLoader, autoescape=True) - TEMPLATE_FILE = "/functest/template/index-status-tmpl.html" + TEMPLATE_FILE = "./functest/template/index-status-tmpl.html" template = templateEnv.get_template(TEMPLATE_FILE) outputText = template.render(scenario_stats=scenario_stats, scenario_results=scenario_result_criteria, items=items, installer=installer, - period=conf.PERIOD, + period=period, version=version, date=reportingDate) - # csv - # generate sub files based on scenario_history.txt - scenario_installer_file_name = (conf.REPORTING_PATH + - "/functest/release/" + version + - "/scenario_history_" + installer + - ".txt") - scenario_installer_file = open(scenario_installer_file_name, "a") - logger.info("Generate CSV...") - with open(scenario_file_name, "r") as f: - for line in f: - if installer in line: - logger.debug("Add new line... %s" % line) - scenario_installer_file.write(line) - scenario_installer_file.close - - with open(conf.REPORTING_PATH + "/functest/release/" + version + - "/index-status-" + installer + ".html", "wb") as fh: + with open("./display/" + version + + "/functest/status-" + installer + ".html", "wb") as fh: fh.write(outputText) - logger.info("CSV generated...") + + logger.info("Manage export CSV & PDF") + rp_utils.export_csv(scenario_file_name, installer, version) + logger.error("CSV generated...") # Generate outputs for export # pdf - logger.info("Generate PDF...") - try: - pdf_path = ("http://testresults.opnfv.org/reporting/" + - "functest/release/" + version + - "/index-status-" + installer + ".html") - pdf_doc_name = (conf.REPORTING_PATH + - "/functest/release/" + version + - "/status-" + installer + ".pdf") - pdfkit.from_url(pdf_path, pdf_doc_name) - logger.info("PDF generated...") - except IOError: - logger.info("pdf generated anyway...") - except: - logger.error("impossible to generate PDF") + # TODO Change once web site updated...use the current one + # to test pdf production + url_pdf = rp_utils.get_config('general.url') + pdf_path = ("./display/" + version + + "/functest/status-" + installer + ".html") + pdf_doc_name = ("./display/" + version + + "/functest/status-" + installer + ".pdf") + rp_utils.export_pdf(pdf_path, pdf_doc_name) + logger.info("PDF generated...") diff --git a/utils/test/reporting/functest/reporting-tempest.py b/utils/test/reporting/functest/reporting-tempest.py index 363f123cf..5d6bcc062 100755 --- a/utils/test/reporting/functest/reporting-tempest.py +++ b/utils/test/reporting/functest/reporting-tempest.py @@ -1,18 +1,22 @@ from urllib2 import Request, urlopen, URLError import json import jinja2 -import reportingConf as conf -import reportingUtils as utils +import os -installers = conf.installers +# manage conf +import utils.reporting_utils as rp_utils + +installers = rp_utils.get_config('general.installers') items = ["tests", "Success rate", "duration"] -PERIOD = conf.PERIOD +CURRENT_DIR = os.getcwd() + +PERIOD = rp_utils.get_config('general.period') criteria_nb_test = 165 criteria_duration = 1800 criteria_success_rate = 90 -logger = utils.getLogger("Tempest") +logger = rp_utils.getLogger("Tempest") logger.info("************************************************") logger.info("* Generating reporting Tempest_smoke_serial *") logger.info("* Data retention = %s days *" % PERIOD) @@ -25,10 +29,11 @@ logger.info("test duration < %s s " % criteria_duration) logger.info("success rate > %s " % criteria_success_rate) # For all the versions -for version in conf.versions: - for installer in conf.installers: +for version in rp_utils.get_config('general.versions'): + for installer in installers: # we consider the Tempest results of the last PERIOD days - url = 'http://' + conf.URL_BASE + "?case=tempest_smoke_serial" + url = ("http://" + rp_utils.get_config('testapi.url') + + "?case=tempest_smoke_serial") request = Request(url + '&period=' + str(PERIOD) + '&installer=' + installer + '&version=' + version) @@ -68,8 +73,9 @@ for version in conf.versions: nb_tests_run = result['details']['tests'] nb_tests_failed = result['details']['failures'] if nb_tests_run != 0: - success_rate = 100*(int(nb_tests_run) - - int(nb_tests_failed)) / int(nb_tests_run) + success_rate = 100*((int(nb_tests_run) - + int(nb_tests_failed)) / + int(nb_tests_run)) else: success_rate = 0 @@ -115,17 +121,18 @@ for version in conf.versions: except: logger.error("Error field not present (Brahamputra runs?)") - templateLoader = jinja2.FileSystemLoader(conf.REPORTING_PATH) - templateEnv = jinja2.Environment(loader=templateLoader, autoescape=True) + templateLoader = jinja2.FileSystemLoader(".") + templateEnv = jinja2.Environment(loader=templateLoader, + autoescape=True) - TEMPLATE_FILE = "/template/index-tempest-tmpl.html" + TEMPLATE_FILE = "./functest/template/index-tempest-tmpl.html" template = templateEnv.get_template(TEMPLATE_FILE) outputText = template.render(scenario_results=scenario_results, items=items, installer=installer) - with open(conf.REPORTING_PATH + "/release/" + version + - "/index-tempest-" + installer + ".html", "wb") as fh: + with open("./display/" + version + + "/functest/tempest-" + installer + ".html", "wb") as fh: fh.write(outputText) logger.info("Tempest automatic reporting succesfully generated.") diff --git a/utils/test/reporting/functest/reporting-vims.py b/utils/test/reporting/functest/reporting-vims.py index 430a5453c..2077d2a4a 100755 --- a/utils/test/reporting/functest/reporting-vims.py +++ b/utils/test/reporting/functest/reporting-vims.py @@ -1,10 +1,11 @@ from urllib2 import Request, urlopen, URLError import json import jinja2 -import reportingConf as conf -import reportingUtils as utils -logger = utils.getLogger("vIMS") +# manage conf +import utils.reporting_utils as rp_utils + +logger = rp_utils.getLogger("vIMS") def sig_test_format(sig_test): @@ -24,22 +25,26 @@ def sig_test_format(sig_test): total_sig_test_result['skipped'] = nbSkipped return total_sig_test_result +period = rp_utils.get_config('general.period') +versions = rp_utils.get_config('general.versions') +url_base = rp_utils.get_config('testapi.url') + logger.info("****************************************") logger.info("* Generating reporting vIMS *") -logger.info("* Data retention = %s days *" % conf.PERIOD) +logger.info("* Data retention = %s days *" % period) logger.info("* *") logger.info("****************************************") -installers = conf.installers +installers = rp_utils.get_config('general.installers') step_order = ["initialisation", "orchestrator", "vIMS", "sig_test"] logger.info("Start processing....") # For all the versions -for version in conf.versions: +for version in versions: for installer in installers: logger.info("Search vIMS results for installer: %s, version: %s" % (installer, version)) - request = Request("http://" + conf.URL_BASE + '?case=vims&installer=' + + request = Request("http://" + url_base + '?case=vims&installer=' + installer + '&version=' + version) try: @@ -81,7 +86,8 @@ for version in conf.versions: if int(m) != 0: m_display += str(int(m)) + "m " - step_result['duration_display'] = m_display + str(int(s)) + "s" + step_result['duration_display'] = (m_display + + str(int(s)) + "s") result['pr_step_ok'] = 0 if nb_step != 0: @@ -89,8 +95,9 @@ for version in conf.versions: try: logger.debug("Scenario %s, Installer %s" % (s_result[1]['scenario'], installer)) + res = result['details']['orchestrator']['duration'] logger.debug("Orchestrator deployment: %s s" - % result['details']['orchestrator']['duration']) + % res) logger.debug("vIMS deployment: %s s" % result['details']['vIMS']['duration']) logger.debug("Signaling testing: %s s" @@ -101,18 +108,18 @@ for version in conf.versions: logger.error("Data badly formatted") logger.debug("----------------------------------------") - templateLoader = jinja2.FileSystemLoader(conf.REPORTING_PATH) - templateEnv = jinja2.Environment(loader=templateLoader, autoescape=True) + templateLoader = jinja2.FileSystemLoader(".") + templateEnv = jinja2.Environment(loader=templateLoader, + autoescape=True) - TEMPLATE_FILE = "/template/index-vims-tmpl.html" + TEMPLATE_FILE = "./functest/template/index-vims-tmpl.html" template = templateEnv.get_template(TEMPLATE_FILE) outputText = template.render(scenario_results=scenario_results, step_order=step_order, installer=installer) - with open(conf.REPORTING_PATH + - "/release/" + version + "/index-vims-" + + with open("./display/" + version + "/functest/vims-" + installer + ".html", "wb") as fh: fh.write(outputText) diff --git a/utils/test/reporting/functest/reportingConf.py b/utils/test/reporting/functest/reportingConf.py deleted file mode 100644 index 1c9a2ac9f..000000000 --- a/utils/test/reporting/functest/reportingConf.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/python -# -# 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 -# -# Reporting: Declaration of the variables -# -# **************************************************** -installers = ["apex", "compass", "fuel", "joid"] -# list of test cases declared in testcases.yaml but that must not be -# taken into account for the scoring -blacklist = ["ovno", "security_scan"] -versions = ["master", "colorado"] -PERIOD = 10 -MAX_SCENARIO_CRITERIA = 50 -# get the last 5 test results to determinate the success criteria -NB_TESTS = 5 -# REPORTING_PATH = "/usr/share/nginx/html/reporting/functest" -REPORTING_PATH = "." -URL_BASE = 'testresults.opnfv.org/test/api/v1/results' -TEST_CONF = "https://git.opnfv.org/cgit/functest/plain/ci/testcases.yaml" -LOG_LEVEL = "ERROR" -LOG_FILE = REPORTING_PATH + "/reporting.log" diff --git a/utils/test/reporting/functest/reportingUtils.py b/utils/test/reporting/functest/reportingUtils.py deleted file mode 100644 index 74d6f19c9..000000000 --- a/utils/test/reporting/functest/reportingUtils.py +++ /dev/null @@ -1,186 +0,0 @@ -#!/usr/bin/python -# -# This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -from urllib2 import Request, urlopen, URLError -import logging -import json -import reportingConf as conf - - -def getLogger(module): - logFormatter = logging.Formatter("%(asctime)s [" + - module + - "] [%(levelname)-5.5s] %(message)s") - logger = logging.getLogger() - - fileHandler = logging.FileHandler("{0}/{1}".format('.', conf.LOG_FILE)) - fileHandler.setFormatter(logFormatter) - logger.addHandler(fileHandler) - - consoleHandler = logging.StreamHandler() - consoleHandler.setFormatter(logFormatter) - logger.addHandler(consoleHandler) - logger.setLevel(conf.LOG_LEVEL) - return logger - - -def getApiResults(case, installer, scenario, version): - results = json.dumps([]) - # to remove proxy (to be removed at the end for local test only) - # proxy_handler = urllib2.ProxyHandler({}) - # opener = urllib2.build_opener(proxy_handler) - # urllib2.install_opener(opener) - # url = "http://127.0.0.1:8000/results?case=" + case + \ - # "&period=30&installer=" + installer - url = ("http://" + conf.URL_BASE + "?case=" + case + - "&period=" + str(conf.PERIOD) + "&installer=" + installer + - "&scenario=" + scenario + "&version=" + version + - "&last=" + str(conf.NB_TESTS)) - request = Request(url) - - try: - response = urlopen(request) - k = response.read() - results = json.loads(k) - except URLError, e: - print 'No kittez. Got an error code:', e - - return results - - -def getScenarios(case, installer, version): - - case = case.getName() - url = ("http://" + conf.URL_BASE + "?case=" + case + - "&period=" + str(conf.PERIOD) + "&installer=" + installer + - "&version=" + version) - request = Request(url) - - try: - response = urlopen(request) - k = response.read() - results = json.loads(k) - test_results = results['results'] - except URLError, e: - print 'Got an error code:', e - - if test_results is not None: - test_results.reverse() - - scenario_results = {} - - for r in test_results: - # Retrieve all the scenarios per installer - if not r['scenario'] in scenario_results.keys(): - scenario_results[r['scenario']] = [] - scenario_results[r['scenario']].append(r) - - return scenario_results - - -def getScenarioStats(scenario_results): - scenario_stats = {} - for k, v in scenario_results.iteritems(): - scenario_stats[k] = len(v) - - return scenario_stats - - -def getNbtestOk(results): - nb_test_ok = 0 - for r in results: - for k, v in r.iteritems(): - try: - if "PASS" in v: - nb_test_ok += 1 - except: - print "Cannot retrieve test status" - return nb_test_ok - - -def getResult(testCase, installer, scenario, version): - - # retrieve raw results - results = getApiResults(testCase, installer, scenario, version) - # let's concentrate on test results only - test_results = results['results'] - - # if results found, analyze them - if test_results is not None: - test_results.reverse() - - scenario_results = [] - - # print " ---------------- " - # print test_results - # print " ---------------- " - # print "nb of results:" + str(len(test_results)) - - for r in test_results: - # print r["start_date"] - # print r["criteria"] - scenario_results.append({r["start_date"]: r["criteria"]}) - # sort results - scenario_results.sort() - # 4 levels for the results - # 3: 4+ consecutive runs passing the success criteria - # 2: <4 successful consecutive runs but passing the criteria - # 1: close to pass the success criteria - # 0: 0% success, not passing - # -1: no run available - test_result_indicator = 0 - nbTestOk = getNbtestOk(scenario_results) - - # print "Nb test OK (last 10 days):"+ str(nbTestOk) - # check that we have at least 4 runs - if len(scenario_results) < 1: - # No results available - test_result_indicator = -1 - elif nbTestOk < 1: - test_result_indicator = 0 - elif nbTestOk < 2: - test_result_indicator = 1 - else: - # Test the last 4 run - if (len(scenario_results) > 3): - last4runResults = scenario_results[-4:] - nbTestOkLast4 = getNbtestOk(last4runResults) - # print "Nb test OK (last 4 run):"+ str(nbTestOkLast4) - if nbTestOkLast4 > 3: - test_result_indicator = 3 - else: - test_result_indicator = 2 - else: - test_result_indicator = 2 - return test_result_indicator - - -def getJenkinsUrl(build_tag): - # e.g. jenkins-functest-apex-apex-daily-colorado-daily-colorado-246 - # id = 246 - # note it is linked to jenkins format - # if this format changes...function to be adapted.... - url_base = "https://build.opnfv.org/ci/view/functest/job/" - jenkins_url = "" - try: - build_id = [int(s) for s in build_tag.split("-") if s.isdigit()] - jenkins_path = filter(lambda c: not c.isdigit(), build_tag) - url_id = jenkins_path[8:-1] + "/" + str(build_id[0]) - jenkins_url = url_base + url_id + "/console" - except: - print 'Impossible to get jenkins url:' - - return jenkins_url - -def getScenarioPercent(scenario_score,scenario_criteria): - score = 0.0 - try: - score = float(scenario_score) / float(scenario_criteria) * 100 - except: - print 'Impossible to calculate the percentage score' - return score diff --git a/utils/test/reporting/functest/template/index-status-tmpl.html b/utils/test/reporting/functest/template/index-status-tmpl.html index e3c9c5fc0..094bbf8a2 100644 --- a/utils/test/reporting/functest/template/index-status-tmpl.html +++ b/utils/test/reporting/functest/template/index-status-tmpl.html @@ -3,12 +3,12 @@ - + - - + + - - - - + + + + + diff --git a/utils/test/reporting/danube.html b/utils/test/reporting/html/danube.html similarity index 72% rename from utils/test/reporting/danube.html rename to utils/test/reporting/html/danube.html index 70cd85711..58d6bc0fe 100644 --- a/utils/test/reporting/danube.html +++ b/utils/test/reporting/html/danube.html @@ -9,10 +9,10 @@ Phantom by HTML5 UP - - - - + + + + @@ -24,7 +24,7 @@ @@ -56,7 +56,7 @@
- + - +

Yardstick

Qualification and performance testing

@@ -103,11 +103,11 @@
- - - - - + + + + + diff --git a/utils/test/reporting/elements.html b/utils/test/reporting/html/elements.html similarity index 76% rename from utils/test/reporting/elements.html rename to utils/test/reporting/html/elements.html index 3bfbfc115..7b9bb4d25 100644 --- a/utils/test/reporting/elements.html +++ b/utils/test/reporting/html/elements.html @@ -9,10 +9,10 @@ Elements - Phantom by HTML5 UP - - - - + + + + @@ -24,7 +24,7 @@
@@ -347,21 +347,21 @@ print 'It took ' + i + ' iterations to sort the deck.';

Fit

-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

Left & Right

-

Fringilla nisl. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent.

-

Fringilla nisl. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent.

+

Fringilla nisl. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent.

+

Fringilla nisl. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent.

@@ -409,11 +409,11 @@ print 'It took ' + i + ' iterations to sort the deck.'; - - - - - + + + + + - \ No newline at end of file + diff --git a/utils/test/reporting/functest-colorado.html b/utils/test/reporting/html/functest-colorado.html similarity index 68% rename from utils/test/reporting/functest-colorado.html rename to utils/test/reporting/html/functest-colorado.html index 8e474008b..2fc76d1c5 100644 --- a/utils/test/reporting/functest-colorado.html +++ b/utils/test/reporting/html/functest-colorado.html @@ -9,10 +9,10 @@ Phantom by HTML5 UP - - - - + + + + @@ -24,7 +24,7 @@ @@ -56,9 +56,9 @@
- + - +

Tempest

Tempest OpenStack suite

@@ -114,11 +114,11 @@
- - - - - + + + + + diff --git a/utils/test/reporting/functest-master.html b/utils/test/reporting/html/functest-master.html similarity index 68% rename from utils/test/reporting/functest-master.html rename to utils/test/reporting/html/functest-master.html index 125b8c3cb..03217a6bd 100644 --- a/utils/test/reporting/functest-master.html +++ b/utils/test/reporting/html/functest-master.html @@ -9,10 +9,10 @@ Phantom by HTML5 UP - - - - + + + + @@ -24,7 +24,7 @@
@@ -56,9 +56,9 @@
- + - +

Tempest

Tempest OpenStack suite

@@ -114,11 +114,11 @@
- - - - - + + + + + diff --git a/utils/test/reporting/generic.html b/utils/test/reporting/html/generic.html similarity index 83% rename from utils/test/reporting/generic.html rename to utils/test/reporting/html/generic.html index 1351b2a85..39273da80 100644 --- a/utils/test/reporting/generic.html +++ b/utils/test/reporting/html/generic.html @@ -9,10 +9,10 @@ Generic - Phantom by HTML5 UP - - - - + + + + @@ -24,7 +24,7 @@
@@ -53,7 +53,7 @@

Generic Page

- +

Donec eget ex magna. Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque venenatis dolor imperdiet dolor mattis sagittis. Praesent rutrum sem diam, vitae egestas enim auctor sit amet. Pellentesque leo mauris, consectetur id ipsum sit amet, fergiat. Pellentesque in mi eu massa lacinia malesuada et a elit. Donec urna ex, lacinia in purus ac, pretium pulvinar mauris. Curabitur sapien risus, commodo eget turpis at, elementum convallis elit. Pellentesque enim turpis, hendrerit tristique.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis dapibus rutrum facilisis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Etiam tristique libero eu nibh porttitor fermentum. Nullam venenatis erat id vehicula viverra. Nunc ultrices eros ut ultricies condimentum. Mauris risus lacus, blandit sit amet venenatis non, bibendum vitae dolor. Nunc lorem mauris, fringilla in aliquam at, euismod in lectus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In non lorem sit amet elit placerat maximus. Pellentesque aliquam maximus risus, vel venenatis mauris vehicula hendrerit.

Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque venenatis dolor imperdiet dolor mattis sagittis. Praesent rutrum sem diam, vitae egestas enim auctor sit amet. Pellentesque leo mauris, consectetur id ipsum sit amet, fersapien risus, commodo eget turpis at, elementum convallis elit. Pellentesque enim turpis, hendrerit tristique lorem ipsum dolor.

@@ -102,11 +102,11 @@
- - - - - + + + + + - \ No newline at end of file + diff --git a/utils/test/reporting/index.html b/utils/test/reporting/html/index.html similarity index 74% rename from utils/test/reporting/index.html rename to utils/test/reporting/html/index.html index 1fad2e3f8..b2b8b46f8 100644 --- a/utils/test/reporting/index.html +++ b/utils/test/reporting/html/index.html @@ -9,10 +9,10 @@ OPNFV reporting - - - - + + + + @@ -24,7 +24,7 @@ @@ -56,7 +56,7 @@
- - - - - + + + + + diff --git a/utils/test/reporting/images/colorado.jpg b/utils/test/reporting/img/colorado.jpg similarity index 100% rename from utils/test/reporting/images/colorado.jpg rename to utils/test/reporting/img/colorado.jpg diff --git a/utils/test/reporting/images/danube.jpg b/utils/test/reporting/img/danube.jpg similarity index 100% rename from utils/test/reporting/images/danube.jpg rename to utils/test/reporting/img/danube.jpg diff --git a/utils/test/reporting/images/functest.jpg b/utils/test/reporting/img/functest.jpg similarity index 100% rename from utils/test/reporting/images/functest.jpg rename to utils/test/reporting/img/functest.jpg diff --git a/utils/test/reporting/images/functest.jpg.old b/utils/test/reporting/img/functest.jpg.old similarity index 100% rename from utils/test/reporting/images/functest.jpg.old rename to utils/test/reporting/img/functest.jpg.old diff --git a/utils/test/reporting/img/gauge_0.png b/utils/test/reporting/img/gauge_0.png new file mode 100644 index 0000000000000000000000000000000000000000..ecefc0e662b8c47408fa03a87338047f01b7ed6b GIT binary patch literal 3644 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 y;PBzYC@d^QT3QBs2+ literal 0 HcmV?d00001 diff --git a/utils/test/reporting/img/gauge_33.3.png b/utils/test/reporting/img/gauge_33.3.png new file mode 100644 index 0000000000000000000000000000000000000000..364574b4a0a0a55bb81f857a2baa063ac3c06f4b GIT binary patch literal 3081 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 XqEtK8ahxcu00000NkvXXu0mjf{K?yn literal 0 HcmV?d00001 diff --git a/utils/test/reporting/img/gauge_41.7.png b/utils/test/reporting/img/gauge_41.7.png new file mode 100644 index 0000000000000000000000000000000000000000..8c3e910fa4b06ef6e3ba391443e29765410cf55f GIT binary patch literal 3169 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 Hu0mjf7gg^5 literal 0 HcmV?d00001 diff --git a/utils/test/reporting/img/gauge_50.png b/utils/test/reporting/img/gauge_50.png new file mode 100644 index 0000000000000000000000000000000000000000..2874b9fcfc0d9449756f3a298957a9f32a79dee8 GIT binary patch literal 3123 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 literal 0 HcmV?d00001 diff --git a/utils/test/reporting/img/gauge_66.7.png b/utils/test/reporting/img/gauge_66.7.png new file mode 100644 index 0000000000000000000000000000000000000000..93f44d1331b3a56c2369621edafb49f2843cea95 GIT binary patch literal 3069 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 z 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 M07*qoM6N<$f~H;v0RR91 literal 0 HcmV?d00001 diff --git a/utils/test/reporting/img/gauge_91.7.png b/utils/test/reporting/img/gauge_91.7.png new file mode 100644 index 0000000000000000000000000000000000000000..28086571439ac922f99ed3b7c7421b175e68f834 GIT binary patch literal 3008 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 KMNUMnLSTYV>JcIU literal 0 HcmV?d00001 diff --git a/utils/test/reporting/img/weather-few-clouds.png b/utils/test/reporting/img/weather-few-clouds.png new file mode 100644 index 0000000000000000000000000000000000000000..acfa783984eecb2158c3f06ae1a59b9cab66920b GIT binary patch literal 1927 zcmV;22YC32P) zTWnm#9man%=j_?D7uy?q?Kp8r>|hANB(w>HCBJmRS z0ri1KZF*6Qs!A;pPe>pxfsl$yZ5oQw5I__Pxj0c0VLNub>-E{)bDx<$tZT(hoT4C6 z^)DT1&Sk#;_y6XbIWzG89KpWfWyy=1mVFuwd9@w&vm2$8b+(erZq?6jylNc#YCEc2 zwk-NKTq^{q=x`aB{7eaucLyovS0ZNHS4rTq(&?A)EXU7n?!8h+;kvhwjJialu5jI3 zuJmpE{GGk}`Mb)Odv{p{5-FrrET6bLCSQ8miE3g!*xOK&vPOPe~)qRxj|vTBWJTUpX)j~vR}w)bbBSOEYooZC;S zJVar#WOA-!G9n)zxbH#P)&GR)T2i)3-4bB~8gz)NO@5ck`C9Z^ewU7#uaM2TqA<~J z>eQ&5-M>(pE0|E9G2Qq`=Cx*(_gD z7K?g!>vQkkBdhiA8)*>!hoNQEnT4_}I0VKR%pbRuUYfY)(OY`Qf7ti=n_cu6s2PwM zFbZ@yOCkgjgH|AV&~_jC*jvwuX1LQLZOHo%);=+T-9LY_Unst9+oD}5bM~(5q93ij zv8^Zz6-cMSZU-yhB9PW0^+5YT1SAk?kY$uEdFs>$JKb%s|9)YW|AWAuU;cFC&1-Hx zbkj|1?X2T)e)KFWgS}YSbVG2wl{nK0&Y~89xYi=2nqYhoDZ~mQg~|3~D%0oX$72KX z!TB@j;+R_pcJKK~|ElY5*t34!t#;ONX!<^#Vhp*c25>gPmgHpf$*}r2+<& zBup|)*0N|*z_BbwX0|i5<_@^^3N|Bxo>*Wqw&ZWdU!8Jn(6<9Rc9o`>)Ic-{4sEofwI(Yg zPQ@wFWL;kWxiX}NILwkJnmCDQHk&N&=_LpQ0^cW!BZ4p>W81iH25Cur-zSb^qA0`| z!*p%hMu^UT6Ik$HpkKYi)rG?MOtHAq)eIfogRs9336M?a@ab`^SQtNn^==`c|(IOLOaW zx$R-uZ0!8gTe-hpfBg`aWl{H9l6u|@LEyKp&?gE*;v^;vL&7K|N#Z%otm~4`=jrS$ zy9V>kc`M;11Ugr2qkXVMohre}P)NcHQTypAj%YL!rfXF^&!gFFlFhn=VL*~3NGV$e zjbqX@nZpx8AZ7)(EDMab=S+Bs{LkOrLvwPqIrH~JX5^jSmN)Yo>;5;Z2=;z->_Z(# z5shXOtu=8R(`+_~yaNk_Tqv=U_J2X1vd~= z_1ga6__2|`?AX?U!JaPzjGUNw;as!(%z`D)h7clkZ0kfEnGkK#TxKiw@d&4 N002ovPDHLkV1mG)xL*JO literal 0 HcmV?d00001 diff --git a/utils/test/reporting/img/weather-overcast.png b/utils/test/reporting/img/weather-overcast.png new file mode 100644 index 0000000000000000000000000000000000000000..4296246d0cd9dee7293ef68c1ad379903109fc0a GIT binary patch literal 1588 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 3): + last4runResults = scenario_results[-4:] + nbTestOkLast4 = getNbtestOk(last4runResults) + # print "Nb test OK (last 4 run):"+ str(nbTestOkLast4) + if nbTestOkLast4 > 3: + test_result_indicator = 3 + else: + test_result_indicator = 2 + else: + test_result_indicator = 2 + return test_result_indicator + + +def getJenkinsUrl(build_tag): + # e.g. jenkins-functest-apex-apex-daily-colorado-daily-colorado-246 + # id = 246 + # note it is linked to jenkins format + # if this format changes...function to be adapted.... + url_base = get_config('functest.jenkins_url') + try: + build_id = [int(s) for s in build_tag.split("-") if s.isdigit()] + jenkins_path = filter(lambda c: not c.isdigit(), build_tag) + url_id = jenkins_path[8:-1] + "/" + str(build_id[0]) + jenkins_url = url_base + url_id + "/console" + except: + print 'Impossible to get jenkins url:' + + return jenkins_url + + +def getScenarioPercent(scenario_score, scenario_criteria): + score = 0.0 + try: + score = float(scenario_score) / float(scenario_criteria) * 100 + except: + print 'Impossible to calculate the percentage score' + return score + + +# ********* +# Yardstick +# ********* +def subfind(given_list, pattern_list): + LASTEST_TESTS = get_config('general.nb_iteration_tests_success_criteria') + for i in range(len(given_list)): + if given_list[i] == pattern_list[0] and \ + given_list[i:i + LASTEST_TESTS] == pattern_list: + return True + return False + + +def _get_percent(status): + + if status * 100 % 6: + return round(float(status) * 100 / 6, 1) + else: + return status * 100 / 6 + + +def get_percent(four_list, ten_list): + four_score = 0 + ten_score = 0 + + for v in four_list: + four_score += v + for v in ten_list: + ten_score += v + + LASTEST_TESTS = get_config('general.nb_iteration_tests_success_criteria') + if four_score == LASTEST_TESTS: + status = 6 + elif subfind(ten_list, [1, 1, 1, 1]): + status = 5 + elif ten_score == 0: + status = 0 + else: + status = four_score + 1 + + return _get_percent(status) + + +def _test(): + status = getScenarioStatus("compass", "master") + print "status:++++++++++++++++++++++++" + print json.dumps(status, indent=4) + + +# ---------------------------------------------------------- +# +# Export +# +# ----------------------------------------------------------- + +def export_csv(scenario_file_name, installer, version): + # csv + # generate sub files based on scenario_history.txt + scenario_installer_file_name = ("./display/" + version + + "/functest/scenario_history_" + + installer + ".csv") + scenario_installer_file = open(scenario_installer_file_name, "a") + with open(scenario_file_name, "r") as f: + scenario_installer_file.write("date,scenario,installer,detail,score\n") + for line in f: + if installer in line: + scenario_installer_file.write(line) + scenario_installer_file.close + + +def export_pdf(pdf_path, pdf_doc_name): + try: + pdfkit.from_file(pdf_path, pdf_doc_name) + except IOError: + print("Error but pdf generated anyway...") + except: + print("impossible to generate PDF") diff --git a/utils/test/reporting/yardstick/reporting-status.py b/utils/test/reporting/yardstick/reporting-status.py index 49809e9d8..338154987 100644 --- a/utils/test/reporting/yardstick/reporting-status.py +++ b/utils/test/reporting/yardstick/reporting-status.py @@ -10,31 +10,36 @@ import datetime import jinja2 import os -import reportingUtils as utils -import reportingConf as conf import scenarioResult as sr from scenarios import config as cf +# manage conf +import utils.reporting_utils as rp_utils + +installers = rp_utils.get_config('general.installers') +versions = rp_utils.get_config('general.versions') +PERIOD = rp_utils.get_config('general.period') + # Logger -logger = utils.getLogger("Yardstick-Status") +logger = rp_utils.getLogger("Yardstick-Status") reportingDate = datetime.datetime.now().strftime("%Y-%m-%d %H:%M") logger.info("*******************************************") logger.info("* Generating reporting scenario status *") -logger.info("* Data retention = %s days *" % conf.PERIOD) +logger.info("* Data retention = %s days *" % PERIOD) logger.info("* *") logger.info("*******************************************") # For all the versions -for version in conf.versions: +for version in versions: # For all the installers - for installer in conf.installers: + for installer in installers: # get scenarios results data - scenario_results = utils.getScenarioStatus(installer, version) + scenario_results = rp_utils.getScenarioStatus(installer, version) if 'colorado' == version: - stable_result = utils.getScenarioStatus(installer, - 'stable/colorado') + stable_result = rp_utils.getScenarioStatus(installer, + 'stable/colorado') for k, v in stable_result.items(): if k not in scenario_results.keys(): scenario_results[k] = [] @@ -48,24 +53,25 @@ for version in conf.versions: # From each scenarios get results list for s, s_result in scenario_results.items(): logger.info("---------------------------------") - logger.info("installer %s, version %s, scenario %s:" % (installer, - version, s)) + logger.info("installer %s, version %s, " + + "scenario %s:" % (installer, version, s)) ten_criteria = len(s_result) ten_score = 0 for v in s_result: ten_score += v - four_result = s_result[:conf.LASTEST_TESTS] + LASTEST_TESTS = rp_utils.get_config('general.nb_iteration_tests_success_criteria') + four_result = s_result[:LASTEST_TESTS] four_criteria = len(four_result) four_score = 0 for v in four_result: four_score += v - s_status = str(utils.get_percent(four_result, s_result)) + s_status = str(rp_utils.get_percent(four_result, s_result)) s_four_score = str(four_score) + '/' + str(four_criteria) s_ten_score = str(ten_score) + '/' + str(ten_criteria) - s_score_percent = utils.get_percent(four_result, s_result) + s_score_percent = rp_utils.get_percent(four_result, s_result) if '100' == s_status: logger.info(">>>>> scenario OK, save the information") @@ -74,9 +80,8 @@ for version in conf.versions: last 10 days = %s" % (s_four_score, s_ten_score)) # Save daily results in a file - path_validation_file = (conf.REPORTING_PATH + - "/release/" + version + - "/scenario_history.txt") + path_validation_file = ("./display/" + version + + "/yardstick/scenario_history.txt") if not os.path.exists(path_validation_file): with open(path_validation_file, 'w') as f: @@ -96,18 +101,19 @@ for version in conf.versions: logger.info("--------------------------") - templateLoader = jinja2.FileSystemLoader(conf.REPORTING_PATH) - templateEnv = jinja2.Environment(loader=templateLoader, autoescape=True) + templateLoader = jinja2.FileSystemLoader(".") + templateEnv = jinja2.Environment(loader=templateLoader, + autoescape=True) - TEMPLATE_FILE = "/template/index-status-tmpl.html" + TEMPLATE_FILE = "./yardstick/template/index-status-tmpl.html" template = templateEnv.get_template(TEMPLATE_FILE) outputText = template.render(scenario_results=scenario_result_criteria, installer=installer, - period=conf.PERIOD, + period=PERIOD, version=version, date=reportingDate) - with open(conf.REPORTING_PATH + "/release/" + version + - "/index-status-" + installer + ".html", "wb") as fh: + with open("./display/" + version + + "/yardstick/status-" + installer + ".html", "wb") as fh: fh.write(outputText) diff --git a/utils/test/reporting/yardstick/reportingConf.py b/utils/test/reporting/yardstick/reportingConf.py deleted file mode 100644 index 2db41f0e1..000000000 --- a/utils/test/reporting/yardstick/reportingConf.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/python -# -# 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 -# -# Reporting: Declaration of the variables -# -# **************************************************** -installers = ["apex", "compass", "fuel", "joid"] - -versions = ["master", "colorado"] - -# get data in the past 10 days -PERIOD = 10 - -# get the lastest 4 test results to determinate the success criteria -LASTEST_TESTS = 4 - -REPORTING_PATH = "." - -URL_BASE = 'http://testresults.opnfv.org/test/api/v1/results' -TEST_CONF = "https://git.opnfv.org/cgit/yardstick/plain/tests/ci/report_config.yaml" - -# LOG_LEVEL = "ERROR" -LOG_LEVEL = "INFO" -LOG_FILE = REPORTING_PATH + "/reporting.log" diff --git a/utils/test/reporting/yardstick/reportingUtils.py b/utils/test/reporting/yardstick/reportingUtils.py deleted file mode 100644 index ec9ed76dc..000000000 --- a/utils/test/reporting/yardstick/reportingUtils.py +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/python -# -# This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -from urllib2 import Request, urlopen, URLError -import logging -import json -import reportingConf as conf - - -def getLogger(module): - logFormatter = logging.Formatter("%(asctime)s [" + - module + - "] [%(levelname)-5.5s] %(message)s") - logger = logging.getLogger() - - fileHandler = logging.FileHandler("{0}/{1}".format('.', conf.LOG_FILE)) - fileHandler.setFormatter(logFormatter) - logger.addHandler(fileHandler) - - consoleHandler = logging.StreamHandler() - consoleHandler.setFormatter(logFormatter) - logger.addHandler(consoleHandler) - logger.setLevel(conf.LOG_LEVEL) - return logger - - -def getScenarioStatus(installer, version): - url = (conf.URL_BASE + "?case=" + "scenario_status" + - "&installer=" + installer + - "&version=" + version + "&period=" + str(conf.PERIOD)) - request = Request(url) - - try: - response = urlopen(request) - k = response.read() - response.close() - results = json.loads(k) - test_results = results['results'] - except URLError, e: - print 'Got an error code:', e - - scenario_results = {} - result_dict = {} - if test_results is not None: - for r in test_results: - if r['stop_date'] != 'None' and r['criteria'] is not None: - if not r['scenario'] in scenario_results.keys(): - scenario_results[r['scenario']] = [] - scenario_results[r['scenario']].append(r) - - for k, v in scenario_results.items(): - # scenario_results[k] = v[:conf.LASTEST_TESTS] - s_list = [] - for element in v: - if element['criteria'] == 'SUCCESS': - s_list.append(1) - else: - s_list.append(0) - result_dict[k] = s_list - - # return scenario_results - return result_dict - - -def subfind(given_list, pattern_list): - - for i in range(len(given_list)): - if given_list[i] == pattern_list[0] and \ - given_list[i:i + conf.LASTEST_TESTS] == pattern_list: - return True - return False - - -def _get_percent(status): - - if status * 100 % 6: - return round(float(status) * 100 / 6, 1) - else: - return status * 100 / 6 - - -def get_percent(four_list, ten_list): - four_score = 0 - ten_score = 0 - - for v in four_list: - four_score += v - for v in ten_list: - ten_score += v - - if four_score == conf.LASTEST_TESTS: - status = 6 - elif subfind(ten_list, [1, 1, 1, 1]): - status = 5 - elif ten_score == 0: - status = 0 - else: - status = four_score + 1 - - return _get_percent(status) - - -def _test(): - status = getScenarioStatus("compass", "master") - print "status:++++++++++++++++++++++++" - print json.dumps(status, indent=4) - - -if __name__ == '__main__': # pragma: no cover - _test() diff --git a/utils/test/reporting/yardstick/scenarios.py b/utils/test/reporting/yardstick/scenarios.py index 590fea2a4..26e8c8bb0 100644 --- a/utils/test/reporting/yardstick/scenarios.py +++ b/utils/test/reporting/yardstick/scenarios.py @@ -1,11 +1,18 @@ -import yaml -import os +#!/usr/bin/python +# +# 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 requests +import yaml -import reportingConf as conf - +import utils.reporting_utils as rp_utils -response = requests.get(conf.TEST_CONF) +yardstick_conf = rp_utils.get_config('yardstick.test_conf') +response = requests.get(yardstick_conf) yaml_file = yaml.safe_load(response.text) reporting = yaml_file.get('reporting') @@ -15,6 +22,6 @@ for element in reporting: name = element['name'] scenarios = element['scenario'] for s in scenarios: - if not config.has_key(name): + if name not in config: config[name] = {} config[name][s] = True diff --git a/utils/test/reporting/yardstick/template/index-status-tmpl.html b/utils/test/reporting/yardstick/template/index-status-tmpl.html index 5a4dc347c..b6f237a1a 100644 --- a/utils/test/reporting/yardstick/template/index-status-tmpl.html +++ b/utils/test/reporting/yardstick/template/index-status-tmpl.html @@ -3,12 +3,12 @@ - + - - + +