5738fd47988a935a145501eca9d754d285736fcc
[functest-xtesting.git] / testcases / VIM / OpenStack / CI / libraries / run_tempest.py
1 #!/usr/bin/env python
2 #
3 # Description:
4 #    Runs tempest and pushes the results to the DB
5 #
6 # Authors:
7 #    morgan.richomme@orange.com
8 #    jose.lausuch@ericsson.com
9 #    viktor.tikkanen@nokia.com
10 #
11 # All rights reserved. This program and the accompanying materials
12 # are made available under the terms of the Apache License, Version 2.0
13 # which accompanies this distribution, and is available at
14 # http://www.apache.org/licenses/LICENSE-2.0
15 #
16 import argparse
17 import json
18 import logging
19 import os
20 import re
21 import requests
22 import shutil
23 import subprocess
24 import sys
25 import time
26 import yaml
27 import keystoneclient.v2_0.client as ksclient
28 from neutronclient.v2_0 import client as neutronclient
29
30 modes = ['full', 'smoke', 'baremetal', 'compute', 'data_processing',
31          'identity', 'image', 'network', 'object_storage', 'orchestration',
32          'telemetry', 'volume', 'custom']
33
34 """ tests configuration """
35 parser = argparse.ArgumentParser()
36 parser.add_argument("-d", "--debug", help="Debug mode",  action="store_true")
37 parser.add_argument("-m", "--mode", help="Tempest test mode [smoke, all]",
38                     default="smoke")
39 parser.add_argument("-r", "--report",
40                     help="Create json result file",
41                     action="store_true")
42
43 args = parser.parse_args()
44
45 """ logging configuration """
46 logger = logging.getLogger('run_tempest')
47 logger.setLevel(logging.DEBUG)
48
49 ch = logging.StreamHandler()
50 if args.debug:
51     ch.setLevel(logging.DEBUG)
52 else:
53     ch.setLevel(logging.INFO)
54
55 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
56 ch.setFormatter(formatter)
57 logger.addHandler(ch)
58
59 REPO_PATH=os.environ['repos_dir']+'/functest/'
60 if not os.path.exists(REPO_PATH):
61     logger.error("Functest repository directory not found '%s'" % REPO_PATH)
62     exit(-1)
63 sys.path.append(REPO_PATH + "testcases/")
64 import functest_utils
65
66 with open("/home/opnfv/functest/conf/config_functest.yaml") as f:
67     functest_yaml = yaml.safe_load(f)
68 f.close()
69 TEST_DB = functest_yaml.get("results").get("test_db_url")
70
71 MODE = "smoke"
72 TENANT_NAME = functest_yaml.get("tempest").get("identity").get("tenant_name")
73 TENANT_DESCRIPTION = functest_yaml.get("tempest").get("identity").get("tenant_description")
74 USER_NAME = functest_yaml.get("tempest").get("identity").get("user_name")
75 USER_PASSWORD = functest_yaml.get("tempest").get("identity").get("user_password")
76 SSH_USER_REGEX = functest_yaml.get("tempest").get("input-scenario").get("ssh_user_regex")
77 DEPLOYMENT_MAME = functest_yaml.get("rally").get("deployment_name")
78 RALLY_INSTALLATION_DIR = functest_yaml.get("general").get("directories").get("dir_rally_inst")
79 RESULTS_DIR = functest_yaml.get("general").get("directories").get("dir_results")
80 TEMPEST_RESULTS_DIR = RESULTS_DIR + '/tempest'
81
82
83 def get_info(file_result):
84     test_run = ""
85     duration = ""
86     test_failed = ""
87
88     p = subprocess.Popen('cat tempest.log',
89                          shell=True, stdout=subprocess.PIPE,
90                          stderr=subprocess.STDOUT)
91     for line in p.stdout.readlines():
92         # print line,
93         if (len(test_run) < 1):
94             test_run = re.findall("[0-9]*\.[0-9]*s", line)
95         if (len(duration) < 1):
96             duration = re.findall("[0-9]*\ tests", line)
97         regexp = r"(failures=[0-9]+)"
98         if (len(test_failed) < 1):
99             test_failed = re.findall(regexp, line)
100
101     retval = p.wait()
102
103     logger.debug("test_run:"+test_run)
104     logger.debug("duration:"+duration)
105
106
107 def push_results_to_db(payload, module, pod_name):
108
109     # TODO move DB creds into config file
110     url = TEST_DB + "/results"
111     installer = functest_utils.get_installer_type(logger)
112     scenario = functest_utils.get_scenario(logger)
113     logger.info("Pushing results to DB: '%s'." % url)
114
115     params = {"project_name": "functest", "case_name": "Tempest",
116               "pod_name": str(pod_name), 'installer': installer,
117               "version": scenario, 'details': payload}
118     headers = {'Content-Type': 'application/json'}
119
120     r = requests.post(url, data=json.dumps(params), headers=headers)
121     logger.debug(r)
122
123
124 def create_tempest_resources():
125     ks_creds = functest_utils.get_credentials("keystone")
126     logger.info("Creating tenant and user for Tempest suite")
127     keystone = ksclient.Client(**ks_creds)
128     tenant_id = functest_utils.create_tenant(keystone, TENANT_NAME, TENANT_DESCRIPTION)
129     if tenant_id == '':
130         logger.error("Error : Failed to create %s tenant" %TENANT_NAME)
131
132     user_id = functest_utils.create_user(keystone, USER_NAME, USER_PASSWORD, None, tenant_id)
133     if user_id == '':
134         logger.error("Error : Failed to create %s user" %USER_NAME)
135
136
137 def free_tempest_resources():
138     ks_creds = functest_utils.get_credentials("keystone")
139     logger.info("Deleting tenant and user for Tempest suite)")
140     keystone = ksclient.Client(**ks_creds)
141
142     user_id = functest_utils.get_user_id(keystone, USER_NAME)
143     if user_id == '':
144         logger.error("Error : Failed to get id of %s user" % USER_NAME)
145     else:
146         if not functest_utils.delete_user(keystone, user_id):
147             logger.error("Error : Failed to delete %s user" % USER_NAME)
148
149     tenant_id = functest_utils.get_tenant_id(keystone, TENANT_NAME)
150     if tenant_id == '':
151         logger.error("Error : Failed to get id of %s tenant" % TENANT_NAME)
152     else:
153         if not functest_utils.delete_tenant(keystone, tenant_id):
154             logger.error("Error : Failed to delete %s tenant" % TENANT_NAME)
155
156
157 def configure_tempest():
158     """
159     Add/update needed parameters into tempest.conf file generated by Rally
160     """
161
162     logger.debug("Generating tempest.conf file...")
163     cmd = "rally verify genconfig"
164     functest_utils.execute_command(cmd,logger)
165
166     logger.debug("Resolving deployment UUID...")
167     cmd = "rally deployment list | awk '/"+DEPLOYMENT_MAME+"/ {print $2}'"
168     p = subprocess.Popen(cmd, shell=True,
169                          stdout=subprocess.PIPE,
170                          stderr=subprocess.STDOUT);
171     deployment_uuid = p.stdout.readline().rstrip()
172     if deployment_uuid == "":
173         logger.debug("   Rally deployment NOT found")
174         return False
175
176     logger.debug("Finding tempest.conf file...")
177     tempest_conf_file = RALLY_INSTALLATION_DIR+"/tempest/for-deployment-" \
178                         +deployment_uuid+"/tempest.conf"
179     if not os.path.isfile(tempest_conf_file):
180         logger.error("   Tempest configuration file %s NOT found." % tempest_conf_file)
181         return False
182
183     logger.debug("  Updating fixed_network_name...")
184     private_net_name = ""
185     creds_neutron = functest_utils.get_credentials("neutron")
186     neutron_client = neutronclient.Client(**creds_neutron)
187     private_net = functest_utils.get_private_net(neutron_client)
188     if private_net is None:
189         logger.error("No shared private networks found.")
190     else:
191         private_net_name = private_net['name']
192     cmd = "crudini --set "+tempest_conf_file+" compute fixed_network_name " \
193           +private_net_name
194     functest_utils.execute_command(cmd,logger)
195
196     logger.debug("  Updating non-admin credentials...")
197     cmd = "crudini --set "+tempest_conf_file+" identity tenant_name " \
198           +TENANT_NAME
199     functest_utils.execute_command(cmd,logger)
200     cmd = "crudini --set "+tempest_conf_file+" identity username " \
201           +USER_NAME
202     functest_utils.execute_command(cmd,logger)
203     cmd = "crudini --set "+tempest_conf_file+" identity password " \
204           +USER_PASSWORD
205     functest_utils.execute_command(cmd,logger)
206     cmd = "crudini --set "+tempest_conf_file+" input-scenario ssh_user_regex " \
207           +SSH_USER_REGEX
208     functest_utils.execute_command(cmd,logger)
209
210
211     # Copy tempest.conf to /home/opnfv/functest/results/tempest/
212     print shutil.copyfile(tempest_conf_file,TEMPEST_RESULTS_DIR+'/tempest.conf')
213     return True
214
215
216 def run_tempest(OPTION):
217     #
218     # the "main" function of the script which launches Rally to run Tempest
219     # :param option: tempest option (smoke, ..)
220     # :return: void
221     #
222     logger.info("Starting Tempest test suite: '%s'." % OPTION)
223     cmd_line = "rally verify start "+OPTION
224     logger.debug('Executing command : {}'.format(cmd_line))
225
226     CI_DEBUG = os.environ.get("CI_DEBUG")
227     if CI_DEBUG == "true" or CI_DEBUG == "True":
228         subprocess.call(cmd_line, shell=True, stderr=subprocess.STDOUT)
229     else:
230         header = "Tempest environment:\n"\
231             "  Installer: %s\n  Scenario: %s\n  Node: %s\n  Date: %s\n" % \
232             (os.getenv('INSTALLER_TYPE','Unknown'), \
233              os.getenv('DEPLOY_SCENARIO','Unknown'), \
234              os.getenv('NODE_NAME','Unknown'), \
235              time.strftime("%a %b %d %H:%M:%S %Z %Y"))
236
237         f_stdout = open(TEMPEST_RESULTS_DIR+"/tempest.log", 'w+')
238         f_stderr = open(TEMPEST_RESULTS_DIR+"/tempest-error.log", 'w+')
239         f_env = open(TEMPEST_RESULTS_DIR+"/environment.log", 'w+')
240         f_env.write(header)
241
242         subprocess.call(cmd_line, shell=True, stdout=f_stdout, stderr=f_stderr)
243
244         f_stdout.close()
245         f_stderr.close()
246         f_env.close()
247
248         cmd_line = "rally verify show"
249         subprocess.call(cmd_line, shell=True)
250
251     cmd_line = "rally verify list"
252     logger.debug('Executing command : {}'.format(cmd_line))
253     cmd = os.popen(cmd_line)
254     output = (((cmd.read()).splitlines()[3]).replace(" ", "")).split("|")
255     # Format:
256     # | UUID | Deployment UUID | smoke | tests | failures | Created at |
257     # Duration | Status  |
258     num_tests = output[4]
259     num_failures = output[5]
260     time_start = output[6]
261     duration = output[7]
262     # Compute duration (lets assume it does not take more than 60 min)
263     dur_min=int(duration.split(':')[1])
264     dur_sec_float=float(duration.split(':')[2])
265     dur_sec_int=int(round(dur_sec_float,0))
266     dur_sec_int = dur_sec_int + 60 * dur_min
267
268     # Generate json results for DB
269     json_results = {"timestart": time_start, "duration": dur_sec_int,
270                     "tests": int(num_tests), "failures": int(num_failures)}
271     logger.info("Results: "+str(json_results))
272     pod_name = functest_utils.get_pod_name(logger)
273
274     # Push results in payload of testcase
275     if args.report:
276         logger.debug("Push result into DB")
277         push_results_to_db(json_results, MODE, pod_name)
278
279
280 def main():
281     global MODE
282     if not (args.mode):
283         MODE = "smoke"
284     elif not (args.mode in modes):
285         logger.error("Tempest mode not valid. Possible values are:\n"
286                      + str(modes))
287         exit(-1)
288     elif (args.mode == 'custom'):
289         MODE = "--tests-file "+REPO_PATH+"testcases/VIM/OpenStack/CI/custom_tests/test_list.txt"
290     else:
291         MODE = "--set "+args.mode
292
293     if not os.path.exists(TEMPEST_RESULTS_DIR):
294         os.makedirs(TEMPEST_RESULTS_DIR)
295
296     create_tempest_resources()
297     configure_tempest()
298     run_tempest(MODE)
299     free_tempest_resources()
300
301
302 if __name__ == '__main__':
303     main()