Add vsperf in reporting
[releng.git] / utils / test / reporting / reporting / utils / reporting_utils.py
1 #!/usr/bin/python
2 #
3 # This program and the accompanying materials
4 # are made available under the terms of the Apache License, Version 2.0
5 # which accompanies this distribution, and is available at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 from urllib2 import Request, urlopen, URLError
10 import logging
11 import json
12 import os
13 import requests
14 import pdfkit
15 import yaml
16
17
18 # ----------------------------------------------------------
19 #
20 #               YAML UTILS
21 #
22 # -----------------------------------------------------------
23 def get_parameter_from_yaml(parameter, config_file):
24     """
25     Returns the value of a given parameter in file.yaml
26     parameter must be given in string format with dots
27     Example: general.openstack.image_name
28     """
29     with open(config_file) as my_file:
30         file_yaml = yaml.safe_load(my_file)
31     my_file.close()
32     value = file_yaml
33     for element in parameter.split("."):
34         value = value.get(element)
35         if value is None:
36             raise ValueError("The parameter %s is not defined in"
37                              " reporting.yaml" % parameter)
38     return value
39
40
41 def get_config(parameter):
42     """
43     Get configuration parameter from yaml configuration file
44     """
45     yaml_ = os.environ["CONFIG_REPORTING_YAML"]
46     return get_parameter_from_yaml(parameter, yaml_)
47
48
49 # ----------------------------------------------------------
50 #
51 #               LOGGER UTILS
52 #
53 # -----------------------------------------------------------
54 def getLogger(module):
55     """
56     Get Logger
57     """
58     log_formatter = logging.Formatter("%(asctime)s [" +
59                                       module +
60                                       "] [%(levelname)-5.5s]  %(message)s")
61     logger = logging.getLogger()
62     log_file = get_config('general.log.log_file')
63     log_level = get_config('general.log.log_level')
64
65     file_handler = logging.FileHandler("{0}/{1}".format('.', log_file))
66     file_handler.setFormatter(log_formatter)
67     logger.addHandler(file_handler)
68
69     console_handler = logging.StreamHandler()
70     console_handler.setFormatter(log_formatter)
71     logger.addHandler(console_handler)
72     logger.setLevel(log_level)
73     return logger
74
75
76 # ----------------------------------------------------------
77 #
78 #               REPORTING UTILS
79 #
80 # -----------------------------------------------------------
81 def getApiResults(case, installer, scenario, version):
82     """
83     Get Results by calling the API
84     """
85     results = json.dumps([])
86     # to remove proxy (to be removed at the end for local test only)
87     # proxy_handler = urllib2.ProxyHandler({})
88     # opener = urllib2.build_opener(proxy_handler)
89     # urllib2.install_opener(opener)
90     # url = "http://127.0.0.1:8000/results?case=" + case + \
91     #       "&period=30&installer=" + installer
92     period = get_config('general.period')
93     url_base = get_config('testapi.url')
94     nb_tests = get_config('general.nb_iteration_tests_success_criteria')
95
96     url = ("http://" + url_base + "?case=" + case +
97            "&period=" + str(period) + "&installer=" + installer +
98            "&scenario=" + scenario + "&version=" + version +
99            "&last=" + str(nb_tests))
100     request = Request(url)
101
102     try:
103         response = urlopen(request)
104         k = response.read()
105         results = json.loads(k)
106     except URLError:
107         print "Error when retrieving results form API"
108
109     return results
110
111
112 def getScenarios(project, case, installer, version):
113     """
114     Get the list of Scenarios
115     """
116
117     period = get_config('general.period')
118     url_base = get_config('testapi.url')
119
120     url = ("http://" + url_base +
121            "?installer=" + installer +
122            "&period=" + str(period))
123
124     if version is not None:
125         url += "&version=" + version
126
127     if project is not None:
128         url += "&project=" + project
129
130     if case is not None:
131         url += "&case=" + case
132
133     try:
134         request = Request(url)
135         response = urlopen(request)
136         k = response.read()
137         results = json.loads(k)
138         test_results = results['results']
139         try:
140             page = results['pagination']['total_pages']
141             if page > 1:
142                 test_results = []
143                 for i in range(1, page + 1):
144                     url_page = url + "&page=" + str(i)
145                     request = Request(url_page)
146                     response = urlopen(request)
147                     k = response.read()
148                     results = json.loads(k)
149                     test_results += results['results']
150         except KeyError:
151             print "No pagination detected"
152     except URLError as err:
153         print 'Got an error code: {}'.format(err)
154
155     if test_results is not None:
156         test_results.reverse()
157         scenario_results = {}
158
159         for my_result in test_results:
160             # Retrieve all the scenarios per installer
161             if not my_result['scenario'] in scenario_results.keys():
162                 scenario_results[my_result['scenario']] = []
163             # Do we consider results from virtual pods ...
164             # Do we consider results for non HA scenarios...
165             exclude_virtual_pod = get_config('functest.exclude_virtual')
166             exclude_noha = get_config('functest.exclude_noha')
167             if ((exclude_virtual_pod and "virtual" in my_result['pod_name']) or
168                     (exclude_noha and "noha" in my_result['scenario'])):
169                 print "exclude virtual pod results..."
170             else:
171                 scenario_results[my_result['scenario']].append(my_result)
172
173     return scenario_results
174
175
176 def getScenarioStats(scenario_results):
177     """
178     Get the number of occurence of scenarios over the defined PERIOD
179     """
180     scenario_stats = {}
181     for res_k, res_v in scenario_results.iteritems():
182         scenario_stats[res_k] = len(res_v)
183     return scenario_stats
184
185
186 def getScenarioStatus(installer, version):
187     """
188     Get the status of a scenariofor Yardstick
189     they used criteria SUCCESS (default: PASS)
190     """
191     period = get_config('general.period')
192     url_base = get_config('testapi.url')
193
194     url = ("http://" + url_base + "?case=scenario_status" +
195            "&installer=" + installer +
196            "&version=" + version + "&period=" + str(period))
197     request = Request(url)
198
199     try:
200         response = urlopen(request)
201         k = response.read()
202         response.close()
203         results = json.loads(k)
204         test_results = results['results']
205     except URLError:
206         print "GetScenarioStatus: error when calling the API"
207
208     scenario_results = {}
209     result_dict = {}
210     if test_results is not None:
211         for test_r in test_results:
212             if (test_r['stop_date'] != 'None' and
213                     test_r['criteria'] is not None):
214                 if not test_r['scenario'] in scenario_results.keys():
215                     scenario_results[test_r['scenario']] = []
216                 scenario_results[test_r['scenario']].append(test_r)
217
218         for scen_k, scen_v in scenario_results.items():
219             # scenario_results[k] = v[:LASTEST_TESTS]
220             s_list = []
221             for element in scen_v:
222                 if element['criteria'] == 'SUCCESS':
223                     s_list.append(1)
224                 else:
225                     s_list.append(0)
226             result_dict[scen_k] = s_list
227
228     # return scenario_results
229     return result_dict
230
231
232 def getQtipResults(version, installer):
233     """
234     Get QTIP results
235     """
236     period = get_config('qtip.period')
237     url_base = get_config('testapi.url')
238
239     url = ("http://" + url_base + "?project=qtip" +
240            "&installer=" + installer +
241            "&version=" + version + "&period=" + str(period))
242     request = Request(url)
243
244     try:
245         response = urlopen(request)
246         k = response.read()
247         response.close()
248         results = json.loads(k)['results']
249     except URLError as err:
250         print 'Got an error code: {}'.format(err)
251
252     result_dict = {}
253     if results:
254         for r in results:
255             key = '{}/{}'.format(r['pod_name'], r['scenario'])
256             if key not in result_dict.keys():
257                 result_dict[key] = []
258             result_dict[key].append(r['details']['score'])
259
260     # return scenario_results
261     return result_dict
262
263
264 def getNbtestOk(results):
265     """
266     based on default value (PASS) count the number of test OK
267     """
268     nb_test_ok = 0
269     for my_result in results:
270         for res_k, res_v in my_result.iteritems():
271             try:
272                 if "PASS" in res_v:
273                     nb_test_ok += 1
274             except Exception:
275                 print "Cannot retrieve test status"
276     return nb_test_ok
277
278
279 def getResult(testCase, installer, scenario, version):
280     """
281     Get Result  for a given Functest Testcase
282     """
283     # retrieve raw results
284     results = getApiResults(testCase, installer, scenario, version)
285     # let's concentrate on test results only
286     test_results = results['results']
287
288     # if results found, analyze them
289     if test_results is not None:
290         test_results.reverse()
291
292         scenario_results = []
293
294         # print " ---------------- "
295         # print test_results
296         # print " ---------------- "
297         # print "nb of results:" + str(len(test_results))
298
299         for res_r in test_results:
300             # print r["start_date"]
301             # print r["criteria"]
302             scenario_results.append({res_r["start_date"]: res_r["criteria"]})
303         # sort results
304         scenario_results.sort()
305         # 4 levels for the results
306         # 3: 4+ consecutive runs passing the success criteria
307         # 2: <4 successful consecutive runs but passing the criteria
308         # 1: close to pass the success criteria
309         # 0: 0% success, not passing
310         # -1: no run available
311         test_result_indicator = 0
312         nbTestOk = getNbtestOk(scenario_results)
313
314         # print "Nb test OK (last 10 days):"+ str(nbTestOk)
315         # check that we have at least 4 runs
316         if len(scenario_results) < 1:
317             # No results available
318             test_result_indicator = -1
319         elif nbTestOk < 1:
320             test_result_indicator = 0
321         elif nbTestOk < 2:
322             test_result_indicator = 1
323         else:
324             # Test the last 4 run
325             if len(scenario_results) > 3:
326                 last4runResults = scenario_results[-4:]
327                 nbTestOkLast4 = getNbtestOk(last4runResults)
328                 # print "Nb test OK (last 4 run):"+ str(nbTestOkLast4)
329                 if nbTestOkLast4 > 3:
330                     test_result_indicator = 3
331                 else:
332                     test_result_indicator = 2
333             else:
334                 test_result_indicator = 2
335     return test_result_indicator
336
337
338 def getJenkinsUrl(build_tag):
339     """
340     Get Jenkins url_base corespoding to the last test CI run
341     e.g. jenkins-functest-apex-apex-daily-colorado-daily-colorado-246
342     id = 246
343     jenkins-functest-compass-huawei-pod5-daily-master-136
344     id = 136
345     note it is linked to jenkins format
346     if this format changes...function to be adapted....
347     """
348     url_base = get_config('functest.jenkins_url')
349     try:
350         build_id = [int(s) for s in build_tag.split("-") if s.isdigit()]
351         url_id = (build_tag[8:-(len(str(build_id[0])) + 1)] +
352                   "/" + str(build_id[0]))
353         jenkins_url = url_base + url_id + "/console"
354     except Exception:
355         print 'Impossible to get jenkins url:'
356
357     if "jenkins-" not in build_tag:
358         jenkins_url = None
359
360     return jenkins_url
361
362
363 def getScenarioPercent(scenario_score, scenario_criteria):
364     """
365     Get success rate of the scenario (in %)
366     """
367     score = 0.0
368     try:
369         score = float(scenario_score) / float(scenario_criteria) * 100
370     except Exception:
371         print 'Impossible to calculate the percentage score'
372     return score
373
374
375 # *********
376 # Functest
377 # *********
378 def getFunctestConfig(version=""):
379     """
380     Get Functest configuration
381     """
382     config_file = get_config('functest.test_conf') + version
383     response = requests.get(config_file)
384     return yaml.safe_load(response.text)
385
386
387 def getArchitectures(scenario_results):
388     """
389     Get software architecture (x86 or Aarch64)
390     """
391     supported_arch = ['x86']
392     if len(scenario_results) > 0:
393         for scenario_result in scenario_results.values():
394             for value in scenario_result:
395                 if "armband" in value['build_tag']:
396                     supported_arch.append('aarch64')
397                     return supported_arch
398     return supported_arch
399
400
401 def filterArchitecture(results, architecture):
402     """
403     Restrict the list of results based on given architecture
404     """
405     filtered_results = {}
406     for name, res in results.items():
407         filtered_values = []
408         for value in res:
409             if architecture is "x86":
410                 # drop aarch64 results
411                 if ("armband" not in value['build_tag']):
412                     filtered_values.append(value)
413             elif architecture is "aarch64":
414                 # drop x86 results
415                 if ("armband" in value['build_tag']):
416                     filtered_values.append(value)
417         if (len(filtered_values) > 0):
418             filtered_results[name] = filtered_values
419     return filtered_results
420
421
422 # *********
423 # Yardstick
424 # *********
425 def subfind(given_list, pattern_list):
426     """
427     Yardstick util function
428     """
429     LASTEST_TESTS = get_config('general.nb_iteration_tests_success_criteria')
430     for i in range(len(given_list)):
431         if given_list[i] == pattern_list[0] and \
432                 given_list[i:i + LASTEST_TESTS] == pattern_list:
433             return True
434     return False
435
436
437 def _get_percent(status):
438     """
439     Yardstick util function to calculate success rate
440     """
441     if status * 100 % 6:
442         return round(float(status) * 100 / 6, 1)
443     else:
444         return status * 100 / 6
445
446
447 def get_percent(four_list, ten_list):
448     """
449     Yardstick util function to calculate success rate
450     """
451     four_score = 0
452     ten_score = 0
453
454     for res_v in four_list:
455         four_score += res_v
456     for res_v in ten_list:
457         ten_score += res_v
458
459     LASTEST_TESTS = get_config('general.nb_iteration_tests_success_criteria')
460     if four_score == LASTEST_TESTS:
461         status = 6
462     elif subfind(ten_list, [1, 1, 1, 1]):
463         status = 5
464     elif ten_score == 0:
465         status = 0
466     else:
467         status = four_score + 1
468
469     return _get_percent(status)
470
471
472 def _test():
473     """
474     Yardstick util function (test)
475     """
476     status = getScenarioStatus("compass", "master")
477     print "status:++++++++++++++++++++++++"
478     print json.dumps(status, indent=4)
479
480
481 # ----------------------------------------------------------
482 #
483 #               Export
484 #
485 # -----------------------------------------------------------
486
487 def export_csv(scenario_file_name, installer, version):
488     """
489     Generate sub files based on scenario_history.txt
490     """
491     scenario_installer_file_name = ("./display/" + version +
492                                     "/functest/scenario_history_" +
493                                     installer + ".csv")
494     scenario_installer_file = open(scenario_installer_file_name, "a")
495     with open(scenario_file_name, "r") as scenario_file:
496         scenario_installer_file.write("date,scenario,installer,detail,score\n")
497         for line in scenario_file:
498             if installer in line:
499                 scenario_installer_file.write(line)
500     scenario_installer_file.close
501
502
503 def generate_csv(scenario_file):
504     """
505     Generate sub files based on scenario_history.txt
506     """
507     import shutil
508     csv_file = scenario_file.replace('txt', 'csv')
509     shutil.copy2(scenario_file, csv_file)
510
511
512 def export_pdf(pdf_path, pdf_doc_name):
513     """
514     Export results to pdf
515     """
516     try:
517         pdfkit.from_file(pdf_path, pdf_doc_name)
518     except IOError:
519         print "Error but pdf generated anyway..."
520     except Exception:
521         print "impossible to generate PDF"