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