Fix print when push_results_to_db fails
[functest.git] / utils / functest_utils.py
1 #!/usr/bin/env python
2 #
3 # jose.lausuch@ericsson.com
4 # valentin.boucher@orange.com
5 # All rights reserved. This program and the accompanying materials
6 # are made available under the terms of the Apache License, Version 2.0
7 # which accompanies this distribution, and is available at
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10
11 """ global variables """
12
13 from datetime import datetime as dt
14 import json
15 import os
16 import os.path
17 import re
18 import shutil
19 import subprocess
20 import sys
21 import urllib2
22 import dns.resolver
23
24 import functest.ci.tier_builder as tb
25 from git import Repo
26 import requests
27 import yaml
28
29
30 REPOS_DIR = os.getenv('repos_dir')
31 FUNCTEST_REPO = ("%s/functest/" % REPOS_DIR)
32
33
34 # ----------------------------------------------------------
35 #
36 #               INTERNET UTILS
37 #
38 # -----------------------------------------------------------
39 def check_internet_connectivity(url='http://www.opnfv.org/'):
40     """
41     Check if there is access to the internet
42     """
43     try:
44         urllib2.urlopen(url, timeout=5)
45         return True
46     except urllib2.URLError:
47         return False
48
49
50 def download_url(url, dest_path):
51     """
52     Download a file to a destination path given a URL
53     """
54     name = url.rsplit('/')[-1]
55     dest = dest_path + "/" + name
56     try:
57         response = urllib2.urlopen(url)
58     except (urllib2.HTTPError, urllib2.URLError):
59         return False
60
61     with open(dest, 'wb') as f:
62         shutil.copyfileobj(response, f)
63     return True
64
65
66 # ----------------------------------------------------------
67 #
68 #               CI UTILS
69 #
70 # -----------------------------------------------------------
71 def get_git_branch(repo_path):
72     """
73     Get git branch name
74     """
75     repo = Repo(repo_path)
76     branch = repo.active_branch
77     return branch.name
78
79
80 def get_installer_type(logger=None):
81     """
82     Get installer type (fuel, apex, joid, compass)
83     """
84     try:
85         installer = os.environ['INSTALLER_TYPE']
86     except KeyError:
87         if logger:
88             logger.error("Impossible to retrieve the installer type")
89         installer = "Unknown_installer"
90
91     return installer
92
93
94 def get_scenario(logger=None):
95     """
96     Get scenario
97     """
98     try:
99         scenario = os.environ['DEPLOY_SCENARIO']
100     except KeyError:
101         if logger:
102             logger.error("Impossible to retrieve the scenario")
103         scenario = "Unknown_scenario"
104
105     return scenario
106
107
108 def get_version(logger=None):
109     """
110     Get version
111     """
112     # Use the build tag to retrieve the version
113     # By default version is unknown
114     # if launched through CI the build tag has the following format
115     # jenkins-<project>-<installer>-<pod>-<job>-<branch>-<id>
116     # e.g. jenkins-functest-fuel-opnfv-jump-2-daily-master-190
117     # use regex to match branch info
118     rule = "daily-(.+?)-[0-9]*"
119     build_tag = get_build_tag(logger)
120     m = re.search(rule, build_tag)
121     if m:
122         return m.group(1)
123     else:
124         return "unknown"
125
126
127 def get_pod_name(logger=None):
128     """
129     Get PoD Name from env variable NODE_NAME
130     """
131     try:
132         return os.environ['NODE_NAME']
133     except KeyError:
134         if logger:
135             logger.error(
136                 "Unable to retrieve the POD name from environment. " +
137                 "Using pod name 'unknown-pod'")
138         return "unknown-pod"
139
140
141 def get_build_tag(logger=None):
142     """
143     Get build tag of jenkins jobs
144     """
145     try:
146         build_tag = os.environ['BUILD_TAG']
147     except KeyError:
148         if logger:
149             logger.error("Impossible to retrieve the build tag")
150         build_tag = "unknown_build_tag"
151
152     return build_tag
153
154
155 def get_db_url(logger=None):
156     """
157     Returns DB URL
158     """
159     with open(os.environ["CONFIG_FUNCTEST_YAML"]) as f:
160         functest_yaml = yaml.safe_load(f)
161     f.close()
162     db_url = functest_yaml.get("results").get("test_db_url")
163     return db_url
164
165
166 def logger_test_results(logger, project, case_name, status, details):
167     pod_name = get_pod_name(logger)
168     scenario = get_scenario(logger)
169     version = get_version(logger)
170     build_tag = get_build_tag(logger)
171
172     logger.info("\n"
173                 "****************************************\n"
174                 "\t %(p)s/%(n)s results \n\n"
175                 "****************************************\n"
176                 "DB:\t%(db)s\n"
177                 "pod:\t%(pod)s\n"
178                 "version:\t%(v)s\n"
179                 "scenario:\t%(s)s\n"
180                 "status:\t%(c)s\n"
181                 "build tag:\t%(b)s\n"
182                 "details:\t%(d)s\n"
183                 % {
184                     'p': project,
185                     'n': case_name,
186                     'db': get_db_url(),
187                     'pod': pod_name,
188                     'v': version,
189                     's': scenario,
190                     'c': status,
191                     'b': build_tag,
192                     'd': details,
193                 })
194
195
196 def push_results_to_db(project, case_name, logger,
197                        start_date, stop_date, criteria, details):
198     """
199     POST results to the Result target DB
200     """
201     # Retrieve params from CI and conf
202     url = get_db_url(logger) + "/results"
203     installer = get_installer_type(logger)
204     scenario = get_scenario(logger)
205     version = get_version(logger)
206     pod_name = get_pod_name(logger)
207     build_tag = get_build_tag(logger)
208     test_start = dt.fromtimestamp(start_date).strftime('%Y-%m-%d %H:%M:%S')
209     test_stop = dt.fromtimestamp(stop_date).strftime('%Y-%m-%d %H:%M:%S')
210
211     params = {"project_name": project, "case_name": case_name,
212               "pod_name": pod_name, "installer": installer,
213               "version": version, "scenario": scenario, "criteria": criteria,
214               "build_tag": build_tag, "start_date": test_start,
215               "stop_date": test_stop, "details": details}
216
217     headers = {'Content-Type': 'application/json'}
218     try:
219         r = requests.post(url, data=json.dumps(params), headers=headers)
220         if logger:
221             logger.debug(r)
222         r.raise_for_status()
223         return True
224     except Exception, e:
225         print("Error [push_results_to_db('%s', '%s', '%s', '%s',"
226               "'%s', '%s', '%s', '%s', '%s')]:" %
227               (url, project, case_name, pod_name, version,
228                scenario, criteria, build_tag, details)), e
229         return False
230
231
232 def get_resolvconf_ns():
233     """
234     Get nameservers from current resolv.conf
235     """
236     nameservers = []
237     rconf = open("/etc/resolv.conf", "r")
238     line = rconf.readline()
239     resolver = dns.resolver.Resolver()
240     while line:
241         ip = re.search(r"\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b", line)
242         if ip:
243             resolver.nameservers = [str(ip)]
244             try:
245                 result = resolver.query('opnfv.org')[0]
246                 if result != "":
247                     nameservers.append(ip.group())
248             except dns.exception.Timeout:
249                 pass
250         line = rconf.readline()
251     return nameservers
252
253
254 def get_ci_envvars():
255     """
256     Get the CI env variables
257     """
258     ci_env_var = {
259         "installer": os.environ.get('INSTALLER_TYPE'),
260         "scenario": os.environ.get('DEPLOY_SCENARIO')}
261     return ci_env_var
262
263
264 def execute_command(cmd, logger=None,
265                     exit_on_error=True,
266                     info=False,
267                     error_msg="",
268                     verbose=True):
269     if not error_msg:
270         error_msg = ("The command '%s' failed." % cmd)
271     msg_exec = ("Executing command: '%s'" % cmd)
272     if verbose:
273         if logger:
274             if info:
275                 logger.info(msg_exec)
276             else:
277                 logger.debug(msg_exec)
278         else:
279             print(msg_exec)
280     p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
281     for line in iter(p.stdout.readline, b''):
282         line = line.replace('\n', '')
283         if logger:
284             if info:
285                 logger.info(line)
286             else:
287                 logger.debug(line)
288         else:
289             print line
290     p.stdout.close()
291     returncode = p.wait()
292     if returncode != 0:
293         if verbose:
294             if logger:
295                 logger.error(error_msg)
296             else:
297                 print(error_msg)
298         if exit_on_error:
299             sys.exit(1)
300
301     return returncode
302
303
304 def get_deployment_dir(logger=None):
305     """
306     Returns current Rally deployment directory
307     """
308     with open(os.environ["CONFIG_FUNCTEST_YAML"]) as f:
309         functest_yaml = yaml.safe_load(f)
310     f.close()
311     deployment_name = functest_yaml.get("rally").get("deployment_name")
312     rally_dir = functest_yaml.get("general").get("directories").get(
313         "dir_rally_inst")
314     cmd = ("rally deployment list | awk '/" + deployment_name +
315            "/ {print $2}'")
316     p = subprocess.Popen(cmd, shell=True,
317                          stdout=subprocess.PIPE,
318                          stderr=subprocess.STDOUT)
319     deployment_uuid = p.stdout.readline().rstrip()
320     if deployment_uuid == "":
321         if logger:
322             logger.error("Rally deployment not found.")
323         exit(-1)
324     deployment_dir = (rally_dir + "/tempest/for-deployment-" +
325                       deployment_uuid)
326     return deployment_dir
327
328
329 def get_criteria_by_test(testname):
330     criteria = ""
331     file = FUNCTEST_REPO + "/ci/testcases.yaml"
332     tiers = tb.TierBuilder("", "", file)
333     for tier in tiers.get_tiers():
334         for test in tier.get_tests():
335             if test.get_name() == testname:
336                 criteria = test.get_criteria()
337
338     return criteria
339
340
341 # ----------------------------------------------------------
342 #
343 #               YAML UTILS
344 #
345 # -----------------------------------------------------------
346 def get_parameter_from_yaml(parameter, file=None):
347     """
348     Returns the value of a given parameter in config_functest.yaml
349     parameter must be given in string format with dots
350     Example: general.openstack.image_name
351     """
352     if file is None:
353         file = os.environ["CONFIG_FUNCTEST_YAML"]
354     with open(file) as f:
355         functest_yaml = yaml.safe_load(f)
356     f.close()
357     value = functest_yaml
358     for element in parameter.split("."):
359         value = value.get(element)
360         if value is None:
361             raise ValueError("The parameter %s is not defined in"
362                              " config_functest.yaml" % parameter)
363     return value
364
365
366 def check_success_rate(case_name, success_rate):
367     success_rate = float(success_rate)
368     criteria = get_criteria_by_test(case_name)
369
370     def get_criteria_value(op):
371         return float(criteria.split(op)[1].rstrip('%'))
372
373     status = 'FAIL'
374     ops = ['==', '>=']
375     for op in ops:
376         if op in criteria:
377             c_value = get_criteria_value(op)
378             if eval("%s %s %s" % (success_rate, op, c_value)):
379                 status = 'PASS'
380             break
381
382     return status
383
384
385 def merge_dicts(dict1, dict2):
386     for k in set(dict1.keys()).union(dict2.keys()):
387         if k in dict1 and k in dict2:
388             if isinstance(dict1[k], dict) and isinstance(dict2[k], dict):
389                 yield (k, dict(merge_dicts(dict1[k], dict2[k])))
390             else:
391                 yield (k, dict2[k])
392         elif k in dict1:
393             yield (k, dict1[k])
394         else:
395             yield (k, dict2[k])
396
397
398 def check_test_result(test_name, ret, start_time, stop_time):
399     def get_criteria_value():
400         return get_criteria_by_test(test_name).split('==')[1].strip()
401
402     status = 'FAIL'
403     if str(ret) == get_criteria_value():
404         status = 'PASS'
405
406     details = {
407         'timestart': start_time,
408         'duration': round(stop_time - start_time, 1),
409         'status': status,
410     }
411
412     return status, details