blob: b24cba230fbde8b2f5672f212bc6f1e5365a743f [file] [log] [blame]
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +03001# Copyright 2018 Mirantis, Inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030015import json
Oleksii Butenko71d76f32018-06-05 17:46:34 +030016import os
Oleksii Butenkoe41d39f2018-06-22 17:12:41 +030017import time
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030018
19from devops.helpers import helpers
20
21from tcp_tests import logger
22from tcp_tests import settings
23
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030024LOG = logger.logger
25
26TEMPEST_CFG_DIR = '/tmp/test'
27
28CONFIG = {
Oleksii Butenko6f691d42018-07-19 15:34:15 +030029 'classes': ['service.runtest.tempest',
30 'service.runtest.tempest.services.manila.glance'],
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030031 'parameters': {
32 '_param': {
33 'runtest_tempest_cfg_dir': TEMPEST_CFG_DIR,
34 'runtest_tempest_cfg_name': 'tempest.conf',
35 'runtest_tempest_public_net': 'net04_ext',
36 'tempest_test_target': 'gtw01*'
37 },
38 'neutron': {
39 'client': {
40 'enabled': True
41 }
42 },
43 'runtest': {
44 'enabled': True,
45 'keystonerc_node': 'ctl01*',
46 'tempest': {
47 'enabled': True,
48 'cfg_dir': '${_param:runtest_tempest_cfg_dir}',
49 'cfg_name': '${_param:runtest_tempest_cfg_name}',
50 'DEFAULT': {
51 'log_file': 'tempest.log'
52 },
53 'compute': {
54 'build_timeout': 600,
55 'max_microversion': 2.53,
56 'min_compute_nodes': 2,
57 'min_microversion': 2.1,
58 'volume_device_name': 'vdc'
59 },
60 'convert_to_uuid': {
61 'network': {
62 'public_network_id':
63 '${_param:runtest_tempest_public_net}'
64 }
65 },
66 'dns_feature_enabled': {
67 'api_admin': False,
68 'api_v1': False,
69 'api_v2': True,
70 'api_v2_quotas': True,
71 'api_v2_root_recordsets': True,
72 'bug_1573141_fixed': True
73 },
74 'heat_plugin': {
75 'floating_network_name':
76 '${_param:runtest_tempest_public_net}'
77 },
78 'network': {
79 'floating_network_name':
80 '${_param:runtest_tempest_public_net}'
81 },
82 'share': {
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030083 'capability_snapshot_support': True,
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030084 'run_driver_assisted_migration_tests': False,
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030085 'run_manage_unmanage_snapshot_tests': False,
86 'run_manage_unmanage_tests': False,
87 'run_migration_with_preserve_snapshots_tests': False,
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030088 'run_quota_tests': True,
89 'run_replication_tests': False,
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030090 'run_snapshot_tests': True,
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030091 }}}}}
92
93
94class RuntestManager(object):
95 """Helper manager for execution tempest via runtest-formula"""
96
97 image_name = settings.TEMPEST_IMAGE
98 image_version = settings.TEMPEST_IMAGE_VERSION
99 container_name = 'run-tempest-ci'
100 master_host = "cfg01"
101 master_tgt = "{}*".format(master_host)
102 class_name = "runtest"
103 run_cmd = '/bin/bash -c "run-tempest"'
104
105 def __init__(self, underlay, salt_api, cluster_name, domain_name,
106 tempest_threads, tempest_exclude_test_args,
107 tempest_pattern=settings.TEMPEST_PATTERN,
108 run_cmd=None, target='gtw01'):
109 self.underlay = underlay
110 self.__salt_api = salt_api
111 self.target = target
112 self.cluster_name = cluster_name
113 self.domain_name = domain_name
114 self.tempest_threads = tempest_threads
115 self.tempest_exclude_test_args = tempest_exclude_test_args
116 self.tempest_pattern = tempest_pattern
117 self.run_cmd = run_cmd or self.run_cmd
118
119 @property
120 def salt_api(self):
121 return self.__salt_api
122
123 def install_python_lib(self):
124 return self.salt_api.local(
125 "{}*".format(self.target),
126 'pip.install', 'docker'), None
127
Oleksii Butenko5cd0a162018-06-14 18:18:10 +0300128 def run_salt_minion_state(self):
Oleksii Butenkoe41d39f2018-06-22 17:12:41 +0300129 return self.salt_api.local('cfg01*', 'state.sls', 'salt.minion')
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300130
131 def create_networks(self):
Oleksii Butenkoe0184a02018-07-26 13:10:03 +0300132 return self.salt_api.local('cfg01*', 'state.sls', 'neutron.client')
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300133
134 def create_flavors(self):
Oleksii Butenkoe0184a02018-07-26 13:10:03 +0300135 return self.salt_api.local('cfg01*', 'state.sls', 'nova.client')
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300136
Tatyana Leontovich61a821d2018-08-21 12:30:51 +0300137 def set_property(self):
138 return self.salt_api.local(
139 tgt='ctl01*',
140 fun='cmd.run',
141 args='. /root/keystonercv3; openstack '
142 'flavor set m1.tiny_test '
143 '--property hw:mem_page_size=small')
144
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300145 def create_cirros(self):
Oleksii Butenkoe0184a02018-07-26 13:10:03 +0300146 return self.salt_api.local('cfg01*', 'state.sls', 'glance.client')
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300147
148 def generate_config(self):
Oleksii Butenkoe0184a02018-07-26 13:10:03 +0300149 return self.salt_api.local('cfg01*', 'state.sls', 'runtest')
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300150
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300151 def fetch_arficats(self, username=None, file_format='xml'):
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300152 target_name = next(node_name for node_name
153 in self.underlay.node_names() if
154 self.target in node_name)
155 with self.underlay.remote(node_name=target_name, username=None) as tgt:
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300156 result = tgt.execute('find {} -name "report_*.{}"'.format(
157 TEMPEST_CFG_DIR, file_format))
158 LOG.debug("Find result {0}".format(result))
159 assert len(result['stdout']) > 0, ('No report found, please check'
160 ' if test run was successful.')
161 report = result['stdout'][0].rstrip()
162 LOG.debug("Found files {0}".format(report))
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300163 tgt.download(
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300164 destination=report, # noqa
165 target=os.getcwd())
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300166
167 def store_runtest_model(self, config=CONFIG):
168 master_name = next(node_name for node_name
169 in self.underlay.node_names() if
170 self.master_host in node_name)
171 with self.underlay.yaml_editor(
172 file_path="/srv/salt/reclass/classes/cluster/"
173 "{cluster_name}/infra/"
174 "{class_name}.yml".format(
175 cluster_name=self.cluster_name,
176 class_name=self.class_name),
177 node_name=master_name) as editor:
178 editor.content = config
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300179 with self.underlay.yaml_editor(
180 file_path="/srv/salt/reclass/nodes/_generated/"
181 "cfg01.{domain_name}.yml".format(
182 domain_name=self.domain_name),
183 node_name=master_name) as editor:
184 editor.content['classes'].append(
185 'cluster.{cluster_name}.infra.{class_name}'.format(
186 cluster_name=self.cluster_name,
187 class_name=self.class_name))
188
189 self.salt_api.local('*', 'saltutil.refresh_pillar')
190 self.salt_api.local('*', 'saltutil.sync_all')
191
192 def save_runtime_logs(self, logs=None, inspect=None):
193 if logs:
194 with open("{path}/{target}_tempest_run.log".format(
195 path=settings.LOGS_DIR, target=self.target), 'w') as f:
196 LOG.info("Save tempest console log")
197 container_log = logs
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300198 f.write(container_log.encode('ascii', 'ignore'))
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300199
200 if inspect:
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300201 with open("{path}/{target}_tempest_container_info.json.log".format(
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300202 path=settings.LOGS_DIR, target=self.target), 'w') as f:
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300203 LOG.info("Save tempest container inspect data")
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300204
205 container_inspect = json.dumps(inspect,
206 indent=4, sort_keys=True)
207 f.write(container_inspect)
208
Tatyana Leontovich61a821d2018-08-21 12:30:51 +0300209 def prepare(self, dpdk=None):
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300210 self.store_runtest_model()
Oleksii Butenko5cd0a162018-06-14 18:18:10 +0300211
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300212 res = self.install_python_lib()
213 LOG.info(json.dumps(res, indent=4))
Oleksii Butenko5cd0a162018-06-14 18:18:10 +0300214
215 res = self.run_salt_minion_state()
216 LOG.info(json.dumps(res, indent=4))
Oleksii Butenkoe0184a02018-07-26 13:10:03 +0300217 time.sleep(20)
Oleksii Butenko5cd0a162018-06-14 18:18:10 +0300218
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300219 res = self.create_networks()
220 LOG.info(json.dumps(res, indent=4))
Oleksii Butenkoe0184a02018-07-26 13:10:03 +0300221 time.sleep(20)
Oleksii Butenko5cd0a162018-06-14 18:18:10 +0300222
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300223 res = self.create_flavors()
224 LOG.info(json.dumps(res, indent=4))
Oleksii Butenkoe0184a02018-07-26 13:10:03 +0300225 time.sleep(20)
Tatyana Leontovich61a821d2018-08-21 12:30:51 +0300226 if dpdk:
227 res = self.set_property()
228 LOG.info('Update flavor property')
229 LOG.info(json.dumps(res, indent=4))
230 time.sleep(20)
Oleksii Butenko5cd0a162018-06-14 18:18:10 +0300231
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300232 res = self.create_cirros()
233 LOG.info(json.dumps(res, indent=4))
Oleksii Butenkoe0184a02018-07-26 13:10:03 +0300234 time.sleep(20)
Oleksii Butenko5cd0a162018-06-14 18:18:10 +0300235
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300236 res = self.generate_config()
237 LOG.info(json.dumps(res, indent=4))
Oleksii Butenkoe0184a02018-07-26 13:10:03 +0300238 time.sleep(20)
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300239
240 def run_tempest(self, timeout=600):
241 tgt = "{}*".format(self.target)
242 params = {
243 "name": self.container_name,
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300244 "image": "{}:{}".format(self.image_name, self.image_version),
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300245 "environment": {
246 "ARGS": "-r {tempest_pattern} -w "
247 "{tempest_threads} "
248 "{tempest_exclude_test_args}".format(
249 tempest_pattern=self.tempest_pattern,
250 tempest_threads=self.tempest_threads,
251 tempest_exclude_test_args=self.tempest_exclude_test_args) # noqa
252 },
253 "binds": [
254 "{cfg_dir}/tempest.conf:/etc/tempest/tempest.conf".format(cfg_dir=TEMPEST_CFG_DIR), # noqa
255 "/tmp/:/tmp/",
256 "{cfg_dir}:/root/tempest".format(cfg_dir=TEMPEST_CFG_DIR),
257 "/etc/ssl/certs/:/etc/ssl/certs/"
258 ],
259 "auto_remove": False,
260 "cmd": self.run_cmd
261 }
262
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300263 res = self.salt_api.local(tgt, 'dockerng.pull', "{}:{}".format(
264 self.image_name, self.image_version))
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300265 LOG.info("Tempest image has beed pulled- \n{}".format(
266 json.dumps(res, indent=4)))
267
268 res = self.salt_api.local(tgt, 'dockerng.create', kwargs=params)
269 LOG.info("Tempest container has been created - \n{}".format(
270 json.dumps(res, indent=4)))
271
272 res = self.salt_api.local(tgt, 'dockerng.start', self.container_name)
273 LOG.info("Tempest container has been started - \n{}".format(
274 json.dumps(res, indent=4)))
275
276 def wait_status(s):
277 inspect_res = self.salt_api.local(tgt,
278 'dockerng.inspect',
279 self.container_name)
280 if 'return' in inspect_res:
281 inspect = inspect_res['return']
282 inspect = inspect[0]
283 inspect = next(inspect.iteritems())[1]
284 status = inspect['State']['Status']
285
286 return status.lower() == s.lower()
287
288 return False
289
290 helpers.wait(lambda: wait_status('exited'),
291 timeout=timeout,
292 timeout_msg=('Tempest run didnt finished '
293 'in {}'.format(timeout)))
294
295 inspect_res = self.salt_api.local(tgt,
296 'dockerng.inspect',
297 self.container_name)
298 inspect = inspect_res['return'][0]
299 inspect = next(inspect.iteritems())[1]
300 if inspect['State']['ExitCode'] != 0:
301 LOG.error("Tempest running failed")
302 LOG.info("Tempest tests have been finished - \n{}".format(
303 json.dumps(res, indent=4)))
304
305 logs_res = self.salt_api.local(tgt,
306 'dockerng.logs',
307 self.container_name)
308 logs = logs_res['return'][0]
309 logs = next(logs.iteritems())[1]
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300310 LOG.info("Tempest result - \n{}".format(
311 logs.encode('ascii', 'ignore')))
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300312
313 res = self.salt_api.local(tgt, 'dockerng.rm', self.container_name)
314 LOG.info("Tempest container was removed".format(
315 json.dumps(res, indent=4)))
316
317 return {'inspect': inspect,
318 'logs': logs}
Oleksii Butenkoe82441d2018-06-12 16:01:33 +0300319
Tatyana Leontovich61a821d2018-08-21 12:30:51 +0300320 def prepare_and_run_tempest(self, username='root', dpdk=None):
Oleksii Butenkoe82441d2018-06-12 16:01:33 +0300321 """
322 Run tempest tests
323 """
324 tempest_timeout = settings.TEMPEST_TIMEOUT
Tatyana Leontovich61a821d2018-08-21 12:30:51 +0300325 self.prepare(dpdk=dpdk)
Oleksii Butenkoe82441d2018-06-12 16:01:33 +0300326 test_res = self.run_tempest(tempest_timeout)
327 self.fetch_arficats(username=username)
328 self.save_runtime_logs(**test_res)