Merge "Adjust timeouts in Vnfs"
[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 logging
13 import os
14 import pkg_resources
15 import re
16 import shutil
17 import subprocess
18 import sys
19 import time
20 from datetime import datetime as dt
21
22 import dns.resolver
23 import requests
24 from six.moves import urllib
25 import yaml
26
27 from functest.utils import constants
28 from functest.utils import decorators
29 from functest.utils.constants import CONST
30
31 logger = logging.getLogger(__name__)
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         urllib.request.urlopen(url, timeout=5)
45         return True
46     except urllib.error.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 = urllib.request.urlopen(url)
58     except (urllib.error.HTTPError, urllib.error.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_version():
72     """
73     Get version
74     """
75     # Use the build tag to retrieve the version
76     # By default version is unknown
77     # if launched through CI the build tag has the following format
78     # jenkins-<project>-<installer>-<pod>-<job>-<branch>-<id>
79     # e.g. jenkins-functest-fuel-opnfv-jump-2-daily-master-190
80     # jenkins-functest-fuel-baremetal-weekly-master-8
81     # use regex to match branch info
82     rule = "(dai|week)ly-(.+?)-[0-9]*"
83     build_tag = CONST.__getattribute__('BUILD_TAG')
84     if not build_tag:
85         build_tag = 'none'
86     m = re.search(rule, build_tag)
87     if m:
88         return m.group(2)
89     else:
90         return "unknown"
91
92
93 def logger_test_results(project, case_name, status, details):
94     """
95     Format test case results for the logger
96     """
97     pod_name = CONST.__getattribute__('NODE_NAME')
98     scenario = CONST.__getattribute__('DEPLOY_SCENARIO')
99     version = get_version()
100     build_tag = CONST.__getattribute__('BUILD_TAG')
101     db_url = CONST.__getattribute__("results_test_db_url")
102
103     logger.info(
104         "\n"
105         "****************************************\n"
106         "\t %(p)s/%(n)s results \n\n"
107         "****************************************\n"
108         "DB:\t%(db)s\n"
109         "pod:\t%(pod)s\n"
110         "version:\t%(v)s\n"
111         "scenario:\t%(s)s\n"
112         "status:\t%(c)s\n"
113         "build tag:\t%(b)s\n"
114         "details:\t%(d)s\n"
115         % {'p': project,
116             'n': case_name,
117             'db': db_url,
118             'pod': pod_name,
119             'v': version,
120             's': scenario,
121             'c': status,
122             'b': build_tag,
123             'd': details})
124
125
126 @decorators.can_dump_request_to_file
127 def push_results_to_db(project, case_name,
128                        start_date, stop_date, result, details):
129     """
130     POST results to the Result target DB
131     """
132     # Retrieve params from CI and conf
133     if (hasattr(CONST, 'TEST_DB_URL')):
134         url = CONST.__getattribute__('TEST_DB_URL')
135     else:
136         url = CONST.__getattribute__("results_test_db_url")
137
138     try:
139         installer = os.environ['INSTALLER_TYPE']
140         scenario = os.environ['DEPLOY_SCENARIO']
141         pod_name = os.environ['NODE_NAME']
142         build_tag = os.environ['BUILD_TAG']
143     except KeyError as e:
144         logger.error("Please set env var: " + str(e))
145         return False
146     version = get_version()
147     test_start = dt.fromtimestamp(start_date).strftime('%Y-%m-%d %H:%M:%S')
148     test_stop = dt.fromtimestamp(stop_date).strftime('%Y-%m-%d %H:%M:%S')
149
150     params = {"project_name": project, "case_name": case_name,
151               "pod_name": pod_name, "installer": installer,
152               "version": version, "scenario": scenario, "criteria": result,
153               "build_tag": build_tag, "start_date": test_start,
154               "stop_date": test_stop, "details": details}
155
156     error = None
157     headers = {'Content-Type': 'application/json'}
158     try:
159         r = requests.post(url, data=json.dumps(params, sort_keys=True),
160                           headers=headers)
161         logger.debug(r)
162         r.raise_for_status()
163     except requests.RequestException as exc:
164         if 'r' in locals():
165             error = ("Pushing Result to DB(%s) failed: %s" %
166                      (r.url, r.content))
167         else:
168             error = ("Pushing Result to DB(%s) failed: %s" % (url, exc))
169     except Exception as e:
170         error = ("Error [push_results_to_db("
171                  "DB: '%(db)s', "
172                  "project: '%(project)s', "
173                  "case: '%(case)s', "
174                  "pod: '%(pod)s', "
175                  "version: '%(v)s', "
176                  "scenario: '%(s)s', "
177                  "criteria: '%(c)s', "
178                  "build_tag: '%(t)s', "
179                  "details: '%(d)s')]: "
180                  "%(error)s" %
181                  {
182                      'db': url,
183                      'project': project,
184                      'case': case_name,
185                      'pod': pod_name,
186                      'v': version,
187                      's': scenario,
188                      'c': result,
189                      't': build_tag,
190                      'd': details,
191                      'error': e
192                  })
193     finally:
194         if error:
195             logger.error(error)
196             return False
197         return True
198
199
200 def get_resolvconf_ns():
201     """
202     Get nameservers from current resolv.conf
203     """
204     nameservers = []
205     rconf = open("/etc/resolv.conf", "r")
206     line = rconf.readline()
207     resolver = dns.resolver.Resolver()
208     while line:
209         ip = re.search(r"\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b", line)
210         if ip:
211             resolver.nameservers = [ip.group(0)]
212             try:
213                 result = resolver.query('opnfv.org')[0]
214                 if result != "":
215                     nameservers.append(ip.group())
216             except dns.exception.Timeout:
217                 pass
218         line = rconf.readline()
219     return nameservers
220
221
222 def get_ci_envvars():
223     """
224     Get the CI env variables
225     """
226     ci_env_var = {
227         "installer": os.environ.get('INSTALLER_TYPE'),
228         "scenario": os.environ.get('DEPLOY_SCENARIO')}
229     return ci_env_var
230
231
232 def execute_command_raise(cmd, info=False, error_msg="",
233                           verbose=True, output_file=None):
234     ret = execute_command(cmd, info, error_msg, verbose, output_file)
235     if ret != 0:
236         raise Exception(error_msg)
237
238
239 def execute_command(cmd, info=False, error_msg="",
240                     verbose=True, output_file=None):
241     if not error_msg:
242         error_msg = ("The command '%s' failed." % cmd)
243     msg_exec = ("Executing command: '%s'" % cmd)
244     if verbose:
245         if info:
246             logger.info(msg_exec)
247         else:
248             logger.debug(msg_exec)
249     p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
250                          stderr=subprocess.STDOUT)
251     if output_file:
252         f = open(output_file, "w")
253     for line in iter(p.stdout.readline, b''):
254         if output_file:
255             f.write(line)
256         else:
257             line = line.replace('\n', '')
258             print(line)
259             sys.stdout.flush()
260     if output_file:
261         f.close()
262     p.stdout.close()
263     returncode = p.wait()
264     if returncode != 0:
265         if verbose:
266             logger.error(error_msg)
267
268     return returncode
269
270
271 def get_dict_by_test(testname):
272     with open(pkg_resources.resource_filename(
273             'functest', 'ci/testcases.yaml')) as f:
274         testcases_yaml = yaml.safe_load(f)
275
276     for dic_tier in testcases_yaml.get("tiers"):
277         for dic_testcase in dic_tier['testcases']:
278             if dic_testcase['case_name'] == testname:
279                 return dic_testcase
280
281     logger.error('Project %s is not defined in testcases.yaml' % testname)
282     return None
283
284
285 def get_criteria_by_test(testname):
286     dict = get_dict_by_test(testname)
287     if dict:
288         return dict['criteria']
289     return None
290
291
292 # ----------------------------------------------------------
293 #
294 #               YAML UTILS
295 #
296 # -----------------------------------------------------------
297 def get_parameter_from_yaml(parameter, file):
298     """
299     Returns the value of a given parameter in file.yaml
300     parameter must be given in string format with dots
301     Example: general.openstack.image_name
302     """
303     with open(file) as f:
304         file_yaml = yaml.safe_load(f)
305     f.close()
306     value = file_yaml
307     for element in parameter.split("."):
308         value = value.get(element)
309         if value is None:
310             raise ValueError("The parameter %s is not defined in"
311                              " %s" % (parameter, file))
312     return value
313
314
315 def get_functest_config(parameter):
316     yaml_ = constants.CONST.__getattribute__('CONFIG_FUNCTEST_YAML')
317     return get_parameter_from_yaml(parameter, yaml_)
318
319
320 def merge_dicts(dict1, dict2):
321     for k in set(dict1.keys()).union(dict2.keys()):
322         if k in dict1 and k in dict2:
323             if isinstance(dict1[k], dict) and isinstance(dict2[k], dict):
324                 yield (k, dict(merge_dicts(dict1[k], dict2[k])))
325             else:
326                 yield (k, dict2[k])
327         elif k in dict1:
328             yield (k, dict1[k])
329         else:
330             yield (k, dict2[k])
331
332
333 def get_functest_yaml():
334     with open(constants.CONST.__getattribute__('CONFIG_FUNCTEST_YAML')) as f:
335         functest_yaml = yaml.safe_load(f)
336     f.close()
337     return functest_yaml
338
339
340 def print_separator():
341     logger.info("==============================================")
342
343
344 def timethis(func):
345     """Measure the time it takes for a function to complete"""
346     @functools.wraps(func)
347     def timed(*args, **kwargs):
348         ts = time.time()
349         result = func(*args, **kwargs)
350         te = time.time()
351         elapsed = '{0}'.format(te - ts)
352         logger.info('{f}(*{a}, **{kw}) took: {t} sec'.format(
353             f=func.__name__, a=args, kw=kwargs, t=elapsed))
354         return result, elapsed
355     return timed