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