fix path for rally result
[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 import novaclient.v2.client as novaclient
26
27 """ tests configuration """
28 tests = ['authenticate', 'glance', 'cinder', 'ceilometer', 'heat', 'keystone',
29          'neutron', 'nova', 'quotas', 'requests', 'vm', 'all']
30 parser = argparse.ArgumentParser()
31 parser.add_argument("repo_path", help="Path to the repository")
32 parser.add_argument("test_name",
33                     help="Module name to be tested"
34                          "Possible values are : "
35                          "[ {d[0]} | {d[1]} | {d[2]} | {d[3]} | {d[4]} | "
36                          "{d[5]} | {d[6]} | {d[7]} | {d[8]} | {d[9]} | "
37                          "{d[10]} | {d[11]}]. The 'all' value "
38                          "performs all the  possible tests scenarios"
39                          .format(d=tests))
40
41 parser.add_argument("-d", "--debug", help="Debug mode",  action="store_true")
42 parser.add_argument("-r", "--report",
43                     help="Create json result file",
44                     action="store_true")
45
46 args = parser.parse_args()
47
48 sys.path.append(args.repo_path + "testcases/")
49 import functest_utils
50
51 """ logging configuration """
52 logger = logging.getLogger("run_rally")
53 logger.setLevel(logging.DEBUG)
54
55 ch = logging.StreamHandler()
56 if args.debug:
57     ch.setLevel(logging.DEBUG)
58 else:
59     ch.setLevel(logging.INFO)
60
61 formatter = logging.Formatter("%(asctime)s - %(name)s - "
62                               "%(levelname)s - %(message)s")
63 ch.setFormatter(formatter)
64 logger.addHandler(ch)
65
66 with open(args.repo_path+"testcases/config_functest.yaml") as f:
67     functest_yaml = yaml.safe_load(f)
68 f.close()
69
70 HOME = os.environ['HOME']+"/"
71 REPO_PATH = args.repo_path
72 SCENARIOS_DIR = REPO_PATH + functest_yaml.get("general"). \
73     get("directories").get("dir_rally_scn")
74 RESULTS_DIR = functest_yaml.get("general").get("directories"). \
75     get("dir_rally_res")
76 TEST_DB = functest_yaml.get("results").get("test_db_url")
77
78 GLANCE_IMAGE_NAME = "functest-img-rally"
79 GLANCE_IMAGE_FILENAME = functest_yaml.get("general"). \
80     get("openstack").get("image_file_name")
81 GLANCE_IMAGE_FORMAT = functest_yaml.get("general"). \
82     get("openstack").get("image_disk_format")
83 GLANCE_IMAGE_PATH = functest_yaml.get("general"). \
84     get("directories").get("dir_functest_data") + "/" + GLANCE_IMAGE_FILENAME
85
86
87 def push_results_to_db(payload):
88
89     url = TEST_DB + "/results"
90     installer = functest_utils.get_installer_type(logger)
91     git_version = functest_utils.get_git_branch(args.repo_path)
92     pod_name = functest_utils.get_pod_name(logger)
93     # TODO pod_name hardcoded, info shall come from Jenkins
94     params = {"project_name": "functest", "case_name": "Rally",
95               "pod_name": pod_name, "installer": installer,
96               "version": git_version, "details": payload}
97
98     headers = {'Content-Type': 'application/json'}
99     r = requests.post(url, data=json.dumps(params), headers=headers)
100     logger.debug(r)
101
102
103 def get_task_id(cmd_raw):
104     """
105     get task id from command rally result
106     :param cmd_raw:
107     :return: task_id as string
108     """
109     taskid_re = re.compile('^Task +(.*): started$')
110     for line in cmd_raw.splitlines(True):
111         line = line.strip()
112         match = taskid_re.match(line)
113         if match:
114             return match.group(1)
115     return None
116
117
118 def create_glance_image(path, name, disk_format):
119     """
120     Create a glance image given the absolute path of the image, its name and the disk format
121     """
122     cmd = ("glance image-create --name " + name + "  --visibility public "
123            "--disk-format " + disk_format + " --container-format bare --file " + path)
124     functest_utils.execute_command(cmd, logger)
125     return True
126
127
128 def task_succeed(json_raw):
129     """
130     Parse JSON from rally JSON results
131     :param json_raw:
132     :return: Bool
133     """
134     rally_report = json.loads(json_raw)
135     rally_report = rally_report[0]
136     if rally_report is None:
137         return False
138     if rally_report.get('result') is None:
139         return False
140
141     for result in rally_report.get('result'):
142         if len(result.get('error')) > 0:
143             return False
144
145     return True
146
147
148 def run_task(test_name):
149     #
150     # the "main" function of the script who lunch rally for a task
151     # :param test_name: name for the rally test
152     # :return: void
153     #
154
155     logger.info('starting {} test ...'.format(test_name))
156
157     # check directory for scenarios test files or retrieve from git otherwise
158     proceed_test = True
159     test_file_name = '{}opnfv-{}.json'.format(SCENARIOS_DIR, test_name)
160
161     if not os.path.exists(test_file_name):
162         logger.error("The scenario '%s' does not exist." % test_file_name)
163         exit(-1)
164
165     # we do the test only if we have a scenario test file
166     if proceed_test:
167         logger.debug('Scenario fetched from : {}'.format(test_file_name))
168         cmd_line = "rally task start --abort-on-sla-failure {}".format(test_file_name)
169         logger.debug('running command line : {}'.format(cmd_line))
170         cmd = os.popen(cmd_line)
171         task_id = get_task_id(cmd.read())
172         logger.debug('task_id : {}'.format(task_id))
173
174         if task_id is None:
175             logger.error("failed to retrieve task_id")
176             exit(-1)
177
178         # check for result directory and create it otherwise
179         if not os.path.exists(RESULTS_DIR):
180             logger.debug('does not exists, we create it'.format(RESULTS_DIR))
181             os.makedirs(RESULTS_DIR)
182
183         # write html report file
184         report_file_name = '{}opnfv-{}.html'.format(RESULTS_DIR, test_name)
185         cmd_line = "rally task report {} --out {}".format(task_id,
186                                                           report_file_name)
187
188         logger.debug('running command line : {}'.format(cmd_line))
189         os.popen(cmd_line)
190
191         # get and save rally operation JSON result
192         cmd_line = "rally task results %s" % task_id
193         logger.debug('running command line : {}'.format(cmd_line))
194         cmd = os.popen(cmd_line)
195         json_results = cmd.read()
196         with open('{}opnfv-{}.json'.format(RESULTS_DIR, test_name), 'w') as f:
197             logger.debug('saving json file')
198             f.write(json_results)
199
200         with open('{}opnfv-{}.json'
201                   .format(RESULTS_DIR, test_name)) as json_file:
202             json_data = json.load(json_file)
203
204         # Push results in payload of testcase
205         if args.report:
206             logger.debug("Push result into DB")
207             push_results_to_db(json_data)
208
209         """ parse JSON operation result """
210         if task_succeed(json_results):
211             print 'Test OK'
212         else:
213             print 'Test KO'
214     else:
215         logger.error('{} test failed, unable to fetch a scenario test file'
216                      .format(test_name))
217
218
219 def delete_glance_image(name):
220     cmd = ("glance image-delete $(glance image-list | grep %s "
221            "| awk '{print $2}' | head -1)" % name)
222     functest_utils.execute_command(cmd, logger)
223     return True
224
225
226 def cleanup(nova):
227     logger.info("Cleaning up...")
228     logger.debug("Deleting image...")
229     delete_glance_image(GLANCE_IMAGE_NAME)
230     return True
231
232
233 def main():
234     # configure script
235     if not (args.test_name in tests):
236         logger.error('argument not valid')
237         exit(-1)
238
239     creds_nova = functest_utils.get_credentials("nova")
240     nova_client = novaclient.Client(**creds_nova)
241
242     logger.debug("Creating image '%s' from '%s'..." % (GLANCE_IMAGE_NAME, GLANCE_IMAGE_PATH))
243     create_glance_image(GLANCE_IMAGE_PATH, GLANCE_IMAGE_NAME, GLANCE_IMAGE_FORMAT)
244
245     # Check if the given image exists
246     try:
247         nova_client.images.find(name=GLANCE_IMAGE_NAME)
248         logger.info("Glance image found '%s'" % GLANCE_IMAGE_NAME)
249     except:
250         logger.error("ERROR: Glance image '%s' not found." % GLANCE_IMAGE_NAME)
251         logger.info("Available images are: ")
252         exit(-1)
253
254     if args.test_name == "all":
255         for test_name in tests:
256             if not (test_name == 'all' or
257                     test_name == 'heat' or
258                     test_name == 'ceilometer' or
259                     test_name == 'smoke' or
260                     test_name == 'vm'):
261                 print(test_name)
262                 run_task(test_name)
263     else:
264         print(args.test_name)
265         run_task(args.test_name)
266
267     cleanup(nova_client)
268
269 if __name__ == '__main__':
270     main()