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