Remove redundant tempest cleanup utility
[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     url = CONST.__getattribute__("results_test_db_url")
134
135     try:
136         installer = os.environ['INSTALLER_TYPE']
137         scenario = os.environ['DEPLOY_SCENARIO']
138         pod_name = os.environ['NODE_NAME']
139         build_tag = os.environ['BUILD_TAG']
140     except KeyError as e:
141         logger.error("Please set env var: " + str(e))
142         return False
143     version = get_version()
144     test_start = dt.fromtimestamp(start_date).strftime('%Y-%m-%d %H:%M:%S')
145     test_stop = dt.fromtimestamp(stop_date).strftime('%Y-%m-%d %H:%M:%S')
146
147     params = {"project_name": project, "case_name": case_name,
148               "pod_name": pod_name, "installer": installer,
149               "version": version, "scenario": scenario, "criteria": result,
150               "build_tag": build_tag, "start_date": test_start,
151               "stop_date": test_stop, "details": details}
152
153     error = None
154     headers = {'Content-Type': 'application/json'}
155     try:
156         r = requests.post(url, data=json.dumps(params, sort_keys=True),
157                           headers=headers)
158         logger.debug(r)
159         r.raise_for_status()
160     except requests.RequestException as exc:
161         if 'r' in locals():
162             error = ("Pushing Result to DB(%s) failed: %s" %
163                      (r.url, r.content))
164         else:
165             error = ("Pushing Result to DB(%s) failed: %s" % (url, exc))
166     except Exception as e:
167         error = ("Error [push_results_to_db("
168                  "DB: '%(db)s', "
169                  "project: '%(project)s', "
170                  "case: '%(case)s', "
171                  "pod: '%(pod)s', "
172                  "version: '%(v)s', "
173                  "scenario: '%(s)s', "
174                  "criteria: '%(c)s', "
175                  "build_tag: '%(t)s', "
176                  "details: '%(d)s')]: "
177                  "%(error)s" %
178                  {
179                      'db': url,
180                      'project': project,
181                      'case': case_name,
182                      'pod': pod_name,
183                      'v': version,
184                      's': scenario,
185                      'c': result,
186                      't': build_tag,
187                      'd': details,
188                      'error': e
189                  })
190     finally:
191         if error:
192             logger.error(error)
193             return False
194         return True
195
196
197 def get_resolvconf_ns():
198     """
199     Get nameservers from current resolv.conf
200     """
201     nameservers = []
202     rconf = open("/etc/resolv.conf", "r")
203     line = rconf.readline()
204     resolver = dns.resolver.Resolver()
205     while line:
206         ip = re.search(r"\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b", line)
207         if ip:
208             resolver.nameservers = [ip.group(0)]
209             try:
210                 result = resolver.query('opnfv.org')[0]
211                 if result != "":
212                     nameservers.append(ip.group())
213             except dns.exception.Timeout:
214                 pass
215         line = rconf.readline()
216     return nameservers
217
218
219 def get_ci_envvars():
220     """
221     Get the CI env variables
222     """
223     ci_env_var = {
224         "installer": os.environ.get('INSTALLER_TYPE'),
225         "scenario": os.environ.get('DEPLOY_SCENARIO')}
226     return ci_env_var
227
228
229 def execute_command_raise(cmd, info=False, error_msg="",
230                           verbose=True, output_file=None):
231     ret = execute_command(cmd, info, error_msg, verbose, output_file)
232     if ret != 0:
233         raise Exception(error_msg)
234
235
236 def execute_command(cmd, info=False, error_msg="",
237                     verbose=True, output_file=None):
238     if not error_msg:
239         error_msg = ("The command '%s' failed." % cmd)
240     msg_exec = ("Executing command: '%s'" % cmd)
241     if verbose:
242         if info:
243             logger.info(msg_exec)
244         else:
245             logger.debug(msg_exec)
246     p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
247                          stderr=subprocess.STDOUT)
248     if output_file:
249         f = open(output_file, "w")
250     for line in iter(p.stdout.readline, b''):
251         if output_file:
252             f.write(line)
253         else:
254             line = line.replace('\n', '')
255             print(line)
256             sys.stdout.flush()
257     if output_file:
258         f.close()
259     p.stdout.close()
260     returncode = p.wait()
261     if returncode != 0:
262         if verbose:
263             logger.error(error_msg)
264
265     return returncode
266
267
268 def get_dict_by_test(testname):
269     with open(pkg_resources.resource_filename(
270             'functest', 'ci/testcases.yaml')) as f:
271         testcases_yaml = yaml.safe_load(f)
272
273     for dic_tier in testcases_yaml.get("tiers"):
274         for dic_testcase in dic_tier['testcases']:
275             if dic_testcase['case_name'] == testname:
276                 return dic_testcase
277
278     logger.error('Project %s is not defined in testcases.yaml' % testname)
279     return None
280
281
282 def get_criteria_by_test(testname):
283     dict = get_dict_by_test(testname)
284     if dict:
285         return dict['criteria']
286     return None
287
288
289 # ----------------------------------------------------------
290 #
291 #               YAML UTILS
292 #
293 # -----------------------------------------------------------
294 def get_parameter_from_yaml(parameter, file):
295     """
296     Returns the value of a given parameter in file.yaml
297     parameter must be given in string format with dots
298     Example: general.openstack.image_name
299     """
300     with open(file) as f:
301         file_yaml = yaml.safe_load(f)
302     f.close()
303     value = file_yaml
304     for element in parameter.split("."):
305         value = value.get(element)
306         if value is None:
307             raise ValueError("The parameter %s is not defined in"
308                              " %s" % (parameter, file))
309     return value
310
311
312 def get_functest_config(parameter):
313     yaml_ = constants.CONST.__getattribute__('CONFIG_FUNCTEST_YAML')
314     return get_parameter_from_yaml(parameter, yaml_)
315
316
317 def merge_dicts(dict1, dict2):
318     for k in set(dict1.keys()).union(dict2.keys()):
319         if k in dict1 and k in dict2:
320             if isinstance(dict1[k], dict) and isinstance(dict2[k], dict):
321                 yield (k, dict(merge_dicts(dict1[k], dict2[k])))
322             else:
323                 yield (k, dict2[k])
324         elif k in dict1:
325             yield (k, dict1[k])
326         else:
327             yield (k, dict2[k])
328
329
330 def get_functest_yaml():
331     with open(constants.CONST.__getattribute__('CONFIG_FUNCTEST_YAML')) as f:
332         functest_yaml = yaml.safe_load(f)
333     f.close()
334     return functest_yaml
335
336
337 def print_separator():
338     logger.info("==============================================")
339
340
341 def timethis(func):
342     """Measure the time it takes for a function to complete"""
343     @functools.wraps(func)
344     def timed(*args, **kwargs):
345         ts = time.time()
346         result = func(*args, **kwargs)
347         te = time.time()
348         elapsed = '{0}'.format(te - ts)
349         logger.info('{f}(*{a}, **{kw}) took: {t} sec'.format(
350             f=func.__name__, a=args, kw=kwargs, t=elapsed))
351         return result, elapsed
352     return timed