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