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