Merge "[odl-sfc] Add timeout to get_vnf_id and create_vnf functions"
[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 functools
11 import json
12 import os
13 import re
14 import shutil
15 import subprocess
16 import sys
17 import time
18 import urllib2
19 from datetime import datetime as dt
20
21 import dns.resolver
22 import requests
23 import yaml
24 from git import Repo
25
26 from functest.utils import decorators
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.info("Impossible to retrieve the scenario."
99                     "Use default os-nosdn-nofeature-noha")
100         scenario = "os-nosdn-nofeature-noha"
101
102     return scenario
103
104
105 def get_version():
106     """
107     Get version
108     """
109     # Use the build tag to retrieve the version
110     # By default version is unknown
111     # if launched through CI the build tag has the following format
112     # jenkins-<project>-<installer>-<pod>-<job>-<branch>-<id>
113     # e.g. jenkins-functest-fuel-opnfv-jump-2-daily-master-190
114     # use regex to match branch info
115     rule = "daily-(.+?)-[0-9]*"
116     build_tag = get_build_tag()
117     m = re.search(rule, build_tag)
118     if m:
119         return m.group(1)
120     else:
121         return "unknown"
122
123
124 def get_pod_name():
125     """
126     Get PoD Name from env variable NODE_NAME
127     """
128     try:
129         return os.environ['NODE_NAME']
130     except KeyError:
131         logger.info(
132             "Unable to retrieve the POD name from environment. " +
133             "Using pod name 'unknown-pod'")
134         return "unknown-pod"
135
136
137 def get_build_tag():
138     """
139     Get build tag of jenkins jobs
140     """
141     try:
142         build_tag = os.environ['BUILD_TAG']
143     except KeyError:
144         logger.info("Impossible to retrieve the build tag")
145         build_tag = "none"
146
147     return build_tag
148
149
150 def get_db_url():
151     """
152     Returns DB URL
153     """
154     return get_functest_config('results.test_db_url')
155
156
157 def logger_test_results(project, case_name, status, details):
158     pod_name = get_pod_name()
159     scenario = get_scenario()
160     version = get_version()
161     build_tag = get_build_tag()
162
163     logger.info(
164         "\n"
165         "****************************************\n"
166         "\t %(p)s/%(n)s results \n\n"
167         "****************************************\n"
168         "DB:\t%(db)s\n"
169         "pod:\t%(pod)s\n"
170         "version:\t%(v)s\n"
171         "scenario:\t%(s)s\n"
172         "status:\t%(c)s\n"
173         "build tag:\t%(b)s\n"
174         "details:\t%(d)s\n"
175         % {'p': project,
176             'n': case_name,
177             'db': get_db_url(),
178             'pod': pod_name,
179             'v': version,
180             's': scenario,
181             'c': status,
182             'b': build_tag,
183             'd': details})
184
185
186 @decorators.can_dump_request_to_file
187 def push_results_to_db(project, case_name,
188                        start_date, stop_date, criteria, details):
189     """
190     POST results to the Result target DB
191     """
192     # Retrieve params from CI and conf
193     url = get_db_url() + "/results"
194
195     try:
196         installer = os.environ['INSTALLER_TYPE']
197         scenario = os.environ['DEPLOY_SCENARIO']
198         pod_name = os.environ['NODE_NAME']
199         build_tag = os.environ['BUILD_TAG']
200     except KeyError as e:
201         logger.error("Please set env var: " + str(e))
202         return False
203     rule = "daily-(.+?)-[0-9]*"
204     m = re.search(rule, build_tag)
205     if m:
206         version = m.group(1)
207     else:
208         logger.error("Please fix BUILD_TAG env var: " + build_tag)
209         return False
210     test_start = dt.fromtimestamp(start_date).strftime('%Y-%m-%d %H:%M:%S')
211     test_stop = dt.fromtimestamp(stop_date).strftime('%Y-%m-%d %H:%M:%S')
212
213     params = {"project_name": project, "case_name": case_name,
214               "pod_name": pod_name, "installer": installer,
215               "version": version, "scenario": scenario, "criteria": criteria,
216               "build_tag": build_tag, "start_date": test_start,
217               "stop_date": test_stop, "details": details}
218
219     error = None
220     headers = {'Content-Type': 'application/json'}
221     try:
222         r = requests.post(url, data=json.dumps(params), headers=headers)
223         logger.debug(r)
224         r.raise_for_status()
225     except requests.RequestException as exc:
226         if 'r' in locals():
227             error = ("Pushing Result to DB(%s) failed: %s" %
228                      (r.url, r.content))
229         else:
230             error = ("Pushing Result to DB(%s) failed: %s" % (url, exc))
231     except Exception as e:
232         error = ("Error [push_results_to_db("
233                  "DB: '%(db)s', "
234                  "project: '%(project)s', "
235                  "case: '%(case)s', "
236                  "pod: '%(pod)s', "
237                  "version: '%(v)s', "
238                  "scenario: '%(s)s', "
239                  "criteria: '%(c)s', "
240                  "build_tag: '%(t)s', "
241                  "details: '%(d)s')]: "
242                  "%(error)s" %
243                  {
244                      'db': url,
245                      'project': project,
246                      'case': case_name,
247                      'pod': pod_name,
248                      'v': version,
249                      's': scenario,
250                      'c': criteria,
251                      't': build_tag,
252                      'd': details,
253                      'error': e
254                  })
255     finally:
256         if error:
257             logger.error(error)
258             return False
259         return True
260
261
262 def get_resolvconf_ns():
263     """
264     Get nameservers from current resolv.conf
265     """
266     nameservers = []
267     rconf = open("/etc/resolv.conf", "r")
268     line = rconf.readline()
269     resolver = dns.resolver.Resolver()
270     while line:
271         ip = re.search(r"\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b", line)
272         if ip:
273             resolver.nameservers = [ip.group(0)]
274             try:
275                 result = resolver.query('opnfv.org')[0]
276                 if result != "":
277                     nameservers.append(ip.group())
278             except dns.exception.Timeout:
279                 pass
280         line = rconf.readline()
281     return nameservers
282
283
284 def get_ci_envvars():
285     """
286     Get the CI env variables
287     """
288     ci_env_var = {
289         "installer": os.environ.get('INSTALLER_TYPE'),
290         "scenario": os.environ.get('DEPLOY_SCENARIO')}
291     return ci_env_var
292
293
294 def execute_command(cmd, info=False, error_msg="",
295                     verbose=True, output_file=None):
296     if not error_msg:
297         error_msg = ("The command '%s' failed." % cmd)
298     msg_exec = ("Executing command: '%s'" % cmd)
299     if verbose:
300         if info:
301             logger.info(msg_exec)
302         else:
303             logger.debug(msg_exec)
304     p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
305                          stderr=subprocess.STDOUT)
306     if output_file:
307         f = open(output_file, "w")
308     for line in iter(p.stdout.readline, b''):
309         if output_file:
310             f.write(line)
311         else:
312             line = line.replace('\n', '')
313             print line
314             sys.stdout.flush()
315     if output_file:
316         f.close()
317     p.stdout.close()
318     returncode = p.wait()
319     if returncode != 0:
320         if verbose:
321             logger.error(error_msg)
322
323     return returncode
324
325
326 def get_dict_by_test(testname):
327     with open(get_testcases_file_dir()) as f:
328         testcases_yaml = yaml.safe_load(f)
329
330     for dic_tier in testcases_yaml.get("tiers"):
331         for dic_testcase in dic_tier['testcases']:
332             if dic_testcase['name'] == testname:
333                 return dic_testcase
334
335     logger.error('Project %s is not defined in testcases.yaml' % testname)
336     return None
337
338
339 def get_criteria_by_test(testname):
340     dict = get_dict_by_test(testname)
341     if dict:
342         return dict['criteria']
343     return None
344
345
346 # ----------------------------------------------------------
347 #
348 #               YAML UTILS
349 #
350 # -----------------------------------------------------------
351 def get_parameter_from_yaml(parameter, file):
352     """
353     Returns the value of a given parameter in file.yaml
354     parameter must be given in string format with dots
355     Example: general.openstack.image_name
356     """
357     with open(file) as f:
358         file_yaml = yaml.safe_load(f)
359     f.close()
360     value = file_yaml
361     for element in parameter.split("."):
362         value = value.get(element)
363         if value is None:
364             raise ValueError("The parameter %s is not defined in"
365                              " config_functest.yaml" % parameter)
366     return value
367
368
369 def get_functest_config(parameter):
370     yaml_ = os.environ["CONFIG_FUNCTEST_YAML"]
371     return get_parameter_from_yaml(parameter, yaml_)
372
373
374 def check_success_rate(case_name, success_rate):
375     success_rate = float(success_rate)
376     criteria = get_criteria_by_test(case_name)
377
378     def get_criteria_value(op):
379         return float(criteria.split(op)[1].rstrip('%'))
380
381     status = 'FAIL'
382     ops = ['==', '>=']
383     for op in ops:
384         if op in criteria:
385             c_value = get_criteria_value(op)
386             if eval("%s %s %s" % (success_rate, op, c_value)):
387                 status = 'PASS'
388             break
389
390     return status
391
392
393 def merge_dicts(dict1, dict2):
394     for k in set(dict1.keys()).union(dict2.keys()):
395         if k in dict1 and k in dict2:
396             if isinstance(dict1[k], dict) and isinstance(dict2[k], dict):
397                 yield (k, dict(merge_dicts(dict1[k], dict2[k])))
398             else:
399                 yield (k, dict2[k])
400         elif k in dict1:
401             yield (k, dict1[k])
402         else:
403             yield (k, dict2[k])
404
405
406 def get_testcases_file_dir():
407     return get_functest_config('general.functest.testcases_yaml')
408
409
410 def get_functest_yaml():
411     with open(os.environ["CONFIG_FUNCTEST_YAML"]) as f:
412         functest_yaml = yaml.safe_load(f)
413     f.close()
414     return functest_yaml
415
416
417 def print_separator():
418     logger.info("==============================================")
419
420
421 def timethis(func):
422     """Measure the time it takes for a function to complete"""
423     @functools.wraps(func)
424     def timed(*args, **kwargs):
425         ts = time.time()
426         result = func(*args, **kwargs)
427         te = time.time()
428         elapsed = '{0}'.format(te - ts)
429         logger.info('{f}(*{a}, **{kw}) took: {t} sec'.format(
430             f=func.__name__, a=args, kw=kwargs, t=elapsed))
431         return result, elapsed
432     return timed