Check if rally image already exist and don't create it again if so
[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     image_id = functest_utils.get_image_id(glance_client, GLANCE_IMAGE_NAME)
286
287     if image_id == '':
288         logger.debug("Creating image '%s' from '%s'..." % (GLANCE_IMAGE_NAME, \
289                                                            GLANCE_IMAGE_PATH))
290         image_id = functest_utils.create_glance_image(glance_client,\
291                                                 GLANCE_IMAGE_NAME,GLANCE_IMAGE_PATH)
292         if not image_id:
293             logger.error("Failed to create the Glance image...")
294             exit(-1)
295         else:
296             logger.debug("Image '%s' with ID '%s' created succesfully ." \
297                          % (GLANCE_IMAGE_NAME, image_id))
298     else:
299         logger.debug("Using existing image '%s' with ID '%s'..." \
300                      % (GLANCE_IMAGE_NAME,image_id))
301
302     if args.test_name == "all":
303         for test_name in tests:
304             if not (test_name == 'all' or
305                     test_name == 'vm'):
306                 print(test_name)
307                 run_task(test_name)
308     else:
309         print(args.test_name)
310         run_task(args.test_name)
311
312     logger.debug("Deleting image '%s' with ID '%s'..." \
313                          % (GLANCE_IMAGE_NAME, image_id))
314     if not functest_utils.delete_glance_image(nova_client, image_id):
315         logger.error("Error deleting the glance image")
316
317 if __name__ == '__main__':
318     main()