From e6a5f6f941be2bbdcf21a760f9597a2e309059f7 Mon Sep 17 00:00:00 2001 From: Morgan Richomme Date: Wed, 16 Mar 2016 18:31:06 +0100 Subject: [PATCH] Add automatic status reporting Show last scenario run + sun/cloud/storm dynamically JIRA:FUNCTEST-151 Change-Id: I86eeb64f0dea842a71b0cba9dd1058d7fa876269 Signed-off-by: Morgan Richomme --- 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/index-status-tmpl.html | 92 ++++++++++++ utils/test/reporting/index-tempest-tmpl.html | 2 +- utils/test/reporting/index-vims-tmpl.html | 12 +- utils/test/reporting/index.html | 52 +++++++ utils/test/reporting/reporting-status.py | 178 ++++++++++++++++++++++++ 9 files changed, 329 insertions(+), 7 deletions(-) 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 create mode 100644 utils/test/reporting/index-status-tmpl.html create mode 100644 utils/test/reporting/index.html create mode 100644 utils/test/reporting/reporting-status.py diff --git a/utils/test/reporting/img/weather-clear.png b/utils/test/reporting/img/weather-clear.png new file mode 100644 index 0000000000000000000000000000000000000000..a0d9677506947bcf7db6e9d1d39b6fe5abc790cf GIT binary patch literal 1560 zcmV+z2Iu*SP) zUufIc6~{mK-k+qOY(-HN+2r41lbDvWW)Gw5Oq|9}OM^)k;uT7t#>yVX*vshl81fYI zG*}-7dmLREgF?dUx^=y+T@vb+(76;=wzX-K+I5{cs;$VDEd74c@7}YApHk}8(mDk; zcHqF3?!D*xx#yhUKlp#P`qaq2=8K2--AfGJ?MC;)uui6ug46nf7!=f3}eDlWV%4Vh0&TnRP-l*^Y{DbKc|L|=;W(wL9O zpxQZbf_m|RXm#W4mIrA^-F*9dKUD+6kFb8D%&iaJQ@2acnJ3>Wg_B2m6zT{{ofPZx z3Z<1%C9(1f$ZYbvvsF8JI4?W$PqX{}Lu%WBoLhMNjl{Qq^NX*X0RTSxWSU&z>+}|L zlJ=95z;Dc){Gr}CcusciE|^@OMtOh(7huc9AF;Yz4qxp#>%xtfNu_<&dpj!?%B(I- zx0C76gl=ALUHcp9bW)ere`~Y-ztjUqj_a(KAzFpndtf&}LeL76-sZ7?cR}sg^>g>h zyN~NqcNO#1mR>fn5Wg0I}eL7DNE50nUQz z$9a2k@Bih3+6bO8L{)wIO1Ygz#~X+qzfyKya$FA_KCWdEqR+ti;BAXt`=Anl0nzbs z!ew-Q0rj=}j@yJj-bwu50))RG?J<1==XBO(2tNe`lmV|NcAWz4#s2{)6-Nwjqji|| z@`dy6!l>UhfKu*=2gyfZ?uY=3=e34)v+iV zu&r?vL)-;$Kr@CEC;bxYBtyF%Q^`FeRH`@Z(%kjt)W}TY)SI7r0H&cLXQY8^Mo8pP z8mEj_+QzAN4s_Hyox~96mQobEa$o`H5G!#QM_i&WUTdvHZ7P92efg%FeDrxc|4ts? zA0p*{6XODfDiQ@?Jy22XA}w*%iey{;Az)h`35Y>#gfdc832Dgk^6L`Jf2jl=?*q|` zN5|y;2mfRe6%SViPlNelv$c*Q0u2X|LjngI5#lmPChPR#^~cS`?4R3dbiN9RuBx>= zMNBppXjo`kuq}52k&EY=Ee|acSX6HgHmXICtDQ7D0+@-n0$VDcv4LF|lZ#ImIH<=$ z)FTLNEE-y*W|68zrFI~muDZ3-88h)_t!n@`Gnz9IugT)a)1-ES$szS977HEHaPhd_ zN_#azrERYP5zfAR=LE!!W z3?M&ng6vR^3MJ|v21*m8PF?!7yD<7YXNtqlsFUJUKY-G*tQ=F?nYqLhmjgFBnzQww zs_KI)DtoX%-|BbT0c=?Uck$7FmrtITp8kJuXJOh{H?L2=u_=XiX6zdhsw99)NyCg^ z{_Ca~lSh&^X$Do{z__d4KE&Gk+2q9R@>j}$7-{Ir#uwe!7z)W1~_zVY-Aed9hJtF`T&d+sPt5028X*bvXcR?-q zh)VT_NNe1sbq!$Z;fgHHT^EVR-Nv~mYfg>qYfg>a)5q!m-2Mp@iS#Q(>@>Ro0000< 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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 + + + + + + + + + + +
+
+

Functest status page

+ +
+
+
+
+ + +
+

List of last scenarios run over the last 7 days

+ + + + + + {% for scenario,iteration in scenario_stats.iteritems() -%} + + + + + {%- endfor %} +
ScenarioIteration
{{scenario}}{{iteration}}
+
+ + + + {% for scenario, iteration in scenario_stats.iteritems() -%} +
+ +
+
+ + +
+ + + {% for test in items[scenario] -%} + + {%- endfor %} + + + {% for test in items[scenario] -%} + {% if test.getCriteria() > 3 -%} + + {%- elif test.getCriteria() > 2 -%} + + {%- elif test.getCriteria() > 1 -%} + + {%- else -%} + + {%- endif %} + {%- endfor %} + +
{{test.getName() }}
+
+
+ {%- endfor %} +
+
+
diff --git a/utils/test/reporting/index-tempest-tmpl.html b/utils/test/reporting/index-tempest-tmpl.html index 24d87bee2..be0b79734 100644 --- a/utils/test/reporting/index-tempest-tmpl.html +++ b/utils/test/reporting/index-tempest-tmpl.html @@ -21,7 +21,7 @@

Tempest status page

@@ -88,4 +88,4 @@ {%- endfor %}
- \ No newline at end of file + diff --git a/utils/test/reporting/index.html b/utils/test/reporting/index.html new file mode 100644 index 000000000..af4033567 --- /dev/null +++ b/utils/test/reporting/index.html @@ -0,0 +1,52 @@ + + + + + + + + + + + +
+
+

Functest reporting page

+ +
+
+
+
+
+

Functest

+ This project develops test suites that cover functionaling test cases in OPNFV. +
The test suites are integrated in the continuation integration (CI) framework and used to evaluate/validate scenario. +
Weekly meeting: every Tuesday 8 AM UTC +
IRC chan #opnfv-testperf + +
+

Useful Links

+
  • Functest in Depth
  • +
  • Functest Repo
  • +
  • Functest Project
  • +
  • Functest Jenkins page
  • +
  • JIRA
  • + +
    +
    +
    +
    diff --git a/utils/test/reporting/reporting-status.py b/utils/test/reporting/reporting-status.py new file mode 100644 index 000000000..b27af4b14 --- /dev/null +++ b/utils/test/reporting/reporting-status.py @@ -0,0 +1,178 @@ +from urllib2 import Request, urlopen, URLError +import urllib2 +import json +import jinja2 +import os +import random + + +class TestCase(object): + def __init__(self, name, project, criteria=-1): + self.name = name + self.project = project + self.criteria = criteria + + def getName(self): + return self.name + + def getProject(self): + return self.project + + def getCriteria(self): + return self.criteria + + def setCriteria(self, criteria): + self.criteria = criteria + + +def getApiResults(case, installer): + case = case.getName() + + # 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://testresults.opnfv.org/testapi/results?case=" + case + "&period=30&installer=" + installer + #url = "http://127.0.0.1:8000/results?case=" + case + "&period=30&installer=" + installer + 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): + + results = getApiResults(case, installer) + test_results = results['test_results'] + + 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['version'] in scenario_results.keys(): + scenario_results[r['version']] = [] + scenario_results[r['version']].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 getResult(testCase, installer): + + # retrieve raw results + results = getApiResults(testCase, installer) + # let's concentrate on test results only + test_results = results['test_results'] + + # if results found, analyze them + if test_results is not None: + test_results.reverse() + + scenario_results = {} + + for r in test_results: + if not r['version'] in scenario_results.keys(): + scenario_results[r['version']] = [] + scenario_results[r['version']].append(r) + + for s, s_result in scenario_results.items(): + scenario_results[s] = s_result[0:5] + # For each scenario, we build a result object to deal with + # results, criteria and error handling + for result in scenario_results[s]: + result["creation_date"] = result["creation_date"].split(".")[0] + + # Cannot be fully generic + # need to look for specific criteria case by case + # TODO add a criteria passed/failed in DB?? + # TODO result["Success_criteria"] = result["success_criteria"] + # meanwhile just random.... + # and consider the last random arbitrarily + # 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 + # + + return int(random.random()*4)+1 + +# ****************************************************************************** +# ****************************************************************************** +# ****************************************************************************** +# ****************************************************************************** +# ****************************************************************************** + +# as the criteria are all difference, we shall use a common way to indicate +# the criteria +# 100 = 100% = all the test must be OK +# 90 = 90% = all the test must be above 90% of success rate +# TODO harmonize success criteria +# some criteria could be the duration, the success rate, the packet loss,... +# to be done case by case +# TODo create TestCriteria Object + + +installers = ["apex", "compass", "fuel", "joid"] +# init just tempest to get the scenario as all the scenarios run Temepst +tempest = TestCase("Tempest", "functest", -1) + +for installer in installers: + + scenario_results = getScenarios(tempest, installer) + scenario_stats = getScenarioStats(scenario_results) + + items = {} + + for s, s_result in scenario_results.items(): + + vPing = TestCase("vPing", "functest") + vPing_userdata = TestCase("vPing_userdata", "functest") + tempest = TestCase("Tempest", "functest") + rally = TestCase("Rally", "functest") + odl = TestCase("ODL", "functest") + onos = TestCase("ONOS", "functest") + ovno = TestCase("OVNO", "functest") + vIMS = TestCase("vIMS", "functest") + doctor = TestCase("doctor-notification", "doctor") + promise = TestCase("promise", "promise") + odl_vpn = TestCase("ODL VPN Service tests", "sdnvpn") + bgpvpn_api = TestCase("OpenStack Neutron BGPVPN API extension tests", + "sdnvpn") + testCases = [vPing, vPing_userdata, tempest, rally, odl, onos, vIMS, + doctor, promise] + + for testCase in testCases: + result = getResult(testCase, installer) + testCase.setCriteria(result) + # print "case %s (%s) = %s " % (testCase.getName(), s, result) + items[s] = testCases + + templateLoader = jinja2.FileSystemLoader(os.path.dirname(os.path.abspath(__file__))) + templateEnv = jinja2.Environment(loader=templateLoader) + + TEMPLATE_FILE = "index-status-tmpl.html" + template = templateEnv.get_template(TEMPLATE_FILE) + + outputText = template.render(scenario_stats=scenario_stats, + items=items, + installer=installer) + + with open("index-status-" + installer + ".html", "wb") as fh: + fh.write(outputText) -- 2.16.6