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