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