blob: bc39643f833b9d6dcfc7283324a507a850c3c76b [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"
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030036 class_name = "runtest"
37 run_cmd = '/bin/bash -c "run-tempest"'
38
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +000039 def __init__(self, config, underlay, salt_api, cluster_name,
Oleksii Butenko74566102018-09-11 11:55:13 +030040 domain_name, tempest_threads,
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030041 tempest_pattern=settings.TEMPEST_PATTERN,
Tatyana Leontovich48a33c12019-01-03 02:19:25 +020042 run_cmd=None, target='gtw01*', control_host='ctl01*',
43 compute_host='cmp001*'):
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]
Tatyana Leontovich48a33c12019-01-03 02:19:25 +020055 self.control_name = control_host
56 self.compute_name = compute_host
obutenko3e44b602018-11-13 12:50:22 +020057 self.barbican = False
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030058
59 @property
60 def salt_api(self):
61 return self.__salt_api
62
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +000063 @property
64 def runtest_pillar(self):
65 public_net = self.__config.underlay.dhcp_ranges[
66 settings.EXTERNAL_ADDRESS_POOL_NAME]
67 public_gateway = public_net["gateway"].encode("ascii")
68 public_cidr = public_net["cidr"].encode("ascii")
69 public_allocation_start = public_net["start"].encode("ascii")
70 public_allocation_end = public_net["end"].encode("ascii")
Dennis Dmitrievcf52b8e2018-11-06 17:28:38 +020071 tempest_test_target = self.target_name.encode("ascii") + "*"
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030072
obutenko3e44b602018-11-13 12:50:22 +020073 pillar = {
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +000074 'classes': ['service.runtest.tempest',
75 'service.runtest.tempest.public_net',
76 'service.runtest.tempest.services.manila.glance'],
77 'parameters': {
78 '_param': {
79 'runtest_tempest_cfg_dir': TEMPEST_CFG_DIR,
80 'runtest_tempest_cfg_name': 'tempest.conf',
81 'runtest_tempest_public_net': 'public',
82 'openstack_public_neutron_subnet_gateway': public_gateway,
83 'openstack_public_neutron_subnet_cidr': public_cidr,
84 'openstack_public_neutron_subnet_allocation_start':
85 public_allocation_start,
86 'openstack_public_neutron_subnet_allocation_end':
87 public_allocation_end,
Dennis Dmitrievcf52b8e2018-11-06 17:28:38 +020088 'tempest_test_target': tempest_test_target,
sgudz2f5aaf62018-10-30 13:20:04 +020089 'glance_image_cirros_location':
90 'http://cz8133.bud.mirantis.net:8099'
91 '/cirros-0.3.5-x86_64-disk.img',
92 'glance_image_fedora_location':
93 'http://cz8133.bud.mirantis.net:8099'
94 '/Fedora-Cloud-Base-27-1.6.x86_64.qcow2',
95 'glance_image_manila_location':
96 'http://cz8133.bud.mirantis.net:8099'
97 '/manila-service-image-master.qcow2',
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +000098 },
99 'neutron': {
100 'client': {
101 'enabled': True
102 }
103 },
104 'runtest': {
105 'enabled': True,
106 'keystonerc_node': 'ctl01*',
107 'tempest': {
108 'enabled': True,
109 'cfg_dir': '${_param:runtest_tempest_cfg_dir}',
110 'cfg_name': '${_param:runtest_tempest_cfg_name}',
Oleksii Butenko09cc0cd2018-10-16 14:31:57 +0300111 'put_keystone_rc_enabled': True,
112 'put_local_image_file_enabled': False,
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000113 'DEFAULT': {
114 'log_file': 'tempest.log'
115 },
116 'compute': {
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000117 'min_compute_nodes': 2,
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000118 },
119 'convert_to_uuid': {
120 'network': {
121 'public_network_id':
122 '${_param:runtest_tempest_public_net}'
123 }
124 },
Oleksii Butenko173e7702018-10-24 18:31:12 +0300125 'heat_plugin': {
126 'build_timeout': '600'
127 },
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000128 'share': {
129 'capability_snapshot_support': True,
130 'run_driver_assisted_migration_tests': False,
131 'run_manage_unmanage_snapshot_tests': False,
132 'run_manage_unmanage_tests': False,
133 'run_migration_with_preserve_snapshots_tests':
134 False,
135 'run_quota_tests': True,
136 'run_replication_tests': False,
137 'run_snapshot_tests': True,
138 }}}}}
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300139
obutenko3e44b602018-11-13 12:50:22 +0200140 if self.barbican:
141 pillar['classes'].append('service.runtest.tempest.barbican')
142
143 return pillar
144
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300145 def fetch_arficats(self, username=None, file_format='xml'):
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000146 with self.underlay.remote(node_name=self.target_name,
147 username=None) as tgt:
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300148 result = tgt.execute('find {} -name "report_*.{}"'.format(
149 TEMPEST_CFG_DIR, file_format))
150 LOG.debug("Find result {0}".format(result))
151 assert len(result['stdout']) > 0, ('No report found, please check'
152 ' if test run was successful.')
153 report = result['stdout'][0].rstrip()
154 LOG.debug("Found files {0}".format(report))
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300155 tgt.download(
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300156 destination=report, # noqa
157 target=os.getcwd())
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300158
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000159 def store_runtest_model(self, runtest_pillar=None):
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300160 with self.underlay.yaml_editor(
161 file_path="/srv/salt/reclass/classes/cluster/"
162 "{cluster_name}/infra/"
163 "{class_name}.yml".format(
164 cluster_name=self.cluster_name,
165 class_name=self.class_name),
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000166 node_name=self.master_name) as editor:
167 editor.content = runtest_pillar or self.runtest_pillar
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300168 with self.underlay.yaml_editor(
169 file_path="/srv/salt/reclass/nodes/_generated/"
170 "cfg01.{domain_name}.yml".format(
171 domain_name=self.domain_name),
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000172 node_name=self.master_name) as editor:
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300173 editor.content['classes'].append(
174 'cluster.{cluster_name}.infra.{class_name}'.format(
175 cluster_name=self.cluster_name,
176 class_name=self.class_name))
177
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300178 def save_runtime_logs(self, logs=None, inspect=None):
179 if logs:
180 with open("{path}/{target}_tempest_run.log".format(
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000181 path=settings.LOGS_DIR,
182 target=self.target_name), 'w') as f:
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300183 LOG.info("Save tempest console log")
184 container_log = logs
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300185 f.write(container_log.encode('ascii', 'ignore'))
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300186
187 if inspect:
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300188 with open("{path}/{target}_tempest_container_info.json.log".format(
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000189 path=settings.LOGS_DIR,
190 target=self.target_name), 'w') as f:
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300191 LOG.info("Save tempest container inspect data")
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300192
193 container_inspect = json.dumps(inspect,
194 indent=4, sort_keys=True)
195 f.write(container_inspect)
196
Tatyana Leontovichdb185db2018-11-30 12:46:40 +0200197 def prepare(self, store_run_test_model):
obutenko3e44b602018-11-13 12:50:22 +0200198 barbican_pillar = "nova:controller:barbican:enabled"
199 result = self.__salt_api.get_pillar(tgt=self.control_name,
200 pillar=barbican_pillar)
201 self.barbican = result[0].get(self.control_name, False)
Tatyana Leontovichdb185db2018-11-30 12:46:40 +0200202 if store_run_test_model:
203 self.store_runtest_model()
Oleksii Butenko09cc0cd2018-10-16 14:31:57 +0300204 cirros_pillar = ("salt-call --out=newline_values_only "
205 "pillar.get "
206 "glance:client:identity:"
207 "admin_identity:image:cirros:location")
sgudza5c901d2018-10-29 15:08:33 +0200208 dpdk_pillar = "linux:network:dpdk:enabled"
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000209 salt_cmd = "salt -l info --hard-crash --state-output=mixed "
210 salt_call_cmd = "salt-call -l info --hard-crash --state-output=mixed "
sgudza5c901d2018-10-29 15:08:33 +0200211
212 result = self.__salt_api.get_pillar(tgt=self.compute_name,
213 pillar=dpdk_pillar)
214
215 dpdk = result[0].get(self.compute_name, False)
216 LOG.info("DPDK enabled: {}".format(bool(dpdk)))
217
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000218 commands = [
219 {
220 'description': "Sync salt objects for runtest model",
221 'node_name': self.master_name,
222 'cmd': ("set -ex;" +
223 salt_cmd + "'*' saltutil.refresh_pillar && " +
224 salt_cmd + "'*' saltutil.sync_all")},
225 {
Tatyana Leontovicheea03e92018-11-20 12:51:50 +0200226 'description': ("Install docker-ce package and "
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000227 "enable packets forwarding"),
228 'node_name': self.target_name,
229 'cmd': ("set -ex;" +
Tatyana Leontovicheea03e92018-11-20 12:51:50 +0200230 salt_call_cmd + " pkg.install docker-ce && " +
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000231 " iptables --policy FORWARD ACCEPT")},
232 {
233 'description': "Install PyPI docker package",
234 'node_name': self.target_name,
235 'cmd': ("set -ex;" +
236 salt_call_cmd + " pip.install setuptools && " +
237 salt_call_cmd + " pip.install docker")},
238 {
239 'description': "Run salt.minion state for runtest formula",
240 'node_name': self.master_name,
241 'cmd': ("set -ex;" +
242 salt_call_cmd + " state.sls salt.minion && "
243 " sleep 20")},
244 {
245 'description': "Enforce keystone state for neutronv2",
246 'node_name': self.master_name,
247 'cmd': ("set -ex;" +
248 salt_call_cmd + " state.sls keystone.client")},
249 {
250 'description': "Create networks for Tempest tests",
251 'node_name': self.master_name,
252 'cmd': ("set -ex;" +
253 salt_call_cmd + " state.sls neutron.client")},
254 {
255 'description': "Create flavors for Tempest tests",
256 'node_name': self.master_name,
257 'cmd': ("set -ex;" +
258 salt_call_cmd + " state.sls nova.client")},
259 {
Oleksii Butenko09cc0cd2018-10-16 14:31:57 +0300260 'description': "Upload images for Tempest",
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000261 'node_name': self.master_name,
262 'cmd': ("set -ex;" +
263 salt_call_cmd + " state.sls glance.client")},
264 {
265 'description': "Generate config for Tempest",
266 'node_name': self.master_name,
267 'cmd': ("set -ex;" +
268 salt_call_cmd + " state.sls runtest")},
Oleksii Butenko09cc0cd2018-10-16 14:31:57 +0300269 {
270 'description': "Upload cirros image",
271 'node_name': self.master_name,
272 'cmd': ("set -ex;"
273 "cirros_url=$({}) && {} '{}' cmd.run "
274 "\"wget $cirros_url -O /tmp/TestCirros-0.3.5.img\""
275 .format(cirros_pillar, salt_cmd, self.target_name))},
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000276 ]
Oleksii Butenko5cd0a162018-06-14 18:18:10 +0300277
Tatyana Leontovich61a821d2018-08-21 12:30:51 +0300278 if dpdk:
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000279 commands.append({
280 'description': "Configure flavor for DPDK",
281 'node_name': self.control_name,
282 'cmd': ("set -ex;" +
283 salt_call_cmd + " cmd.run "
284 " '. /root/keystonercv3;"
sgudz905ec692018-10-16 15:30:39 +0300285 " openstack flavor set m1.extra_tiny_test"
286 " --property hw:mem_page_size=any;"
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000287 " openstack flavor set m1.tiny_test"
sgudz905ec692018-10-16 15:30:39 +0300288 " --property hw:mem_page_size=any'")},
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000289 )
Oleksii Butenko5cd0a162018-06-14 18:18:10 +0300290
obutenko3e44b602018-11-13 12:50:22 +0200291 if self.barbican:
292 commands.append({
293 'description': "Configure barbican",
294 'node_name': self.master_name,
295 'cmd': ("set -ex;" +
296 salt_call_cmd +
297 " state.sls barbican.client && " +
298 salt_call_cmd +
299 " state.sls runtest.test_accounts && " +
300 salt_call_cmd +
301 " state.sls runtest.barbican_sign_image")},
302 )
303
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000304 self.__salt_api.execute_commands(commands=commands,
305 label="Prepare for Tempest")
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300306
307 def run_tempest(self, timeout=600):
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000308 tgt = self.target_name
309 image_nameversion = "{}:{}".format(self.image_name, self.image_version)
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300310
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000311 docker_args = (
Oleksii Butenkob0ca4cc2018-10-22 16:29:54 +0300312 " -t "
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000313 " --name {container_name} "
314 " -e ARGS=\"-r {tempest_pattern} -w {tempest_threads}\""
315 " -v {cfg_dir}/tempest.conf:/etc/tempest/tempest.conf"
316 " -v /tmp/:/tmp/"
317 " -v {cfg_dir}:/root/tempest"
318 " -v /etc/ssl/certs/:/etc/ssl/certs/"
Oleksii Butenkob0ca4cc2018-10-22 16:29:54 +0300319 " -d "
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000320 " {image_nameversion} {run_cmd}"
321 .format(
322 container_name=self.container_name,
323 image_nameversion=image_nameversion,
324 tempest_pattern=self.tempest_pattern,
325 tempest_threads=self.tempest_threads,
326 cfg_dir=TEMPEST_CFG_DIR,
327 run_cmd=self.run_cmd,
328 ))
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300329
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000330 commands = [
331 {
332 'description': "Run Tempest tests {0}".format(
333 image_nameversion),
334 'node_name': self.target_name,
335 'cmd': ("set -ex;" +
336 " docker rm --force {container_name} || true;"
337 " docker run {docker_args}"
338 .format(container_name=self.container_name,
339 docker_args=docker_args)),
340 'timeout': timeout},
341 ]
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300342
Dennis Dmitrievb6bcc5c2018-09-26 11:07:53 +0000343 self.__salt_api.execute_commands(commands=commands,
344 label="Run Tempest tests")
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300345
Oleksii Butenkob0ca4cc2018-10-22 16:29:54 +0300346 def wait_status(s):
347 inspect_res = self.salt_api.local(tgt,
348 'dockerng.inspect',
349 self.container_name)
350 if 'return' in inspect_res:
351 inspect = inspect_res['return']
352 inspect = inspect[0]
353 inspect = next(inspect.iteritems())[1]
354 status = inspect['State']['Status']
355
356 return status.lower() == s.lower()
357
358 return False
359
360 helpers.wait(lambda: wait_status('exited'),
361 timeout=timeout,
362 timeout_msg=('Tempest run didnt finished '
363 'in {}'.format(timeout)))
364
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300365 inspect_res = self.salt_api.local(tgt,
366 'dockerng.inspect',
367 self.container_name)
368 inspect = inspect_res['return'][0]
369 inspect = next(inspect.iteritems())[1]
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300370 logs_res = self.salt_api.local(tgt,
371 'dockerng.logs',
372 self.container_name)
373 logs = logs_res['return'][0]
374 logs = next(logs.iteritems())[1]
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300375
376 res = self.salt_api.local(tgt, 'dockerng.rm', self.container_name)
377 LOG.info("Tempest container was removed".format(
378 json.dumps(res, indent=4)))
379
380 return {'inspect': inspect,
381 'logs': logs}
Oleksii Butenkoe82441d2018-06-12 16:01:33 +0300382
Tatyana Leontovichdb185db2018-11-30 12:46:40 +0200383 def prepare_and_run_tempest(self, username='root',
384 store_run_test_model=True):
Oleksii Butenkoe82441d2018-06-12 16:01:33 +0300385 """
386 Run tempest tests
387 """
388 tempest_timeout = settings.TEMPEST_TIMEOUT
Tatyana Leontovichdb185db2018-11-30 12:46:40 +0200389 self.prepare(store_run_test_model=store_run_test_model)
Oleksii Butenkoe82441d2018-06-12 16:01:33 +0300390 test_res = self.run_tempest(tempest_timeout)
391 self.fetch_arficats(username=username)
392 self.save_runtime_logs(**test_res)