blob: 9ed4a9c79f7caebf126f9b5f7cac9f61d2a331e4 [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
15import os
16import json
17
18from devops.helpers import helpers
19
20from tcp_tests import logger
21from tcp_tests import settings
22
23
24LOG = logger.logger
25
26TEMPEST_CFG_DIR = '/tmp/test'
27
28CONFIG = {
29 'classes': ['service.runtest.tempest'],
30 'parameters': {
31 '_param': {
32 'runtest_tempest_cfg_dir': TEMPEST_CFG_DIR,
33 'runtest_tempest_cfg_name': 'tempest.conf',
34 'runtest_tempest_public_net': 'net04_ext',
35 'tempest_test_target': 'gtw01*'
36 },
37 'neutron': {
38 'client': {
39 'enabled': True
40 }
41 },
42 'runtest': {
43 'enabled': True,
44 'keystonerc_node': 'ctl01*',
45 'tempest': {
46 'enabled': True,
47 'cfg_dir': '${_param:runtest_tempest_cfg_dir}',
48 'cfg_name': '${_param:runtest_tempest_cfg_name}',
49 'DEFAULT': {
50 'log_file': 'tempest.log'
51 },
52 'compute': {
53 'build_timeout': 600,
54 'max_microversion': 2.53,
55 'min_compute_nodes': 2,
56 'min_microversion': 2.1,
57 'volume_device_name': 'vdc'
58 },
59 'convert_to_uuid': {
60 'network': {
61 'public_network_id':
62 '${_param:runtest_tempest_public_net}'
63 }
64 },
65 'dns_feature_enabled': {
66 'api_admin': False,
67 'api_v1': False,
68 'api_v2': True,
69 'api_v2_quotas': True,
70 'api_v2_root_recordsets': True,
71 'bug_1573141_fixed': True
72 },
73 'heat_plugin': {
74 'floating_network_name':
75 '${_param:runtest_tempest_public_net}'
76 },
77 'network': {
78 'floating_network_name':
79 '${_param:runtest_tempest_public_net}'
80 },
81 'share': {
82 'backend_names': 'lvm',
83 'capability_create_share_from_snapshot_support': True,
84 'capability_snapshot_support': True,
85 'default_share_type_name': 'default',
86 'enable_ip_rules_for_protocols': 'nfs',
87 'enable_user_rules_for_protocols': 'cifs',
88 'max_api_microversion': 2.4,
89 'min_api_microversion': 2.0,
90 'run_driver_assisted_migration_tests': False,
91 'run_host_assisted_migration_tests': True,
92 'run_manage_unmanage_snapshot_tests': False,
93 'run_manage_unmanage_tests': False,
94 'run_migration_with_preserve_snapshots_tests': False,
95 'run_mount_snapshot_tests': True,
96 'run_quota_tests': True,
97 'run_replication_tests': False,
98 'run_revert_to_snapshot_tests': True,
99 'run_share_group_tests': False,
100 'run_shrink_tests': False,
101 'run_snapshot_tests': True,
102 'share_creation_retry_number': 2,
103 'suppress_errors_in_cleanup': True
104 }}}}}
105
106
107class RuntestManager(object):
108 """Helper manager for execution tempest via runtest-formula"""
109
110 image_name = settings.TEMPEST_IMAGE
111 image_version = settings.TEMPEST_IMAGE_VERSION
112 container_name = 'run-tempest-ci'
113 master_host = "cfg01"
114 master_tgt = "{}*".format(master_host)
115 class_name = "runtest"
116 run_cmd = '/bin/bash -c "run-tempest"'
117
118 def __init__(self, underlay, salt_api, cluster_name, domain_name,
119 tempest_threads, tempest_exclude_test_args,
120 tempest_pattern=settings.TEMPEST_PATTERN,
121 run_cmd=None, target='gtw01'):
122 self.underlay = underlay
123 self.__salt_api = salt_api
124 self.target = target
125 self.cluster_name = cluster_name
126 self.domain_name = domain_name
127 self.tempest_threads = tempest_threads
128 self.tempest_exclude_test_args = tempest_exclude_test_args
129 self.tempest_pattern = tempest_pattern
130 self.run_cmd = run_cmd or self.run_cmd
131
132 @property
133 def salt_api(self):
134 return self.__salt_api
135
136 def install_python_lib(self):
137 return self.salt_api.local(
138 "{}*".format(self.target),
139 'pip.install', 'docker'), None
140
141 def install_formula(self):
142 return self.salt_api.local(
143 self.master_tgt,
144 'pkg.install', 'salt-formula-runtest'), None
145
146 def create_networks(self):
147 return self.salt_api.enforce_state(self.master_tgt, 'neutron.client')
148
149 def create_flavors(self):
150 return self.salt_api.enforce_state(self.master_tgt, 'nova.client')
151
152 def create_cirros(self):
153 return self.salt_api.enforce_state(self.master_tgt, 'glance.client')
154
155 def generate_config(self):
156 return self.salt_api.enforce_state(self.master_tgt, 'runtest')
157
158 def fetch_arficats(self, username=None):
159 target_name = next(node_name for node_name
160 in self.underlay.node_names() if
161 self.target in node_name)
162 with self.underlay.remote(node_name=target_name, username=None) as tgt:
163 tgt.download(
164 destination="{cfg_dir}/report_*.xml".format(cfg_dir=TEMPEST_CFG_DIR), # noqa
165 target="{}".format(os.environ.get("PWD")))
166
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
179
180 with self.underlay.yaml_editor(
181 file_path="/srv/salt/reclass/nodes/_generated/"
182 "cfg01.{domain_name}.yml".format(
183 domain_name=self.domain_name),
184 node_name=master_name) as editor:
185 editor.content['classes'].append(
186 'cluster.{cluster_name}.infra.{class_name}'.format(
187 cluster_name=self.cluster_name,
188 class_name=self.class_name))
189
190 self.salt_api.local('*', 'saltutil.refresh_pillar')
191 self.salt_api.local('*', 'saltutil.sync_all')
192
193 def save_runtime_logs(self, logs=None, inspect=None):
194 if logs:
195 with open("{path}/{target}_tempest_run.log".format(
196 path=settings.LOGS_DIR, target=self.target), 'w') as f:
197 LOG.info("Save tempest console log")
198 container_log = logs
199 f.write(container_log)
200
201 if inspect:
202 with open("{path}/{target}_tempest_container_info.json".format(
203 path=settings.LOGS_DIR, target=self.target), 'w') as f:
204 LOG.info("Save tempest containes inspect data")
205
206 container_inspect = json.dumps(inspect,
207 indent=4, sort_keys=True)
208 f.write(container_inspect)
209
210 def prepare(self):
211 self.store_runtest_model()
212 res = self.install_formula()
213 LOG.info(json.dumps(res, indent=4))
214 res = self.install_python_lib()
215 LOG.info(json.dumps(res, indent=4))
216 res = self.create_networks()
217 LOG.info(json.dumps(res, indent=4))
218 res = self.create_flavors()
219 LOG.info(json.dumps(res, indent=4))
220 res = self.create_cirros()
221 LOG.info(json.dumps(res, indent=4))
222 res = self.generate_config()
223 LOG.info(json.dumps(res, indent=4))
224
225 def run_tempest(self, timeout=600):
226 tgt = "{}*".format(self.target)
227 params = {
228 "name": self.container_name,
229 "image": self.image_name,
230 "environment": {
231 "ARGS": "-r {tempest_pattern} -w "
232 "{tempest_threads} "
233 "{tempest_exclude_test_args}".format(
234 tempest_pattern=self.tempest_pattern,
235 tempest_threads=self.tempest_threads,
236 tempest_exclude_test_args=self.tempest_exclude_test_args) # noqa
237 },
238 "binds": [
239 "{cfg_dir}/tempest.conf:/etc/tempest/tempest.conf".format(cfg_dir=TEMPEST_CFG_DIR), # noqa
240 "/tmp/:/tmp/",
241 "{cfg_dir}:/root/tempest".format(cfg_dir=TEMPEST_CFG_DIR),
242 "/etc/ssl/certs/:/etc/ssl/certs/"
243 ],
244 "auto_remove": False,
245 "cmd": self.run_cmd
246 }
247
248 res = self.salt_api.local(tgt, 'dockerng.pull', self.image_name)
249 LOG.info("Tempest image has beed pulled- \n{}".format(
250 json.dumps(res, indent=4)))
251
252 res = self.salt_api.local(tgt, 'dockerng.create', kwargs=params)
253 LOG.info("Tempest container has been created - \n{}".format(
254 json.dumps(res, indent=4)))
255
256 res = self.salt_api.local(tgt, 'dockerng.start', self.container_name)
257 LOG.info("Tempest container has been started - \n{}".format(
258 json.dumps(res, indent=4)))
259
260 def wait_status(s):
261 inspect_res = self.salt_api.local(tgt,
262 'dockerng.inspect',
263 self.container_name)
264 if 'return' in inspect_res:
265 inspect = inspect_res['return']
266 inspect = inspect[0]
267 inspect = next(inspect.iteritems())[1]
268 status = inspect['State']['Status']
269
270 return status.lower() == s.lower()
271
272 return False
273
274 helpers.wait(lambda: wait_status('exited'),
275 timeout=timeout,
276 timeout_msg=('Tempest run didnt finished '
277 'in {}'.format(timeout)))
278
279 inspect_res = self.salt_api.local(tgt,
280 'dockerng.inspect',
281 self.container_name)
282 inspect = inspect_res['return'][0]
283 inspect = next(inspect.iteritems())[1]
284 if inspect['State']['ExitCode'] != 0:
285 LOG.error("Tempest running failed")
286 LOG.info("Tempest tests have been finished - \n{}".format(
287 json.dumps(res, indent=4)))
288
289 logs_res = self.salt_api.local(tgt,
290 'dockerng.logs',
291 self.container_name)
292 logs = logs_res['return'][0]
293 logs = next(logs.iteritems())[1]
294 LOG.info("Tempest result - \n{}".format(logs))
295
296 res = self.salt_api.local(tgt, 'dockerng.rm', self.container_name)
297 LOG.info("Tempest container was removed".format(
298 json.dumps(res, indent=4)))
299
300 return {'inspect': inspect,
301 'logs': logs}