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