Used sed instead of crudini for updating regex
[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 = "sed -i 's/.*ssh_user_regex.*/ssh_user_regex = "+SSH_USER_REGEX+"/' "+tempest_conf_file
207     functest_utils.execute_command(cmd,logger)
208
209
210     # Copy tempest.conf to /home/opnfv/functest/results/tempest/
211     print shutil.copyfile(tempest_conf_file,TEMPEST_RESULTS_DIR+'/tempest.conf')
212     return True
213
214
215 def run_tempest(OPTION):
216     #
217     # the "main" function of the script which launches Rally to run Tempest
218     # :param option: tempest option (smoke, ..)
219     # :return: void
220     #
221     logger.info("Starting Tempest test suite: '%s'." % OPTION)
222     cmd_line = "rally verify start "+OPTION
223     logger.debug('Executing command : {}'.format(cmd_line))
224
225     CI_DEBUG = os.environ.get("CI_DEBUG")
226     if CI_DEBUG == "true" or CI_DEBUG == "True":
227         subprocess.call(cmd_line, shell=True, stderr=subprocess.STDOUT)
228     else:
229         header = "Tempest environment:\n"\
230             "  Installer: %s\n  Scenario: %s\n  Node: %s\n  Date: %s\n" % \
231             (os.getenv('INSTALLER_TYPE','Unknown'), \
232              os.getenv('DEPLOY_SCENARIO','Unknown'), \
233              os.getenv('NODE_NAME','Unknown'), \
234              time.strftime("%a %b %d %H:%M:%S %Z %Y"))
235
236         f_stdout = open(TEMPEST_RESULTS_DIR+"/tempest.log", 'w+')
237         f_stderr = open(TEMPEST_RESULTS_DIR+"/tempest-error.log", 'w+')
238         f_env = open(TEMPEST_RESULTS_DIR+"/environment.log", 'w+')
239         f_env.write(header)
240
241         subprocess.call(cmd_line, shell=True, stdout=f_stdout, stderr=f_stderr)
242
243         f_stdout.close()
244         f_stderr.close()
245         f_env.close()
246
247         cmd_line = "rally verify show"
248         subprocess.call(cmd_line, shell=True)
249
250     cmd_line = "rally verify list"
251     logger.debug('Executing command : {}'.format(cmd_line))
252     cmd = os.popen(cmd_line)
253     output = (((cmd.read()).splitlines()[3]).replace(" ", "")).split("|")
254     # Format:
255     # | UUID | Deployment UUID | smoke | tests | failures | Created at |
256     # Duration | Status  |
257     num_tests = output[4]
258     num_failures = output[5]
259     time_start = output[6]
260     duration = output[7]
261     # Compute duration (lets assume it does not take more than 60 min)
262     dur_min=int(duration.split(':')[1])
263     dur_sec_float=float(duration.split(':')[2])
264     dur_sec_int=int(round(dur_sec_float,0))
265     dur_sec_int = dur_sec_int + 60 * dur_min
266
267     # Generate json results for DB
268     json_results = {"timestart": time_start, "duration": dur_sec_int,
269                     "tests": int(num_tests), "failures": int(num_failures)}
270     logger.info("Results: "+str(json_results))
271     pod_name = functest_utils.get_pod_name(logger)
272
273     # Push results in payload of testcase
274     if args.report:
275         logger.debug("Push result into DB")
276         push_results_to_db(json_results, MODE, pod_name)
277
278
279 def main():
280     global MODE
281     if not (args.mode):
282         MODE = "smoke"
283     elif not (args.mode in modes):
284         logger.error("Tempest mode not valid. Possible values are:\n"
285                      + str(modes))
286         exit(-1)
287     elif (args.mode == 'custom'):
288         MODE = "--tests-file "+REPO_PATH+"testcases/VIM/OpenStack/CI/custom_tests/test_list.txt"
289     else:
290         MODE = "--set "+args.mode
291
292     if not os.path.exists(TEMPEST_RESULTS_DIR):
293         os.makedirs(TEMPEST_RESULTS_DIR)
294
295     create_tempest_resources()
296     configure_tempest()
297     run_tempest(MODE)
298     free_tempest_resources()
299
300
301 if __name__ == '__main__':
302     main()