Use only Rally as tescase name
[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 = HOME + functest_yaml.get("general").get("directories"). \
75     get("dir_rally_res") + "/rally/"
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, module):
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     # TODO pod_name hardcoded, info shall come from Jenkins
93     params = {"project_name": "functest", "case_name": "Rally",
94               "pod_name": "opnfv-jump-2", "installer": installer,
95               "version": git_version, "details": payload}
96
97     headers = {'Content-Type': 'application/json'}
98     r = requests.post(url, data=json.dumps(params), headers=headers)
99     logger.debug(r)
100
101
102 def get_task_id(cmd_raw):
103     """
104     get task id from command rally result
105     :param cmd_raw:
106     :return: task_id as string
107     """
108     taskid_re = re.compile('^Task +(.*): started$')
109     for line in cmd_raw.splitlines(True):
110         line = line.strip()
111         match = taskid_re.match(line)
112         if match:
113             return match.group(1)
114     return None
115
116
117 def create_glance_image(path, name, disk_format):
118     """
119     Create a glance image given the absolute path of the image, its name and the disk format
120     """
121     cmd = ("glance image-create --name " + name + "  --visibility public "
122            "--disk-format " + disk_format + " --container-format bare --file " + path)
123     functest_utils.execute_command(cmd, logger)
124     return True
125
126
127 def task_succeed(json_raw):
128     """
129     Parse JSON from rally JSON results
130     :param json_raw:
131     :return: Bool
132     """
133     rally_report = json.loads(json_raw)
134     rally_report = rally_report[0]
135     if rally_report is None:
136         return False
137     if rally_report.get('result') is None:
138         return False
139
140     for result in rally_report.get('result'):
141         if len(result.get('error')) > 0:
142             return False
143
144     return True
145
146
147 def run_task(test_name):
148     #
149     # the "main" function of the script who lunch rally for a task
150     # :param test_name: name for the rally test
151     # :return: void
152     #
153
154     logger.info('starting {} test ...'.format(test_name))
155
156     # check directory for scenarios test files or retrieve from git otherwise
157     proceed_test = True
158     test_file_name = '{}opnfv-{}.json'.format(SCENARIOS_DIR, test_name)
159
160     if not os.path.exists(test_file_name):
161         logger.error("The scenario '%s' does not exist." % test_file_name)
162         exit(-1)
163
164     # we do the test only if we have a scenario test file
165     if proceed_test:
166         logger.debug('Scenario fetched from : {}'.format(test_file_name))
167         cmd_line = "rally task start --abort-on-sla-failure {}".format(test_file_name)
168         logger.debug('running command line : {}'.format(cmd_line))
169         cmd = os.popen(cmd_line)
170         task_id = get_task_id(cmd.read())
171         logger.debug('task_id : {}'.format(task_id))
172
173         if task_id is None:
174             logger.error("failed to retrieve task_id")
175             exit(-1)
176
177         # check for result directory and create it otherwise
178         if not os.path.exists(RESULTS_DIR):
179             logger.debug('does not exists, we create it'.format(RESULTS_DIR))
180             os.makedirs(RESULTS_DIR)
181
182         # write html report file
183         report_file_name = '{}opnfv-{}.html'.format(RESULTS_DIR, test_name)
184         cmd_line = "rally task report {} --out {}".format(task_id,
185                                                           report_file_name)
186
187         logger.debug('running command line : {}'.format(cmd_line))
188         os.popen(cmd_line)
189
190         # get and save rally operation JSON result
191         cmd_line = "rally task results %s" % task_id
192         logger.debug('running command line : {}'.format(cmd_line))
193         cmd = os.popen(cmd_line)
194         json_results = cmd.read()
195         with open('{}opnfv-{}.json'.format(RESULTS_DIR, test_name), 'w') as f:
196             logger.debug('saving json file')
197             f.write(json_results)
198
199         with open('{}opnfv-{}.json'
200                   .format(RESULTS_DIR, test_name)) as json_file:
201             json_data = json.load(json_file)
202
203         # Push results in payload of testcase
204         if args.report:
205             logger.debug("Push result into DB")
206             push_results_to_db(json_data, test_name)
207
208         """ parse JSON operation result """
209         if task_succeed(json_results):
210             print 'Test OK'
211         else:
212             print 'Test KO'
213     else:
214         logger.error('{} test failed, unable to fetch a scenario test file'
215                      .format(test_name))
216
217
218 def delete_glance_image(name):
219     cmd = ("glance image-delete $(glance image-list | grep %s "
220            "| awk '{print $2}' | head -1)" % name)
221     functest_utils.execute_command(cmd, logger)
222     return True
223
224
225 def cleanup(nova):
226     logger.info("Cleaning up...")
227     logger.debug("Deleting image...")
228     delete_glance_image(GLANCE_IMAGE_NAME)
229     return True
230
231
232 def main():
233     # configure script
234     if not (args.test_name in tests):
235         logger.error('argument not valid')
236         exit(-1)
237
238     creds_nova = functest_utils.get_credentials("nova")
239     nova_client = novaclient.Client(**creds_nova)
240
241     logger.debug("Creating image '%s' from '%s'..." % (GLANCE_IMAGE_NAME, GLANCE_IMAGE_PATH))
242     create_glance_image(GLANCE_IMAGE_PATH, GLANCE_IMAGE_NAME, GLANCE_IMAGE_FORMAT)
243
244     # Check if the given image exists
245     try:
246         nova_client.images.find(name=GLANCE_IMAGE_NAME)
247         logger.info("Glance image found '%s'" % GLANCE_IMAGE_NAME)
248     except:
249         logger.error("ERROR: Glance image '%s' not found." % GLANCE_IMAGE_NAME)
250         logger.info("Available images are: ")
251         exit(-1)
252
253     if args.test_name == "all":
254         for test_name in tests:
255             if not (test_name == 'all' or
256                     test_name == 'heat' or
257                     test_name == 'ceilometer' or
258                     test_name == 'smoke' or
259                     test_name == 'vm'):
260                 print(test_name)
261                 run_task(test_name)
262     else:
263         print(args.test_name)
264         run_task(args.test_name)
265
266     cleanup(nova_client)
267
268 if __name__ == '__main__':
269     main()