blob: c400556444f329907b869b8a1bd01bc4b9d0f925 [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
Dennis Dmitriev6d52a452018-09-26 11:06:32 +000017import time
18
19from devops.helpers import helpers
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030020
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
Dennis Dmitriev6d52a452018-09-26 11:06:32 +000028CONFIG = {
29 'classes': ['service.runtest.tempest',
30 'service.runtest.tempest.services.manila.glance'],
31 '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': {
83 'capability_snapshot_support': True,
84 'run_driver_assisted_migration_tests': False,
85 'run_manage_unmanage_snapshot_tests': False,
86 'run_manage_unmanage_tests': False,
87 'run_migration_with_preserve_snapshots_tests': False,
88 'run_quota_tests': True,
89 'run_replication_tests': False,
90 'run_snapshot_tests': True,
91 }}}}}
92
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +030093
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"
Dennis Dmitriev6d52a452018-09-26 11:06:32 +0000101 master_tgt = "{}*".format(master_host)
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300102 class_name = "runtest"
103 run_cmd = '/bin/bash -c "run-tempest"'
104
Dennis Dmitriev6d52a452018-09-26 11:06:32 +0000105 def __init__(self, underlay, salt_api, cluster_name,
Oleksii Butenko74566102018-09-11 11:55:13 +0300106 domain_name, tempest_threads,
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300107 tempest_pattern=settings.TEMPEST_PATTERN,
108 run_cmd=None, target='gtw01'):
109 self.underlay = underlay
110 self.__salt_api = salt_api
Dennis Dmitriev6d52a452018-09-26 11:06:32 +0000111 self.target = target
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300112 self.cluster_name = cluster_name
113 self.domain_name = domain_name
114 self.tempest_threads = tempest_threads
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300115 self.tempest_pattern = tempest_pattern
116 self.run_cmd = run_cmd or self.run_cmd
117
118 @property
119 def salt_api(self):
120 return self.__salt_api
121
Dennis Dmitriev6d52a452018-09-26 11:06:32 +0000122 def install_python_lib(self):
123 return self.salt_api.local(
124 "{}*".format(self.target),
125 'pip.install', 'docker'), None
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300126
Dennis Dmitriev6d52a452018-09-26 11:06:32 +0000127 def run_salt_minion_state(self):
128 return self.salt_api.local('cfg01*', 'state.sls', 'salt.minion')
129
130 def create_networks(self):
131 return self.salt_api.local('cfg01*', 'state.sls', 'neutron.client')
132
133 def create_flavors(self):
134 return self.salt_api.local('cfg01*', 'state.sls', 'nova.client')
135
136 def set_property(self):
137 return self.salt_api.local(
138 tgt='ctl01*',
139 fun='cmd.run',
140 args='. /root/keystonercv3; openstack '
141 'flavor set m1.tiny_test '
142 '--property hw:mem_page_size=small')
143
144 def create_cirros(self):
145 return self.salt_api.local('cfg01*', 'state.sls', 'glance.client')
146
147 def generate_config(self):
148 return self.salt_api.local('cfg01*', 'state.sls', 'runtest')
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300149
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300150 def fetch_arficats(self, username=None, file_format='xml'):
Dennis Dmitriev6d52a452018-09-26 11:06:32 +0000151 target_name = next(node_name for node_name
152 in self.underlay.node_names() if
153 self.target in node_name)
154 with self.underlay.remote(node_name=target_name, username=None) as tgt:
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300155 result = tgt.execute('find {} -name "report_*.{}"'.format(
156 TEMPEST_CFG_DIR, file_format))
157 LOG.debug("Find result {0}".format(result))
158 assert len(result['stdout']) > 0, ('No report found, please check'
159 ' if test run was successful.')
160 report = result['stdout'][0].rstrip()
161 LOG.debug("Found files {0}".format(report))
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300162 tgt.download(
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300163 destination=report, # noqa
164 target=os.getcwd())
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300165
Dennis Dmitriev6d52a452018-09-26 11:06:32 +0000166 def store_runtest_model(self, config=CONFIG):
167 master_name = next(node_name for node_name
168 in self.underlay.node_names() if
169 self.master_host in node_name)
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300170 with self.underlay.yaml_editor(
171 file_path="/srv/salt/reclass/classes/cluster/"
172 "{cluster_name}/infra/"
173 "{class_name}.yml".format(
174 cluster_name=self.cluster_name,
175 class_name=self.class_name),
Dennis Dmitriev6d52a452018-09-26 11:06:32 +0000176 node_name=master_name) as editor:
177 editor.content = config
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300178 with self.underlay.yaml_editor(
179 file_path="/srv/salt/reclass/nodes/_generated/"
180 "cfg01.{domain_name}.yml".format(
181 domain_name=self.domain_name),
Dennis Dmitriev6d52a452018-09-26 11:06:32 +0000182 node_name=master_name) as editor:
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300183 editor.content['classes'].append(
184 'cluster.{cluster_name}.infra.{class_name}'.format(
185 cluster_name=self.cluster_name,
186 class_name=self.class_name))
187
Dennis Dmitriev6d52a452018-09-26 11:06:32 +0000188 self.salt_api.local('*', 'saltutil.refresh_pillar')
189 self.salt_api.local('*', 'saltutil.sync_all')
190
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300191 def save_runtime_logs(self, logs=None, inspect=None):
192 if logs:
193 with open("{path}/{target}_tempest_run.log".format(
Dennis Dmitriev6d52a452018-09-26 11:06:32 +0000194 path=settings.LOGS_DIR, target=self.target), 'w') as f:
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300195 LOG.info("Save tempest console log")
196 container_log = logs
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300197 f.write(container_log.encode('ascii', 'ignore'))
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300198
199 if inspect:
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300200 with open("{path}/{target}_tempest_container_info.json.log".format(
Dennis Dmitriev6d52a452018-09-26 11:06:32 +0000201 path=settings.LOGS_DIR, target=self.target), 'w') as f:
Oleksii Butenko71d76f32018-06-05 17:46:34 +0300202 LOG.info("Save tempest container inspect data")
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300203
204 container_inspect = json.dumps(inspect,
205 indent=4, sort_keys=True)
206 f.write(container_inspect)
207
Tatyana Leontovich61a821d2018-08-21 12:30:51 +0300208 def prepare(self, dpdk=None):
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300209 self.store_runtest_model()
Oleksii Butenko5cd0a162018-06-14 18:18:10 +0300210
Dennis Dmitriev6d52a452018-09-26 11:06:32 +0000211 res = self.install_python_lib()
212 LOG.info(json.dumps(res, indent=4))
Oleksii Butenko5cd0a162018-06-14 18:18:10 +0300213
Dennis Dmitriev6d52a452018-09-26 11:06:32 +0000214 res = self.run_salt_minion_state()
215 LOG.info(json.dumps(res, indent=4))
216 time.sleep(20)
217
218 res = self.create_networks()
219 LOG.info(json.dumps(res, indent=4))
220 time.sleep(20)
221
222 res = self.create_flavors()
223 LOG.info(json.dumps(res, indent=4))
224 time.sleep(20)
Tatyana Leontovich61a821d2018-08-21 12:30:51 +0300225 if dpdk:
Dennis Dmitriev6d52a452018-09-26 11:06:32 +0000226 res = self.set_property()
227 LOG.info('Update flavor property')
228 LOG.info(json.dumps(res, indent=4))
229 time.sleep(20)
Oleksii Butenko5cd0a162018-06-14 18:18:10 +0300230
Dennis Dmitriev6d52a452018-09-26 11:06:32 +0000231 res = self.create_cirros()
232 LOG.info(json.dumps(res, indent=4))
233 time.sleep(20)
234
235 res = self.generate_config()
236 LOG.info(json.dumps(res, indent=4))
237 time.sleep(20)
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300238
239 def run_tempest(self, timeout=600):
Dennis Dmitriev6d52a452018-09-26 11:06:32 +0000240 tgt = "{}*".format(self.target)
241 params = {
242 "name": self.container_name,
243 "image": "{}:{}".format(self.image_name, self.image_version),
244 "environment": {
245 "ARGS": "-r {tempest_pattern} -w "
246 "{tempest_threads} ".format(
247 tempest_pattern=self.tempest_pattern,
248 tempest_threads=self.tempest_threads) # noqa
249 },
250 "binds": [
251 "{cfg_dir}/tempest.conf:/etc/tempest/tempest.conf".format(cfg_dir=TEMPEST_CFG_DIR), # noqa
252 "/tmp/:/tmp/",
253 "{cfg_dir}:/root/tempest".format(cfg_dir=TEMPEST_CFG_DIR),
254 "/etc/ssl/certs/:/etc/ssl/certs/"
255 ],
256 "auto_remove": False,
257 "cmd": self.run_cmd
258 }
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300259
Dennis Dmitriev6d52a452018-09-26 11:06:32 +0000260 res = self.salt_api.local(tgt, 'dockerng.pull', "{}:{}".format(
261 self.image_name, self.image_version))
262 LOG.info("Tempest image has beed pulled- \n{}".format(
263 json.dumps(res, indent=4)))
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300264
Dennis Dmitriev6d52a452018-09-26 11:06:32 +0000265 res = self.salt_api.local(tgt, 'dockerng.create', kwargs=params)
266 LOG.info("Tempest container has been created - \n{}".format(
267 json.dumps(res, indent=4)))
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300268
Dennis Dmitriev6d52a452018-09-26 11:06:32 +0000269 res = self.salt_api.local(tgt, 'dockerng.start', self.container_name)
270 LOG.info("Tempest container has been started - \n{}".format(
271 json.dumps(res, indent=4)))
272
273 def wait_status(s):
274 inspect_res = self.salt_api.local(tgt,
275 'dockerng.inspect',
276 self.container_name)
277 if 'return' in inspect_res:
278 inspect = inspect_res['return']
279 inspect = inspect[0]
280 inspect = next(inspect.iteritems())[1]
281 status = inspect['State']['Status']
282
283 return status.lower() == s.lower()
284
285 return False
286
287 helpers.wait(lambda: wait_status('exited'),
288 timeout=timeout,
289 timeout_msg=('Tempest run didnt finished '
290 'in {}'.format(timeout)))
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300291
292 inspect_res = self.salt_api.local(tgt,
293 'dockerng.inspect',
294 self.container_name)
295 inspect = inspect_res['return'][0]
296 inspect = next(inspect.iteritems())[1]
Dennis Dmitriev6d52a452018-09-26 11:06:32 +0000297 if inspect['State']['ExitCode'] != 0:
298 LOG.error("Tempest running failed")
299 LOG.info("Tempest tests have been finished - \n{}".format(
300 json.dumps(res, indent=4)))
301
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300302 logs_res = self.salt_api.local(tgt,
303 'dockerng.logs',
304 self.container_name)
305 logs = logs_res['return'][0]
306 logs = next(logs.iteritems())[1]
Dennis Dmitriev6d52a452018-09-26 11:06:32 +0000307 LOG.info("Tempest result - \n{}".format(
308 logs.encode('ascii', 'ignore')))
Dmitry Tyzhnenkoc56b77e2018-05-21 11:01:43 +0300309
310 res = self.salt_api.local(tgt, 'dockerng.rm', self.container_name)
311 LOG.info("Tempest container was removed".format(
312 json.dumps(res, indent=4)))
313
314 return {'inspect': inspect,
315 'logs': logs}
Oleksii Butenkoe82441d2018-06-12 16:01:33 +0300316
Tatyana Leontovich61a821d2018-08-21 12:30:51 +0300317 def prepare_and_run_tempest(self, username='root', dpdk=None):
Oleksii Butenkoe82441d2018-06-12 16:01:33 +0300318 """
319 Run tempest tests
320 """
321 tempest_timeout = settings.TEMPEST_TIMEOUT
Tatyana Leontovich61a821d2018-08-21 12:30:51 +0300322 self.prepare(dpdk=dpdk)
Oleksii Butenkoe82441d2018-06-12 16:01:33 +0300323 test_res = self.run_tempest(tempest_timeout)
324 self.fetch_arficats(username=username)
325 self.save_runtime_logs(**test_res)