blob: f5af0d7034398c49d6d3388123c3a4ae945a630c [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
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030017
Oleksii Butenkob0ca4cc2018-10-22 16:29:54 +030018from devops.helpers import helpers
19
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030020from tcp_tests import logger
21from tcp_tests import settings
22
Oleksii Butenkob0ca4cc2018-10-22 16:29:54 +030023
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030024LOG = logger.logger
25
26TEMPEST_CFG_DIR = '/tmp/test'
27
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030028
29class RuntestManager(object):
30 """Helper manager for execution tempest via runtest-formula"""
31
32 image_name = settings.TEMPEST_IMAGE
33 image_version = settings.TEMPEST_IMAGE_VERSION
34 container_name = 'run-tempest-ci'
35 master_host = "cfg01"
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +000036 control_host = "ctl01"
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030037 class_name = "runtest"
38 run_cmd = '/bin/bash -c "run-tempest"'
39
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +000040 def __init__(self, config, underlay, salt_api, cluster_name,
Oleksii Butenko74566102018-09-11 11:55:13 +030041 domain_name, tempest_threads,
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030042 tempest_pattern=settings.TEMPEST_PATTERN,
43 run_cmd=None, target='gtw01'):
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +000044 self.__config = config
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030045 self.underlay = underlay
46 self.__salt_api = salt_api
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030047 self.cluster_name = cluster_name
48 self.domain_name = domain_name
49 self.tempest_threads = tempest_threads
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030050 self.tempest_pattern = tempest_pattern
51 self.run_cmd = run_cmd or self.run_cmd
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +000052 self.target_name = self.underlay.get_target_node_names(target)[0]
53 self.master_name = self.underlay.get_target_node_names(
54 self.master_host)[0]
55 self.control_name = self.underlay.get_target_node_names(
56 self.control_host)[0]
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030057
58 @property
59 def salt_api(self):
60 return self.__salt_api
61
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +000062 @property
63 def runtest_pillar(self):
64 public_net = self.__config.underlay.dhcp_ranges[
65 settings.EXTERNAL_ADDRESS_POOL_NAME]
66 public_gateway = public_net["gateway"].encode("ascii")
67 public_cidr = public_net["cidr"].encode("ascii")
68 public_allocation_start = public_net["start"].encode("ascii")
69 public_allocation_end = public_net["end"].encode("ascii")
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030070
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +000071 return {
72 'classes': ['service.runtest.tempest',
73 'service.runtest.tempest.public_net',
74 'service.runtest.tempest.services.manila.glance'],
75 'parameters': {
76 '_param': {
77 'runtest_tempest_cfg_dir': TEMPEST_CFG_DIR,
78 'runtest_tempest_cfg_name': 'tempest.conf',
79 'runtest_tempest_public_net': 'public',
80 'openstack_public_neutron_subnet_gateway': public_gateway,
81 'openstack_public_neutron_subnet_cidr': public_cidr,
82 'openstack_public_neutron_subnet_allocation_start':
83 public_allocation_start,
84 'openstack_public_neutron_subnet_allocation_end':
85 public_allocation_end,
86 'tempest_test_target': self.target_name.encode("ascii"),
87 },
88 'neutron': {
89 'client': {
90 'enabled': True
91 }
92 },
93 'runtest': {
94 'enabled': True,
95 'keystonerc_node': 'ctl01*',
96 'tempest': {
97 'enabled': True,
98 'cfg_dir': '${_param:runtest_tempest_cfg_dir}',
99 'cfg_name': '${_param:runtest_tempest_cfg_name}',
Oleksii Butenko09cc0cd2018-10-16 14:31:57 +0300100 'put_keystone_rc_enabled': True,
101 'put_local_image_file_enabled': False,
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000102 'DEFAULT': {
103 'log_file': 'tempest.log'
104 },
105 'compute': {
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000106 'min_compute_nodes': 2,
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000107 },
108 'convert_to_uuid': {
109 'network': {
110 'public_network_id':
111 '${_param:runtest_tempest_public_net}'
112 }
113 },
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000114 'share': {
115 'capability_snapshot_support': True,
116 'run_driver_assisted_migration_tests': False,
117 'run_manage_unmanage_snapshot_tests': False,
118 'run_manage_unmanage_tests': False,
119 'run_migration_with_preserve_snapshots_tests':
120 False,
121 'run_quota_tests': True,
122 'run_replication_tests': False,
123 'run_snapshot_tests': True,
124 }}}}}
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300125
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300126 def fetch_arficats(self, username=None, file_format='xml'):
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000127 with self.underlay.remote(node_name=self.target_name,
128 username=None) as tgt:
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300129 result = tgt.execute('find {} -name "report_*.{}"'.format(
130 TEMPEST_CFG_DIR, file_format))
131 LOG.debug("Find result {0}".format(result))
132 assert len(result['stdout']) > 0, ('No report found, please check'
133 ' if test run was successful.')
134 report = result['stdout'][0].rstrip()
135 LOG.debug("Found files {0}".format(report))
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300136 tgt.download(
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300137 destination=report, # noqa
138 target=os.getcwd())
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300139
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000140 def store_runtest_model(self, runtest_pillar=None):
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300141 with self.underlay.yaml_editor(
142 file_path="/srv/salt/reclass/classes/cluster/"
143 "{cluster_name}/infra/"
144 "{class_name}.yml".format(
145 cluster_name=self.cluster_name,
146 class_name=self.class_name),
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000147 node_name=self.master_name) as editor:
148 editor.content = runtest_pillar or self.runtest_pillar
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300149 with self.underlay.yaml_editor(
150 file_path="/srv/salt/reclass/nodes/_generated/"
151 "cfg01.{domain_name}.yml".format(
152 domain_name=self.domain_name),
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000153 node_name=self.master_name) as editor:
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300154 editor.content['classes'].append(
155 'cluster.{cluster_name}.infra.{class_name}'.format(
156 cluster_name=self.cluster_name,
157 class_name=self.class_name))
158
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300159 def save_runtime_logs(self, logs=None, inspect=None):
160 if logs:
161 with open("{path}/{target}_tempest_run.log".format(
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000162 path=settings.LOGS_DIR,
163 target=self.target_name), 'w') as f:
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300164 LOG.info("Save tempest console log")
165 container_log = logs
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300166 f.write(container_log.encode('ascii', 'ignore'))
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300167
168 if inspect:
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300169 with open("{path}/{target}_tempest_container_info.json.log".format(
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000170 path=settings.LOGS_DIR,
171 target=self.target_name), 'w') as f:
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300172 LOG.info("Save tempest container inspect data")
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300173
174 container_inspect = json.dumps(inspect,
175 indent=4, sort_keys=True)
176 f.write(container_inspect)
177
Tatyana Leontovich61a821d2018-08-21 12:30:51 +0300178 def prepare(self, dpdk=None):
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300179 self.store_runtest_model()
Oleksii Butenko09cc0cd2018-10-16 14:31:57 +0300180 cirros_pillar = ("salt-call --out=newline_values_only "
181 "pillar.get "
182 "glance:client:identity:"
183 "admin_identity:image:cirros:location")
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000184 salt_cmd = "salt -l info --hard-crash --state-output=mixed "
185 salt_call_cmd = "salt-call -l info --hard-crash --state-output=mixed "
186 commands = [
187 {
188 'description': "Sync salt objects for runtest model",
189 'node_name': self.master_name,
190 'cmd': ("set -ex;" +
191 salt_cmd + "'*' saltutil.refresh_pillar && " +
192 salt_cmd + "'*' saltutil.sync_all")},
193 {
194 'description': ("Install docker.io package and "
195 "enable packets forwarding"),
196 'node_name': self.target_name,
197 'cmd': ("set -ex;" +
198 salt_call_cmd + " pkg.install docker.io && " +
199 " iptables --policy FORWARD ACCEPT")},
200 {
201 'description': "Install PyPI docker package",
202 'node_name': self.target_name,
203 'cmd': ("set -ex;" +
204 salt_call_cmd + " pip.install setuptools && " +
205 salt_call_cmd + " pip.install docker")},
206 {
207 'description': "Run salt.minion state for runtest formula",
208 'node_name': self.master_name,
209 'cmd': ("set -ex;" +
210 salt_call_cmd + " state.sls salt.minion && "
211 " sleep 20")},
212 {
213 'description': "Enforce keystone state for neutronv2",
214 'node_name': self.master_name,
215 'cmd': ("set -ex;" +
216 salt_call_cmd + " state.sls keystone.client")},
217 {
218 'description': "Create networks for Tempest tests",
219 'node_name': self.master_name,
220 'cmd': ("set -ex;" +
221 salt_call_cmd + " state.sls neutron.client")},
222 {
223 'description': "Create flavors for Tempest tests",
224 'node_name': self.master_name,
225 'cmd': ("set -ex;" +
226 salt_call_cmd + " state.sls nova.client")},
227 {
Oleksii Butenko09cc0cd2018-10-16 14:31:57 +0300228 'description': "Upload images for Tempest",
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000229 'node_name': self.master_name,
230 'cmd': ("set -ex;" +
231 salt_call_cmd + " state.sls glance.client")},
232 {
233 'description': "Generate config for Tempest",
234 'node_name': self.master_name,
235 'cmd': ("set -ex;" +
236 salt_call_cmd + " state.sls runtest")},
Oleksii Butenko09cc0cd2018-10-16 14:31:57 +0300237 {
238 'description': "Upload cirros image",
239 'node_name': self.master_name,
240 'cmd': ("set -ex;"
241 "cirros_url=$({}) && {} '{}' cmd.run "
242 "\"wget $cirros_url -O /tmp/TestCirros-0.3.5.img\""
243 .format(cirros_pillar, salt_cmd, self.target_name))},
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000244 ]
Oleksii Butenko5cd0a162018-06-14 18:18:10 +0300245
Tatyana Leontovich61a821d2018-08-21 12:30:51 +0300246 if dpdk:
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000247 commands.append({
248 'description': "Configure flavor for DPDK",
249 'node_name': self.control_name,
250 'cmd': ("set -ex;" +
251 salt_call_cmd + " cmd.run "
252 " '. /root/keystonercv3;"
253 " openstack flavor set m1.tiny_test"
254 " --property hw:mem_page_size=small'")},
255 )
Oleksii Butenko5cd0a162018-06-14 18:18:10 +0300256
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000257 self.__salt_api.execute_commands(commands=commands,
258 label="Prepare for Tempest")
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300259
260 def run_tempest(self, timeout=600):
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000261 tgt = self.target_name
262 image_nameversion = "{}:{}".format(self.image_name, self.image_version)
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300263
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000264 docker_args = (
Oleksii Butenkob0ca4cc2018-10-22 16:29:54 +0300265 " -t "
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000266 " --name {container_name} "
267 " -e ARGS=\"-r {tempest_pattern} -w {tempest_threads}\""
268 " -v {cfg_dir}/tempest.conf:/etc/tempest/tempest.conf"
269 " -v /tmp/:/tmp/"
270 " -v {cfg_dir}:/root/tempest"
271 " -v /etc/ssl/certs/:/etc/ssl/certs/"
Oleksii Butenkob0ca4cc2018-10-22 16:29:54 +0300272 " -d "
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000273 " {image_nameversion} {run_cmd}"
274 .format(
275 container_name=self.container_name,
276 image_nameversion=image_nameversion,
277 tempest_pattern=self.tempest_pattern,
278 tempest_threads=self.tempest_threads,
279 cfg_dir=TEMPEST_CFG_DIR,
280 run_cmd=self.run_cmd,
281 ))
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300282
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000283 commands = [
284 {
285 'description': "Run Tempest tests {0}".format(
286 image_nameversion),
287 'node_name': self.target_name,
288 'cmd': ("set -ex;" +
289 " docker rm --force {container_name} || true;"
290 " docker run {docker_args}"
291 .format(container_name=self.container_name,
292 docker_args=docker_args)),
293 'timeout': timeout},
294 ]
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300295
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000296 self.__salt_api.execute_commands(commands=commands,
297 label="Run Tempest tests")
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300298
Oleksii Butenkob0ca4cc2018-10-22 16:29:54 +0300299 def wait_status(s):
300 inspect_res = self.salt_api.local(tgt,
301 'dockerng.inspect',
302 self.container_name)
303 if 'return' in inspect_res:
304 inspect = inspect_res['return']
305 inspect = inspect[0]
306 inspect = next(inspect.iteritems())[1]
307 status = inspect['State']['Status']
308
309 return status.lower() == s.lower()
310
311 return False
312
313 helpers.wait(lambda: wait_status('exited'),
314 timeout=timeout,
315 timeout_msg=('Tempest run didnt finished '
316 'in {}'.format(timeout)))
317
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300318 inspect_res = self.salt_api.local(tgt,
319 'dockerng.inspect',
320 self.container_name)
321 inspect = inspect_res['return'][0]
322 inspect = next(inspect.iteritems())[1]
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300323 logs_res = self.salt_api.local(tgt,
324 'dockerng.logs',
325 self.container_name)
326 logs = logs_res['return'][0]
327 logs = next(logs.iteritems())[1]
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300328
329 res = self.salt_api.local(tgt, 'dockerng.rm', self.container_name)
330 LOG.info("Tempest container was removed".format(
331 json.dumps(res, indent=4)))
332
333 return {'inspect': inspect,
334 'logs': logs}
Oleksii Butenkoe82441d2018-06-12 16:01:33 +0300335
Tatyana Leontovich61a821d2018-08-21 12:30:51 +0300336 def prepare_and_run_tempest(self, username='root', dpdk=None):
Oleksii Butenkoe82441d2018-06-12 16:01:33 +0300337 """
338 Run tempest tests
339 """
340 tempest_timeout = settings.TEMPEST_TIMEOUT
Tatyana Leontovich61a821d2018-08-21 12:30:51 +0300341 self.prepare(dpdk=dpdk)
Oleksii Butenkoe82441d2018-06-12 16:01:33 +0300342 test_res = self.run_tempest(tempest_timeout)
343 self.fetch_arficats(username=username)
344 self.save_runtime_logs(**test_res)