Merge "bug-fix: do not have permission to execute sudo pip"
[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     x86 = 'x86'
209     aarch64 = 'aarch64'
210     scenario_results = {x86: {}, aarch64: {}}
211     result_dict = {x86: {}, aarch64: {}}
212     if test_results is not None:
213         for test_r in test_results:
214             if (test_r['stop_date'] != 'None' and
215                     test_r['criteria'] is not None):
216                 scenario_name = test_r['scenario']
217                 if 'arm' in test_r['pod_name']:
218                     if not test_r['scenario'] in scenario_results[aarch64]:
219                         scenario_results[aarch64][scenario_name] = []
220                     scenario_results[aarch64][scenario_name].append(test_r)
221                 else:
222                     if not test_r['scenario'] in scenario_results[x86]:
223                         scenario_results[x86][scenario_name] = []
224                     scenario_results[x86][scenario_name].append(test_r)
225
226         for key in scenario_results:
227             for scen_k, scen_v in scenario_results[key].items():
228                 # scenario_results[k] = v[:LASTEST_TESTS]
229                 s_list = []
230                 for element in scen_v:
231                     if element['criteria'] == 'SUCCESS':
232                         s_list.append(1)
233                     else:
234                         s_list.append(0)
235                 result_dict[key][scen_k] = s_list
236
237     # return scenario_results
238     return result_dict
239
240
241 def getQtipResults(version, installer):
242     """
243     Get QTIP results
244     """
245     period = get_config('qtip.period')
246     url_base = get_config('testapi.url')
247
248     url = ("http://" + url_base + "?project=qtip" +
249            "&installer=" + installer +
250            "&version=" + version + "&period=" + str(period))
251     request = Request(url)
252
253     try:
254         response = urlopen(request)
255         k = response.read()
256         response.close()
257         results = json.loads(k)['results']
258     except URLError as err:
259         print 'Got an error code: {}'.format(err)
260
261     result_dict = {}
262     if results:
263         for r in results:
264             key = '{}/{}'.format(r['pod_name'], r['scenario'])
265             if key not in result_dict.keys():
266                 result_dict[key] = []
267             result_dict[key].append(r['details']['score'])
268
269     # return scenario_results
270     return result_dict
271
272
273 def getNbtestOk(results):
274     """
275     based on default value (PASS) count the number of test OK
276     """
277     nb_test_ok = 0
278     for my_result in results:
279         for res_k, res_v in my_result.iteritems():
280             try:
281                 if "PASS" in res_v:
282                     nb_test_ok += 1
283             except Exception:
284                 print "Cannot retrieve test status"
285     return nb_test_ok
286
287
288 def getResult(testCase, installer, scenario, version):
289     """
290     Get Result  for a given Functest Testcase
291     """
292     # retrieve raw results
293     results = getApiResults(testCase, installer, scenario, version)
294     # let's concentrate on test results only
295     test_results = results['results']
296
297     # if results found, analyze them
298     if test_results is not None:
299         test_results.reverse()
300
301         scenario_results = []
302
303         # print " ---------------- "
304         # print test_results
305         # print " ---------------- "
306         # print "nb of results:" + str(len(test_results))
307
308         for res_r in test_results:
309             # print r["start_date"]
310             # print r["criteria"]
311             scenario_results.append({res_r["start_date"]: res_r["criteria"]})
312         # sort results
313         scenario_results.sort()
314         # 4 levels for the results
315         # 3: 4+ consecutive runs passing the success criteria
316         # 2: <4 successful consecutive runs but passing the criteria
317         # 1: close to pass the success criteria
318         # 0: 0% success, not passing
319         # -1: no run available
320         test_result_indicator = 0
321         nbTestOk = getNbtestOk(scenario_results)
322
323         # print "Nb test OK (last 10 days):"+ str(nbTestOk)
324         # check that we have at least 4 runs
325         if len(scenario_results) < 1:
326             # No results available
327             test_result_indicator = -1
328         elif nbTestOk < 1:
329             test_result_indicator = 0
330         elif nbTestOk < 2:
331             test_result_indicator = 1
332         else:
333             # Test the last 4 run
334             if len(scenario_results) > 3:
335                 last4runResults = scenario_results[-4:]
336                 nbTestOkLast4 = getNbtestOk(last4runResults)
337                 # print "Nb test OK (last 4 run):"+ str(nbTestOkLast4)
338                 if nbTestOkLast4 > 3:
339                     test_result_indicator = 3
340                 else:
341                     test_result_indicator = 2
342             else:
343                 test_result_indicator = 2
344     return test_result_indicator
345
346
347 def getJenkinsUrl(build_tag):
348     """
349     Get Jenkins url_base corespoding to the last test CI run
350     e.g. jenkins-functest-apex-apex-daily-colorado-daily-colorado-246
351     id = 246
352     jenkins-functest-compass-huawei-pod5-daily-master-136
353     id = 136
354     note it is linked to jenkins format
355     if this format changes...function to be adapted....
356     """
357     url_base = get_config('functest.jenkins_url')
358     try:
359         build_id = [int(s) for s in build_tag.split("-") if s.isdigit()]
360         url_id = (build_tag[8:-(len(str(build_id[0])) + 1)] +
361                   "/" + str(build_id[0]))
362         jenkins_url = url_base + url_id + "/console"
363     except Exception:
364         print 'Impossible to get jenkins url:'
365
366     if "jenkins-" not in build_tag:
367         jenkins_url = None
368
369     return jenkins_url
370
371
372 def getScenarioPercent(scenario_score, scenario_criteria):
373     """
374     Get success rate of the scenario (in %)
375     """
376     score = 0.0
377     try:
378         score = float(scenario_score) / float(scenario_criteria) * 100
379     except Exception:
380         print 'Impossible to calculate the percentage score'
381     return score
382
383
384 # *********
385 # Functest
386 # *********
387 def getFunctestConfig(version=""):
388     """
389     Get Functest configuration
390     """
391     config_file = get_config('functest.test_conf') + version
392     response = requests.get(config_file)
393     return yaml.safe_load(response.text)
394
395
396 def getArchitectures(scenario_results):
397     """
398     Get software architecture (x86 or Aarch64)
399     """
400     supported_arch = ['x86']
401     if len(scenario_results) > 0:
402         for scenario_result in scenario_results.values():
403             for value in scenario_result:
404                 if "armband" in value['build_tag']:
405                     supported_arch.append('aarch64')
406                     return supported_arch
407     return supported_arch
408
409
410 def filterArchitecture(results, architecture):
411     """
412     Restrict the list of results based on given architecture
413     """
414     filtered_results = {}
415     for name, res in results.items():
416         filtered_values = []
417         for value in res:
418             if architecture is "x86":
419                 # drop aarch64 results
420                 if ("armband" not in value['build_tag']):
421                     filtered_values.append(value)
422             elif architecture is "aarch64":
423                 # drop x86 results
424                 if ("armband" in value['build_tag']):
425                     filtered_values.append(value)
426         if (len(filtered_values) > 0):
427             filtered_results[name] = filtered_values
428     return filtered_results
429
430
431 # *********
432 # Yardstick
433 # *********
434 def subfind(given_list, pattern_list):
435     """
436     Yardstick util function
437     """
438     LASTEST_TESTS = get_config('general.nb_iteration_tests_success_criteria')
439     for i in range(len(given_list)):
440         if given_list[i] == pattern_list[0] and \
441                 given_list[i:i + LASTEST_TESTS] == pattern_list:
442             return True
443     return False
444
445
446 def _get_percent(status):
447     """
448     Yardstick util function to calculate success rate
449     """
450     if status * 100 % 6:
451         return round(float(status) * 100 / 6, 1)
452     else:
453         return status * 100 / 6
454
455
456 def get_percent(four_list, ten_list):
457     """
458     Yardstick util function to calculate success rate
459     """
460     four_score = 0
461     ten_score = 0
462
463     for res_v in four_list:
464         four_score += res_v
465     for res_v in ten_list:
466         ten_score += res_v
467
468     LASTEST_TESTS = get_config('general.nb_iteration_tests_success_criteria')
469     if four_score == LASTEST_TESTS:
470         status = 6
471     elif subfind(ten_list, [1, 1, 1, 1]):
472         status = 5
473     elif ten_score == 0:
474         status = 0
475     else:
476         status = four_score + 1
477
478     return _get_percent(status)
479
480
481 def _test():
482     """
483     Yardstick util function (test)
484     """
485     status = getScenarioStatus("compass", "master")
486     print "status:++++++++++++++++++++++++"
487     print json.dumps(status, indent=4)
488
489
490 # ----------------------------------------------------------
491 #
492 #               Export
493 #
494 # -----------------------------------------------------------
495
496 def export_csv(scenario_file_name, installer, version):
497     """
498     Generate sub files based on scenario_history.txt
499     """
500     scenario_installer_file_name = ("./display/" + version +
501                                     "/functest/scenario_history_" +
502                                     installer + ".csv")
503     scenario_installer_file = open(scenario_installer_file_name, "a")
504     with open(scenario_file_name, "r") as scenario_file:
505         scenario_installer_file.write("date,scenario,installer,detail,score\n")
506         for line in scenario_file:
507             if installer in line:
508                 scenario_installer_file.write(line)
509     scenario_installer_file.close
510
511
512 def generate_csv(scenario_file):
513     """
514     Generate sub files based on scenario_history.txt
515     """
516     import shutil
517     csv_file = scenario_file.replace('txt', 'csv')
518     shutil.copy2(scenario_file, csv_file)
519
520
521 def export_pdf(pdf_path, pdf_doc_name):
522     """
523     Export results to pdf
524     """
525     try:
526         pdfkit.from_file(pdf_path, pdf_doc_name)
527     except IOError:
528         print "Error but pdf generated anyway..."
529     except Exception:
530         print "impossible to generate PDF"