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