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