refactor run-rally and integrate API to push results into DB
[functest.git] / testcases / VIM / OpenStack / CI / libraries / run_rally.py
1 #!/usr/bin/env python
2 #
3 # Copyright (c) 2015 Orange
4 # guyrodrigue.koffi@orange.com
5 # morgan.richomme@orange.com
6 # All rights reserved. This program and the accompanying materials
7 # are made available under the terms of the Apache License, Version 2.0
8 # which accompanies this distribution, and is available at
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # 0.1 (05/2015) initial commit
12 # 0.2 (28/09/2015) extract Tempest, format json result, add ceilometer suite
13 # 0.3 (19/10/2015) remove Tempest from run_rally
14 # and push result into test DB
15 #
16
17 import re
18 import json
19 import os
20 import argparse
21 import logging
22 import yaml
23 import requests
24 import sys
25
26 """ tests configuration """
27 tests = ['authenticate', 'glance', 'cinder', 'ceilometer', 'heat', 'keystone',
28          'neutron', 'nova', 'quotas', 'requests', 'vm', 'all']
29 parser = argparse.ArgumentParser()
30 parser.add_argument("repo_path", help="Path to the repository")
31 parser.add_argument("test_name",
32                     help="Module name to be tested"
33                          "Possible values are : "
34                          "[ {d[0]} | {d[1]} | {d[2]} | {d[3]} | {d[4]} | "
35                          "{d[5]} | {d[6]} | {d[7]} | {d[8]} | {d[9]} | "
36                          "{d[10]} | {d[11]}]. The 'all' value "
37                          "performs all the  possible tests scenarios"
38                          .format(d=tests))
39
40 parser.add_argument("-d", "--debug", help="Debug mode",  action="store_true")
41 parser.add_argument("-r", "--report",
42                     help="Create json result file",
43                     action="store_true")
44
45 args = parser.parse_args()
46
47 sys.path.append(args.repo_path + "testcases/")
48 import functest_utils
49
50 """ logging configuration """
51 logger = logging.getLogger("run_rally")
52 logger.setLevel(logging.DEBUG)
53
54 ch = logging.StreamHandler()
55 if args.debug:
56     ch.setLevel(logging.DEBUG)
57 else:
58     ch.setLevel(logging.INFO)
59
60 formatter = logging.Formatter("%(asctime)s - %(name)s - "
61                               "%(levelname)s - %(message)s")
62 ch.setFormatter(formatter)
63 logger.addHandler(ch)
64
65 with open(args.repo_path+"testcases/config_functest.yaml") as f:
66     functest_yaml = yaml.safe_load(f)
67 f.close()
68
69 HOME = os.environ['HOME']+"/"
70 REPO_PATH = args.repo_path
71 SCENARIOS_DIR = REPO_PATH + functest_yaml.get("general"). \
72     get("directories").get("dir_rally_scn")
73 RESULTS_DIR = HOME + functest_yaml.get("general").get("directories"). \
74     get("dir_rally_res") + "/rally/"
75 TEST_DB = functest_yaml.get("results").get("test_db_url")
76
77
78 def push_results_to_db(payload, module):
79
80     url = TEST_DB + "/results"
81     installer = functest_utils.get_installer_type(logger)
82     git_version = functest_utils.get_git_branch(args.repo_path)
83     # TODO pod_name hardcoded, info shall come from Jenkins
84     params = {"project_name": "functest", "case_name": "Rally-"+module,
85               "pod_name": "opnfv-jump-2", "installer": installer,
86               "version": git_version, "details": payload}
87
88     headers = {'Content-Type': 'application/json'}
89     r = requests.post(url, data=json.dumps(params), headers=headers)
90     logger.debug(r)
91
92
93 def get_task_id(cmd_raw):
94     """
95     get task id from command rally result
96     :param cmd_raw:
97     :return: task_id as string
98     """
99     taskid_re = re.compile('^Task +(.*): started$')
100     for line in cmd_raw.splitlines(True):
101         line = line.strip()
102         match = taskid_re.match(line)
103         if match:
104             return match.group(1)
105     return None
106
107
108 def task_succeed(json_raw):
109     """
110     Parse JSON from rally JSON results
111     :param json_raw:
112     :return: Bool
113     """
114     rally_report = json.loads(json_raw)
115     rally_report = rally_report[0]
116     if rally_report is None:
117         return False
118     if rally_report.get('result') is None:
119         return False
120
121     for result in rally_report.get('result'):
122         if len(result.get('error')) > 0:
123             return False
124
125     return True
126
127
128 def run_task(test_name):
129     #
130     # the "main" function of the script who lunch rally for a task
131     # :param test_name: name for the rally test
132     # :return: void
133     #
134
135     logger.info('starting {} test ...'.format(test_name))
136
137     # check directory for scenarios test files or retrieve from git otherwise
138     proceed_test = True
139     test_file_name = '{}opnfv-{}.json'.format(SCENARIOS_DIR, test_name)
140
141     if not os.path.exists(test_file_name):
142         logger.error("The scenario '%s' does not exist." % test_file_name)
143         exit(-1)
144
145     # we do the test only if we have a scenario test file
146     if proceed_test:
147         logger.debug('Scenario fetched from : {}'.format(test_file_name))
148         cmd_line = "rally task start --abort-on-sla-failure {}".format(test_file_name)
149         logger.debug('running command line : {}'.format(cmd_line))
150         cmd = os.popen(cmd_line)
151         task_id = get_task_id(cmd.read())
152         logger.debug('task_id : {}'.format(task_id))
153
154         if task_id is None:
155             logger.error("failed to retrieve task_id")
156             exit(-1)
157
158         # check for result directory and create it otherwise
159         if not os.path.exists(RESULTS_DIR):
160             logger.debug('does not exists, we create it'.format(RESULTS_DIR))
161             os.makedirs(RESULTS_DIR)
162
163         # write html report file
164         report_file_name = '{}opnfv-{}.html'.format(RESULTS_DIR, test_name)
165         cmd_line = "rally task report {} --out {}".format(task_id,
166                                                           report_file_name)
167
168         logger.debug('running command line : {}'.format(cmd_line))
169         os.popen(cmd_line)
170
171         # get and save rally operation JSON result
172         cmd_line = "rally task results %s" % task_id
173         logger.debug('running command line : {}'.format(cmd_line))
174         cmd = os.popen(cmd_line)
175         json_results = cmd.read()
176         with open('{}opnfv-{}.json'.format(RESULTS_DIR, test_name), 'w') as f:
177             logger.debug('saving json file')
178             f.write(json_results)
179
180         with open('{}opnfv-{}.json'
181                   .format(RESULTS_DIR, test_name)) as json_file:
182             json_data = json.load(json_file)
183
184         # Push results in payload of testcase
185         if args.report:
186             logger.debug("Push result into DB")
187             push_results_to_db(json_data, test_name)
188
189         """ parse JSON operation result """
190         if task_succeed(json_results):
191             print 'Test OK'
192         else:
193             print 'Test KO'
194     else:
195         logger.error('{} test failed, unable to fetch a scenario test file'
196                      .format(test_name))
197
198
199 def main():
200     # configure script
201     if not (args.test_name in tests):
202         logger.error('argument not valid')
203         exit(-1)
204
205     if args.test_name == "all":
206         for test_name in tests:
207             if not (test_name == 'all' or
208                     test_name == 'heat' or
209                     test_name == 'ceilometer' or
210                     test_name == 'smoke' or
211                     test_name == 'vm'):
212                 print(test_name)
213                 run_task(test_name)
214     else:
215         print(args.test_name)
216         run_task(args.test_name)
217
218 if __name__ == '__main__':
219     main()