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