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