Remove all logers as utils method args.
[functest.git] / 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 from datetime import datetime as dt
11 import json
12 import os
13 import re
14 import requests
15 import shutil
16 import subprocess
17 import sys
18 import urllib2
19
20 import functest.ci.tier_builder as tb
21 import functest.utils.functest_logger as ft_logger
22
23 import dns.resolver
24 from git import Repo
25 import yaml
26
27
28 logger = ft_logger.Logger("functest_utils").getLogger()
29
30 REPOS_DIR = os.getenv('repos_dir')
31 FUNCTEST_REPO = ("%s/functest" % REPOS_DIR)
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         urllib2.urlopen(url, timeout=5)
45         return True
46     except urllib2.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 = urllib2.urlopen(url)
58     except (urllib2.HTTPError, urllib2.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_git_branch(repo_path):
72     """
73     Get git branch name
74     """
75     repo = Repo(repo_path)
76     branch = repo.active_branch
77     return branch.name
78
79
80 def get_installer_type():
81     """
82     Get installer type (fuel, apex, joid, compass)
83     """
84     try:
85         installer = os.environ['INSTALLER_TYPE']
86     except KeyError:
87         logger.error("Impossible to retrieve the installer type")
88         installer = "Unknown_installer"
89
90     return installer
91
92
93 def get_scenario():
94     """
95     Get scenario
96     """
97     try:
98         scenario = os.environ['DEPLOY_SCENARIO']
99     except KeyError:
100         logger.error("Impossible to retrieve the scenario")
101         scenario = "Unknown_scenario"
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     # use regex to match branch info
116     rule = "daily-(.+?)-[0-9]*"
117     build_tag = get_build_tag()
118     m = re.search(rule, build_tag)
119     if m:
120         return m.group(1)
121     else:
122         return "unknown"
123
124
125 def get_pod_name():
126     """
127     Get PoD Name from env variable NODE_NAME
128     """
129     try:
130         return os.environ['NODE_NAME']
131     except KeyError:
132         logger.error(
133             "Unable to retrieve the POD name from environment. " +
134             "Using pod name 'unknown-pod'")
135         return "unknown-pod"
136
137
138 def get_build_tag():
139     """
140     Get build tag of jenkins jobs
141     """
142     try:
143         build_tag = os.environ['BUILD_TAG']
144     except KeyError:
145         logger.error("Impossible to retrieve the build tag")
146         build_tag = "unknown_build_tag"
147
148     return build_tag
149
150
151 def get_db_url():
152     """
153     Returns DB URL
154     """
155     functest_yaml = get_functest_yaml()
156     db_url = functest_yaml.get("results").get("test_db_url")
157     return db_url
158
159
160 def logger_test_results(project, case_name, status, details):
161     pod_name = get_pod_name()
162     scenario = get_scenario()
163     version = get_version()
164     build_tag = get_build_tag()
165
166     logger.info(
167         "\n"
168         "****************************************\n"
169         "\t %(p)s/%(n)s results \n\n"
170         "****************************************\n"
171         "DB:\t%(db)s\n"
172         "pod:\t%(pod)s\n"
173         "version:\t%(v)s\n"
174         "scenario:\t%(s)s\n"
175         "status:\t%(c)s\n"
176         "build tag:\t%(b)s\n"
177         "details:\t%(d)s\n"
178         % {'p': project,
179             'n': case_name,
180             'db': get_db_url(),
181             'pod': pod_name,
182             'v': version,
183             's': scenario,
184             'c': status,
185             'b': build_tag,
186             'd': details})
187
188
189 def push_results_to_db(project, case_name,
190                        start_date, stop_date, criteria, details):
191     """
192     POST results to the Result target DB
193     """
194     # Retrieve params from CI and conf
195     url = get_db_url() + "/results"
196
197     try:
198         installer = os.environ['INSTALLER_TYPE']
199         scenario = os.environ['DEPLOY_SCENARIO']
200         pod_name = os.environ['NODE_NAME']
201         build_tag = os.environ['BUILD_TAG']
202     except KeyError as e:
203         logger.error("Please set env var: " + str(e))
204         return False
205     rule = "daily-(.+?)-[0-9]*"
206     m = re.search(rule, build_tag)
207     if m:
208         version = m.group(1)
209     else:
210         logger.error("Please fix BUILD_TAG env var: " + build_tag)
211         return False
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": criteria,
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), headers=headers)
225         logger.debug(r)
226         r.raise_for_status()
227     except requests.RequestException as exc:
228         if 'r' in locals():
229             error = ("Pushing Result to DB(%s) failed: %s" %
230                      (r.url, r.content))
231         else:
232             error = ("Pushing Result to DB(%s) failed: %s" % (url, exc))
233     except Exception as e:
234         error = ("Error [push_results_to_db("
235                  "DB: '%(db)s', "
236                  "project: '%(project)s', "
237                  "case: '%(case)s', "
238                  "pod: '%(pod)s', "
239                  "version: '%(v)s', "
240                  "scenario: '%(s)s', "
241                  "criteria: '%(c)s', "
242                  "build_tag: '%(t)s', "
243                  "details: '%(d)s')]: "
244                  "%(error)s" %
245                  {
246                      'db': url,
247                      'project': project,
248                      'case': case_name,
249                      'pod': pod_name,
250                      'v': version,
251                      's': scenario,
252                      'c': criteria,
253                      't': build_tag,
254                      'd': details,
255                      'error': e
256                  })
257     finally:
258         if error:
259             logger.error(error)
260             return False
261         return True
262
263
264 def get_resolvconf_ns():
265     """
266     Get nameservers from current resolv.conf
267     """
268     nameservers = []
269     rconf = open("/etc/resolv.conf", "r")
270     line = rconf.readline()
271     resolver = dns.resolver.Resolver()
272     while line:
273         ip = re.search(r"\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b", line)
274         if ip:
275             resolver.nameservers = [str(ip)]
276             try:
277                 result = resolver.query('opnfv.org')[0]
278                 if result != "":
279                     nameservers.append(ip.group())
280             except dns.exception.Timeout:
281                 pass
282         line = rconf.readline()
283     return nameservers
284
285
286 def get_ci_envvars():
287     """
288     Get the CI env variables
289     """
290     ci_env_var = {
291         "installer": os.environ.get('INSTALLER_TYPE'),
292         "scenario": os.environ.get('DEPLOY_SCENARIO')}
293     return ci_env_var
294
295
296 def execute_command(cmd, exit_on_error=True, info=False, error_msg="",
297                     verbose=True):
298     if not error_msg:
299         error_msg = ("The command '%s' failed." % cmd)
300     msg_exec = ("Executing command: '%s'" % cmd)
301     if verbose:
302         if info:
303             logger.info(msg_exec)
304         else:
305             logger.debug(msg_exec)
306     p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
307                          stderr=subprocess.STDOUT)
308     for line in iter(p.stdout.readline, b''):
309         line = line.replace('\n', '')
310         print line
311         sys.stdout.flush()
312     p.stdout.close()
313     returncode = p.wait()
314     if returncode != 0:
315         if verbose:
316             logger.error(error_msg)
317         if exit_on_error:
318             sys.exit(1)
319
320     return returncode
321
322
323 def get_deployment_dir():
324     """
325     Returns current Rally deployment directory
326     """
327     functest_yaml = get_functest_yaml()
328     deployment_name = functest_yaml.get("rally").get("deployment_name")
329     rally_dir = functest_yaml.get("general").get("directories").get(
330         "dir_rally_inst")
331     cmd = ("rally deployment list | awk '/" + deployment_name +
332            "/ {print $2}'")
333     p = subprocess.Popen(cmd, shell=True,
334                          stdout=subprocess.PIPE,
335                          stderr=subprocess.STDOUT)
336     deployment_uuid = p.stdout.readline().rstrip()
337     if deployment_uuid == "":
338         logger.error("Rally deployment not found.")
339         exit(-1)
340     deployment_dir = (rally_dir + "/tempest/for-deployment-" +
341                       deployment_uuid)
342     return deployment_dir
343
344
345 def get_criteria_by_test(testname):
346     criteria = ""
347     file = get_testcases_file()
348     tiers = tb.TierBuilder("", "", file)
349     for tier in tiers.get_tiers():
350         for test in tier.get_tests():
351             if test.get_name() == testname:
352                 criteria = test.get_criteria()
353
354     return criteria
355
356
357 # ----------------------------------------------------------
358 #
359 #               YAML UTILS
360 #
361 # -----------------------------------------------------------
362 def get_parameter_from_yaml(parameter, file=None):
363     """
364     Returns the value of a given parameter in config_functest.yaml
365     parameter must be given in string format with dots
366     Example: general.openstack.image_name
367     """
368     if file is None:
369         file = os.environ["CONFIG_FUNCTEST_YAML"]
370     with open(file) as f:
371         functest_yaml = yaml.safe_load(f)
372     f.close()
373     value = functest_yaml
374     for element in parameter.split("."):
375         value = value.get(element)
376         if value is None:
377             raise ValueError("The parameter %s is not defined in"
378                              " config_functest.yaml" % parameter)
379     return value
380
381
382 def check_success_rate(case_name, success_rate):
383     success_rate = float(success_rate)
384     criteria = get_criteria_by_test(case_name)
385
386     def get_criteria_value(op):
387         return float(criteria.split(op)[1].rstrip('%'))
388
389     status = 'FAIL'
390     ops = ['==', '>=']
391     for op in ops:
392         if op in criteria:
393             c_value = get_criteria_value(op)
394             if eval("%s %s %s" % (success_rate, op, c_value)):
395                 status = 'PASS'
396             break
397
398     return status
399
400
401 def merge_dicts(dict1, dict2):
402     for k in set(dict1.keys()).union(dict2.keys()):
403         if k in dict1 and k in dict2:
404             if isinstance(dict1[k], dict) and isinstance(dict2[k], dict):
405                 yield (k, dict(merge_dicts(dict1[k], dict2[k])))
406             else:
407                 yield (k, dict2[k])
408         elif k in dict1:
409             yield (k, dict1[k])
410         else:
411             yield (k, dict2[k])
412
413
414 def check_test_result(test_name, ret, start_time, stop_time):
415     def get_criteria_value():
416         return get_criteria_by_test(test_name).split('==')[1].strip()
417
418     status = 'FAIL'
419     if str(ret) == get_criteria_value():
420         status = 'PASS'
421
422     details = {
423         'timestart': start_time,
424         'duration': round(stop_time - start_time, 1),
425         'status': status,
426     }
427
428     return status, details
429
430
431 def get_testcases_file():
432     return FUNCTEST_REPO + "/ci/testcases.yaml"
433
434
435 def get_functest_yaml():
436     with open(os.environ["CONFIG_FUNCTEST_YAML"]) as f:
437         functest_yaml = yaml.safe_load(f)
438     f.close()
439     return functest_yaml