Parameterize Rally scenarios
[functest.git] / testcases / VIM / OpenStack / CI / libraries / run_rally-cert.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', '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]}] "
37                          "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 ####todo:
73 #SCENARIOS_DIR = REPO_PATH + functest_yaml.get("general"). \
74 #    get("directories").get("dir_rally_scn")
75 SCENARIOS_DIR = REPO_PATH + "testcases/VIM/OpenStack/CI/rally_cert/"
76 ###
77 TEMPLATE_DIR = SCENARIOS_DIR + "scenario/templates"
78 SUPPORT_DIR = SCENARIOS_DIR + "scenario/support"
79 ###todo:
80 FLAVOR_NAME = "m1.tiny"
81 USERS_AMOUNT = 2
82 TENANTS_AMOUNT = 3
83 CONTROLLERS_AMOUNT = 2
84 ###
85 RESULTS_DIR = functest_yaml.get("general").get("directories"). \
86     get("dir_rally_res")
87 TEST_DB = functest_yaml.get("results").get("test_db_url")
88 FLOATING_NETWORK = functest_yaml.get("general"). \
89     get("openstack").get("neutron_public_net_name")
90
91 GLANCE_IMAGE_NAME = functest_yaml.get("general"). \
92     get("openstack").get("image_name")
93 GLANCE_IMAGE_FILENAME = functest_yaml.get("general"). \
94     get("openstack").get("image_file_name")
95 GLANCE_IMAGE_FORMAT = functest_yaml.get("general"). \
96     get("openstack").get("image_disk_format")
97 GLANCE_IMAGE_PATH = functest_yaml.get("general"). \
98     get("directories").get("dir_functest_data") + "/" + GLANCE_IMAGE_FILENAME
99 GLANCE_IMAGE_LOCATION = "http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-x86_64-disk.img"
100
101
102 def push_results_to_db(payload):
103
104     url = TEST_DB + "/results"
105     installer = functest_utils.get_installer_type(logger)
106     git_version = functest_utils.get_git_branch(args.repo_path)
107     pod_name = functest_utils.get_pod_name(logger)
108     # TODO pod_name hardcoded, info shall come from Jenkins
109     params = {"project_name": "functest", "case_name": "Rally",
110               "pod_name": pod_name, "installer": installer,
111               "version": git_version, "details": payload}
112
113     headers = {'Content-Type': 'application/json'}
114     r = requests.post(url, data=json.dumps(params), headers=headers)
115     logger.debug(r)
116
117
118 def get_task_id(cmd_raw):
119     """
120     get task id from command rally result
121     :param cmd_raw:
122     :return: task_id as string
123     """
124     taskid_re = re.compile('^Task +(.*): started$')
125     for line in cmd_raw.splitlines(True):
126         line = line.strip()
127         match = taskid_re.match(line)
128         if match:
129             return match.group(1)
130     return None
131
132
133 def create_glance_image(path, name, disk_format):
134     """
135     Create a glance image given the absolute path of the image, its name and the disk format
136     """
137     cmd = ("glance image-create --name " + name + "  --visibility public "
138            "--disk-format " + disk_format + " --container-format bare --file " + path)
139     functest_utils.execute_command(cmd, logger)
140     return True
141
142
143 def task_succeed(json_raw):
144     """
145     Parse JSON from rally JSON results
146     :param json_raw:
147     :return: Bool
148     """
149     rally_report = json.loads(json_raw)
150     rally_report = rally_report[0]
151     if rally_report is None:
152         return False
153     if rally_report.get('result') is None:
154         return False
155
156     for result in rally_report.get('result'):
157         if len(result.get('error')) > 0:
158             return False
159
160     return True
161
162
163 def build_task_args(test_file_name):
164     task_args = {'service_list': [test_file_name]}
165     task_args['smoke'] = False 
166     task_args['image_name'] = GLANCE_IMAGE_NAME
167     task_args['flavor_name'] = FLAVOR_NAME
168     task_args['glance_image_location'] = GLANCE_IMAGE_LOCATION
169     task_args['floating_network'] = FLOATING_NETWORK
170     task_args['tmpl_dir'] = TEMPLATE_DIR
171     task_args['sup_dir'] = SUPPORT_DIR
172     task_args['users_amount'] = USERS_AMOUNT
173     task_args['tenants_amount'] = TENANTS_AMOUNT
174     task_args['controllers_amount'] = CONTROLLERS_AMOUNT
175
176     return task_args
177
178
179 def run_task(test_name):
180     #
181     # the "main" function of the script who launch rally for a task
182     # :param test_name: name for the rally test
183     # :return: void
184     #
185
186     logger.info('starting {} test ...'.format(test_name))
187
188     task_file = '{}task.yaml'.format(SCENARIOS_DIR)
189     if not os.path.exists(task_file):
190         logger.error("Task file '%s' does not exist." % task_file)
191         exit(-1)
192
193     test_file_name = '{}opnfv-{}.yaml'.format(SCENARIOS_DIR + "scenario/", test_name)
194     if not os.path.exists(test_file_name):
195         logger.error("The scenario '%s' does not exist." % test_file_name)
196         exit(-1)
197
198     logger.debug('Scenario fetched from : {}'.format(test_file_name))
199
200     cmd_line = "rally task start --abort-on-sla-failure " + \
201                "--task {} ".format(task_file) + \
202                "--task-args \"{}\" ".format(build_task_args(test_name))
203     logger.debug('running command line : {}'.format(cmd_line))
204     cmd = os.popen(cmd_line)
205     task_id = get_task_id(cmd.read())
206     logger.debug('task_id : {}'.format(task_id))
207
208     if task_id is None:
209         logger.error("failed to retrieve task_id")
210         exit(-1)
211
212     # check for result directory and create it otherwise
213     if not os.path.exists(RESULTS_DIR):
214         logger.debug('does not exists, we create it'.format(RESULTS_DIR))
215         os.makedirs(RESULTS_DIR)
216
217     # write html report file
218     report_file_name = '{}opnfv-{}.html'.format(RESULTS_DIR, test_name)
219     cmd_line = "rally task report {} --out {}".format(task_id,
220                                                       report_file_name)
221
222     logger.debug('running command line : {}'.format(cmd_line))
223     os.popen(cmd_line)
224
225     # get and save rally operation JSON result
226     cmd_line = "rally task results %s" % task_id
227     logger.debug('running command line : {}'.format(cmd_line))
228     cmd = os.popen(cmd_line)
229     json_results = cmd.read()
230     with open('{}opnfv-{}.json'.format(RESULTS_DIR, test_name), 'w') as f:
231         logger.debug('saving json file')
232         f.write(json_results)
233
234     with open('{}opnfv-{}.json'
235               .format(RESULTS_DIR, test_name)) as json_file:
236         json_data = json.load(json_file)
237
238     # Push results in payload of testcase
239     if args.report:
240         logger.debug("Push result into DB")
241         push_results_to_db(json_data)
242
243     """ parse JSON operation result """
244     if task_succeed(json_results):
245         print 'Test OK'
246     else:
247         print 'Test KO'
248
249
250 def delete_glance_image(name):
251     cmd = ("glance image-delete $(glance image-list | grep %s "
252            "| awk '{print $2}' | head -1)" % name)
253     functest_utils.execute_command(cmd, logger)
254     return True
255
256
257 def cleanup(nova):
258     logger.info("Cleaning up...")
259     logger.debug("Deleting image...")
260     delete_glance_image(GLANCE_IMAGE_NAME)
261     return True
262
263
264 def main():
265     # configure script
266     if not (args.test_name in tests):
267         logger.error('argument not valid')
268         exit(-1)
269
270     creds_nova = functest_utils.get_credentials("nova")
271     nova_client = novaclient.Client(**creds_nova)
272
273     logger.debug("Creating image '%s' from '%s'..." % (GLANCE_IMAGE_NAME, GLANCE_IMAGE_PATH))
274     create_glance_image(GLANCE_IMAGE_PATH, GLANCE_IMAGE_NAME, GLANCE_IMAGE_FORMAT)
275
276
277     # Check if the given image exists
278     try:
279         nova_client.images.find(name=GLANCE_IMAGE_NAME)
280         logger.info("Glance image found '%s'" % GLANCE_IMAGE_NAME)
281     except:
282         logger.error("ERROR: Glance image '%s' not found." % GLANCE_IMAGE_NAME)
283         logger.info("Available images are: ")
284         exit(-1)
285
286     if args.test_name == "all":
287         for test_name in tests:
288             if not (test_name == 'all' or
289                     test_name == 'heat' or
290                     test_name == 'smoke' or
291                     test_name == 'vm'):
292                 print(test_name)
293                 run_task(test_name)
294     else:
295         print(args.test_name)
296         run_task(args.test_name)
297
298     cleanup(nova_client)
299
300 if __name__ == '__main__':
301     main()