blob: 18488035444d3a3880d2828e8cd81bc009e29962 [file] [log] [blame]
Steve Baker450aa7f2014-08-25 10:37:27 +12001# Licensed under the Apache License, Version 2.0 (the "License"); you may
2# not use this file except in compliance with the License. You may obtain
3# a copy of the License at
4#
5# http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10# License for the specific language governing permissions and limitations
11# under the License.
12
Steve Baker450aa7f2014-08-25 10:37:27 +120013import os
14import random
15import re
Steve Baker450aa7f2014-08-25 10:37:27 +120016import subprocess
Steve Baker450aa7f2014-08-25 10:37:27 +120017import time
18
Pavlo Shchelokovskyy60e0ecd2014-12-14 22:17:21 +020019import fixtures
Steve Baker450aa7f2014-08-25 10:37:27 +120020from heatclient import exc as heat_exceptions
Mark Vanderwiel6d8e0862015-10-15 12:51:07 -050021from neutronclient.common import exceptions as network_exceptions
Steve Baker24641292015-03-13 10:47:50 +130022from oslo_log import log as logging
Jens Rosenboom4f069fb2015-02-18 14:19:07 +010023from oslo_utils import timeutils
Pavlo Shchelokovskyy60e0ecd2014-12-14 22:17:21 +020024import six
Sirushti Murugesan4920fda2015-04-22 00:35:26 +053025from six.moves import urllib
Pavlo Shchelokovskyy60e0ecd2014-12-14 22:17:21 +020026import testscenarios
27import testtools
Steve Baker450aa7f2014-08-25 10:37:27 +120028
Steve Baker450aa7f2014-08-25 10:37:27 +120029from heat_integrationtests.common import clients
30from heat_integrationtests.common import config
31from heat_integrationtests.common import exceptions
32from heat_integrationtests.common import remote_client
33
34LOG = logging.getLogger(__name__)
Angus Salkeld24043702014-11-21 08:49:26 +100035_LOG_FORMAT = "%(levelname)8s [%(name)s] %(message)s"
Steve Baker450aa7f2014-08-25 10:37:27 +120036
37
Angus Salkeld08514ad2015-02-06 10:08:31 +100038def call_until_true(duration, sleep_for, func, *args, **kwargs):
Peter Razumovskyf0ac9582015-09-24 16:49:03 +030039 """Call the function until it returns True or the duration elapsed.
40
Steve Baker450aa7f2014-08-25 10:37:27 +120041 Call the given function until it returns True (and return True) or
42 until the specified duration (in seconds) elapses (and return
43 False).
44
45 :param func: A zero argument callable that returns True on success.
46 :param duration: The number of seconds for which to attempt a
47 successful call of the function.
48 :param sleep_for: The number of seconds to sleep after an unsuccessful
49 invocation of the function.
50 """
51 now = time.time()
52 timeout = now + duration
53 while now < timeout:
Angus Salkeld08514ad2015-02-06 10:08:31 +100054 if func(*args, **kwargs):
Steve Baker450aa7f2014-08-25 10:37:27 +120055 return True
56 LOG.debug("Sleeping for %d seconds", sleep_for)
57 time.sleep(sleep_for)
58 now = time.time()
59 return False
60
61
62def rand_name(name=''):
63 randbits = str(random.randint(1, 0x7fffffff))
64 if name:
65 return name + '-' + randbits
66 else:
67 return randbits
68
69
Angus Salkeld95f65a22014-11-24 12:38:30 +100070class HeatIntegrationTest(testscenarios.WithScenarios,
71 testtools.TestCase):
Steve Baker450aa7f2014-08-25 10:37:27 +120072
73 def setUp(self):
74 super(HeatIntegrationTest, self).setUp()
75
76 self.conf = config.init_conf()
77
78 self.assertIsNotNone(self.conf.auth_url,
79 'No auth_url configured')
80 self.assertIsNotNone(self.conf.username,
81 'No username configured')
82 self.assertIsNotNone(self.conf.password,
83 'No password configured')
84
85 self.manager = clients.ClientManager(self.conf)
86 self.identity_client = self.manager.identity_client
87 self.orchestration_client = self.manager.orchestration_client
88 self.compute_client = self.manager.compute_client
89 self.network_client = self.manager.network_client
90 self.volume_client = self.manager.volume_client
Angus Salkeld4408da32015-02-03 18:53:30 +100091 self.object_client = self.manager.object_client
Angus Salkeld406bbd52015-05-13 14:24:04 +100092 self.metering_client = self.manager.metering_client
Angus Salkeld24043702014-11-21 08:49:26 +100093 self.useFixture(fixtures.FakeLogger(format=_LOG_FORMAT))
Sirushti Murugesan13a8a172015-04-14 00:30:05 +053094 self.updated_time = {}
Steve Baker450aa7f2014-08-25 10:37:27 +120095
Steve Baker450aa7f2014-08-25 10:37:27 +120096 def get_remote_client(self, server_or_ip, username, private_key=None):
97 if isinstance(server_or_ip, six.string_types):
98 ip = server_or_ip
99 else:
100 network_name_for_ssh = self.conf.network_for_ssh
101 ip = server_or_ip.networks[network_name_for_ssh][0]
102 if private_key is None:
103 private_key = self.keypair.private_key
104 linux_client = remote_client.RemoteClient(ip, username,
105 pkey=private_key,
106 conf=self.conf)
107 try:
108 linux_client.validate_authentication()
109 except exceptions.SSHTimeout:
110 LOG.exception('ssh connection to %s failed' % ip)
111 raise
112
113 return linux_client
114
Anastasia Kuznetsova3e0ab4d2015-03-06 18:10:13 +0400115 def check_connectivity(self, check_ip):
116 def try_connect(ip):
117 try:
Sirushti Murugesan4920fda2015-04-22 00:35:26 +0530118 urllib.request.urlopen('http://%s/' % ip)
Anastasia Kuznetsova3e0ab4d2015-03-06 18:10:13 +0400119 return True
120 except IOError:
121 return False
122
123 timeout = self.conf.connectivity_timeout
124 elapsed_time = 0
125 while not try_connect(check_ip):
126 time.sleep(10)
127 elapsed_time += 10
128 if elapsed_time > timeout:
129 raise exceptions.TimeoutException()
130
Steve Baker450aa7f2014-08-25 10:37:27 +1200131 def _log_console_output(self, servers=None):
132 if not servers:
133 servers = self.compute_client.servers.list()
134 for server in servers:
Steve Baker24641292015-03-13 10:47:50 +1300135 LOG.info('Console output for %s', server.id)
136 LOG.info(server.get_console_output())
Steve Baker450aa7f2014-08-25 10:37:27 +1200137
Sergey Kraynevd6fa5c02015-02-13 03:03:55 -0500138 def _load_template(self, base_file, file_name, sub_dir=None):
139 sub_dir = sub_dir or ''
Steve Baker450aa7f2014-08-25 10:37:27 +1200140 filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
Sergey Kraynevd6fa5c02015-02-13 03:03:55 -0500141 sub_dir, file_name)
Steve Baker450aa7f2014-08-25 10:37:27 +1200142 with open(filepath) as f:
143 return f.read()
144
145 def create_keypair(self, client=None, name=None):
146 if client is None:
147 client = self.compute_client
148 if name is None:
149 name = rand_name('heat-keypair')
150 keypair = client.keypairs.create(name)
151 self.assertEqual(keypair.name, name)
152
153 def delete_keypair():
154 keypair.delete()
155
156 self.addCleanup(delete_keypair)
157 return keypair
158
Sergey Krayneva265c132015-02-13 03:51:03 -0500159 def assign_keypair(self):
160 if self.conf.keypair_name:
161 self.keypair = None
162 self.keypair_name = self.conf.keypair_name
163 else:
164 self.keypair = self.create_keypair()
165 self.keypair_name = self.keypair.id
166
Steve Baker450aa7f2014-08-25 10:37:27 +1200167 @classmethod
168 def _stack_rand_name(cls):
169 return rand_name(cls.__name__)
170
Anastasia Kuznetsova673fc432015-03-12 16:41:36 +0400171 def _get_network(self, net_name=None):
172 if net_name is None:
173 net_name = self.conf.fixed_network_name
Steve Baker450aa7f2014-08-25 10:37:27 +1200174 networks = self.network_client.list_networks()
175 for net in networks['networks']:
Anastasia Kuznetsova673fc432015-03-12 16:41:36 +0400176 if net['name'] == net_name:
Steve Baker450aa7f2014-08-25 10:37:27 +1200177 return net
178
Mark Vanderwiel6d8e0862015-10-15 12:51:07 -0500179 def is_network_extension_supported(self, extension_alias):
180 try:
181 self.network_client.show_extension(extension_alias)
182 except network_exceptions.NeutronClientException:
183 return False
184 return True
185
Steve Baker450aa7f2014-08-25 10:37:27 +1200186 @staticmethod
Angus Salkelda89a0282015-07-24 15:47:38 +1000187 def _stack_output(stack, output_key, validate_errors=True):
Steve Baker450aa7f2014-08-25 10:37:27 +1200188 """Return a stack output value for a given key."""
Angus Salkelda89a0282015-07-24 15:47:38 +1000189 value = None
190 for o in stack.outputs:
191 if validate_errors and 'output_error' in o:
192 # scan for errors in the stack output.
193 raise ValueError(
194 'Unexpected output errors in %s : %s' % (
195 output_key, o['output_error']))
196 if o['output_key'] == output_key:
197 value = o['output_value']
198 return value
Steve Baker450aa7f2014-08-25 10:37:27 +1200199
200 def _ping_ip_address(self, ip_address, should_succeed=True):
201 cmd = ['ping', '-c1', '-w1', ip_address]
202
203 def ping():
204 proc = subprocess.Popen(cmd,
205 stdout=subprocess.PIPE,
206 stderr=subprocess.PIPE)
207 proc.wait()
208 return (proc.returncode == 0) == should_succeed
209
210 return call_until_true(
Angus Salkeld08514ad2015-02-06 10:08:31 +1000211 self.conf.build_timeout, 1, ping)
Steve Baker450aa7f2014-08-25 10:37:27 +1200212
Angus Salkelda7500d12015-04-10 15:44:07 +1000213 def _wait_for_all_resource_status(self, stack_identifier,
214 status, failure_pattern='^.*_FAILED$',
215 success_on_not_found=False):
216 for res in self.client.resources.list(stack_identifier):
217 self._wait_for_resource_status(
218 stack_identifier, res.resource_name,
219 status, failure_pattern=failure_pattern,
220 success_on_not_found=success_on_not_found)
221
Steve Baker450aa7f2014-08-25 10:37:27 +1200222 def _wait_for_resource_status(self, stack_identifier, resource_name,
223 status, failure_pattern='^.*_FAILED$',
224 success_on_not_found=False):
225 """Waits for a Resource to reach a given status."""
226 fail_regexp = re.compile(failure_pattern)
227 build_timeout = self.conf.build_timeout
228 build_interval = self.conf.build_interval
229
230 start = timeutils.utcnow()
231 while timeutils.delta_seconds(start,
232 timeutils.utcnow()) < build_timeout:
233 try:
234 res = self.client.resources.get(
235 stack_identifier, resource_name)
236 except heat_exceptions.HTTPNotFound:
237 if success_on_not_found:
238 return
239 # ignore this, as the resource may not have
240 # been created yet
241 else:
242 if res.resource_status == status:
243 return
Sirushti Murugesane5ad25b2015-04-18 23:30:59 +0530244 wait_for_action = status.split('_')[0]
245 resource_action = res.resource_status.split('_')[0]
246 if (resource_action == wait_for_action and
247 fail_regexp.search(res.resource_status)):
Steve Baker450aa7f2014-08-25 10:37:27 +1200248 raise exceptions.StackResourceBuildErrorException(
249 resource_name=res.resource_name,
250 stack_identifier=stack_identifier,
251 resource_status=res.resource_status,
252 resource_status_reason=res.resource_status_reason)
253 time.sleep(build_interval)
254
255 message = ('Resource %s failed to reach %s status within '
256 'the required time (%s s).' %
Anastasia Kuznetsova9a745572015-03-04 13:19:15 +0400257 (resource_name, status, build_timeout))
Steve Baker450aa7f2014-08-25 10:37:27 +1200258 raise exceptions.TimeoutException(message)
259
Sirushti Murugesan13a8a172015-04-14 00:30:05 +0530260 def _verify_status(self, stack, stack_identifier, status, fail_regexp):
261 if stack.stack_status == status:
Sergey Kraynev89082a32015-09-04 04:42:33 -0400262 # Handle UPDATE_COMPLETE/FAILED case: Make sure we don't
263 # wait for a stale UPDATE_COMPLETE/FAILED status.
264 if status in ('UPDATE_FAILED', 'UPDATE_COMPLETE'):
Sirushti Murugesan13a8a172015-04-14 00:30:05 +0530265 if self.updated_time.get(
266 stack_identifier) != stack.updated_time:
267 self.updated_time[stack_identifier] = stack.updated_time
268 return True
269 else:
270 return True
271
Sirushti Murugesane5ad25b2015-04-18 23:30:59 +0530272 wait_for_action = status.split('_')[0]
273 if (stack.action == wait_for_action and
274 fail_regexp.search(stack.stack_status)):
Sergey Kraynev89082a32015-09-04 04:42:33 -0400275 # Handle UPDATE_COMPLETE/UPDATE_FAILED case.
276 if status in ('UPDATE_FAILED', 'UPDATE_COMPLETE'):
Sirushti Murugesan13a8a172015-04-14 00:30:05 +0530277 if self.updated_time.get(
278 stack_identifier) != stack.updated_time:
279 self.updated_time[stack_identifier] = stack.updated_time
280 raise exceptions.StackBuildErrorException(
281 stack_identifier=stack_identifier,
282 stack_status=stack.stack_status,
283 stack_status_reason=stack.stack_status_reason)
284 else:
285 raise exceptions.StackBuildErrorException(
286 stack_identifier=stack_identifier,
287 stack_status=stack.stack_status,
288 stack_status_reason=stack.stack_status_reason)
289
Steve Baker450aa7f2014-08-25 10:37:27 +1200290 def _wait_for_stack_status(self, stack_identifier, status,
Sergey Kraynev89082a32015-09-04 04:42:33 -0400291 failure_pattern=None,
Steve Baker450aa7f2014-08-25 10:37:27 +1200292 success_on_not_found=False):
Peter Razumovskyf0ac9582015-09-24 16:49:03 +0300293 """Waits for a Stack to reach a given status.
Steve Baker450aa7f2014-08-25 10:37:27 +1200294
295 Note this compares the full $action_$status, e.g
296 CREATE_COMPLETE, not just COMPLETE which is exposed
297 via the status property of Stack in heatclient
298 """
Sergey Kraynev89082a32015-09-04 04:42:33 -0400299 if failure_pattern:
300 fail_regexp = re.compile(failure_pattern)
301 elif 'FAILED' in status:
302 # If we're looking for e.g CREATE_FAILED, COMPLETE is unexpected.
303 fail_regexp = re.compile('^.*_COMPLETE$')
304 else:
305 fail_regexp = re.compile('^.*_FAILED$')
Steve Baker450aa7f2014-08-25 10:37:27 +1200306 build_timeout = self.conf.build_timeout
307 build_interval = self.conf.build_interval
308
309 start = timeutils.utcnow()
310 while timeutils.delta_seconds(start,
311 timeutils.utcnow()) < build_timeout:
312 try:
Sergey Kraynevf07f4712016-02-15 05:24:17 -0500313 stack = self.client.stacks.get(stack_identifier,
314 resolve_outputs=False)
Steve Baker450aa7f2014-08-25 10:37:27 +1200315 except heat_exceptions.HTTPNotFound:
316 if success_on_not_found:
317 return
318 # ignore this, as the resource may not have
319 # been created yet
320 else:
Sirushti Murugesan13a8a172015-04-14 00:30:05 +0530321 if self._verify_status(stack, stack_identifier, status,
322 fail_regexp):
Steve Baker450aa7f2014-08-25 10:37:27 +1200323 return
Sirushti Murugesan13a8a172015-04-14 00:30:05 +0530324
Steve Baker450aa7f2014-08-25 10:37:27 +1200325 time.sleep(build_interval)
326
327 message = ('Stack %s failed to reach %s status within '
328 'the required time (%s s).' %
Anastasia Kuznetsova9a745572015-03-04 13:19:15 +0400329 (stack_identifier, status, build_timeout))
Steve Baker450aa7f2014-08-25 10:37:27 +1200330 raise exceptions.TimeoutException(message)
331
332 def _stack_delete(self, stack_identifier):
333 try:
Thomas Herve3eab2942015-10-22 17:29:21 +0200334 self._handle_in_progress(self.client.stacks.delete,
335 stack_identifier)
Steve Baker450aa7f2014-08-25 10:37:27 +1200336 except heat_exceptions.HTTPNotFound:
337 pass
338 self._wait_for_stack_status(
339 stack_identifier, 'DELETE_COMPLETE',
340 success_on_not_found=True)
Steven Hardyc9efd972014-11-20 11:31:55 +0000341
Thomas Herve3eab2942015-10-22 17:29:21 +0200342 def _handle_in_progress(self, fn, *args, **kwargs):
343 build_timeout = self.conf.build_timeout
344 build_interval = self.conf.build_interval
345 start = timeutils.utcnow()
346 while timeutils.delta_seconds(start,
347 timeutils.utcnow()) < build_timeout:
348 try:
349 fn(*args, **kwargs)
350 except heat_exceptions.HTTPConflict as ex:
351 # FIXME(sirushtim): Wait a little for the stack lock to be
352 # released and hopefully, the stack should be usable again.
353 if ex.error['error']['type'] != 'ActionInProgress':
354 raise ex
355
356 time.sleep(build_interval)
357 else:
358 break
359
Steven Hardy23284b62015-10-01 19:03:42 +0100360 def update_stack(self, stack_identifier, template=None, environment=None,
Sabeen Syed277ea692015-02-04 23:30:02 +0000361 files=None, parameters=None, tags=None,
Rakesh H Sa3325d62015-04-04 19:42:29 +0530362 expected_status='UPDATE_COMPLETE',
Steven Hardy23284b62015-10-01 19:03:42 +0100363 disable_rollback=True,
364 existing=False):
Steven Hardyc9efd972014-11-20 11:31:55 +0000365 env = environment or {}
366 env_files = files or {}
Sergey Kraynevbcc78df2015-02-27 04:34:32 -0500367 parameters = parameters or {}
Steven Hardyc9efd972014-11-20 11:31:55 +0000368 stack_name = stack_identifier.split('/')[0]
Sirushti Murugesan3a195a52015-05-01 09:47:09 +0530369
Sergey Kraynev89082a32015-09-04 04:42:33 -0400370 self.updated_time[stack_identifier] = self.client.stacks.get(
Sergey Kraynevf07f4712016-02-15 05:24:17 -0500371 stack_identifier, resolve_outputs=False).updated_time
Sirushti Murugesan3a195a52015-05-01 09:47:09 +0530372
Thomas Herve3eab2942015-10-22 17:29:21 +0200373 self._handle_in_progress(
374 self.client.stacks.update,
375 stack_id=stack_identifier,
376 stack_name=stack_name,
377 template=template,
378 files=env_files,
379 disable_rollback=disable_rollback,
380 parameters=parameters,
381 environment=env,
382 tags=tags,
383 existing=existing)
Sirushti Murugesan3a195a52015-05-01 09:47:09 +0530384
Rakesh H Sa3325d62015-04-04 19:42:29 +0530385 kwargs = {'stack_identifier': stack_identifier,
386 'status': expected_status}
387 if expected_status in ['ROLLBACK_COMPLETE']:
Rakesh H Sa3325d62015-04-04 19:42:29 +0530388 # To trigger rollback you would intentionally fail the stack
389 # Hence check for rollback failures
390 kwargs['failure_pattern'] = '^ROLLBACK_FAILED$'
391
392 self._wait_for_stack_status(**kwargs)
Steven Hardyc9efd972014-11-20 11:31:55 +0000393
Jason Dunsmoreb5aa9022015-09-09 16:57:04 -0500394 def preview_update_stack(self, stack_identifier, template,
395 environment=None, files=None, parameters=None,
Steven Hardye6de2d62015-12-07 15:59:09 +0000396 tags=None, disable_rollback=True,
397 show_nested=False):
Jason Dunsmoreb5aa9022015-09-09 16:57:04 -0500398 env = environment or {}
399 env_files = files or {}
400 parameters = parameters or {}
401 stack_name = stack_identifier.split('/')[0]
402
403 return self.client.stacks.preview_update(
404 stack_id=stack_identifier,
405 stack_name=stack_name,
406 template=template,
407 files=env_files,
408 disable_rollback=disable_rollback,
409 parameters=parameters,
410 environment=env,
Steven Hardye6de2d62015-12-07 15:59:09 +0000411 tags=tags,
412 show_nested=show_nested
Jason Dunsmoreb5aa9022015-09-09 16:57:04 -0500413 )
414
Steven Hardy03da0742015-03-19 00:13:17 -0400415 def assert_resource_is_a_stack(self, stack_identifier, res_name,
416 wait=False):
417 build_timeout = self.conf.build_timeout
418 build_interval = self.conf.build_interval
419 start = timeutils.utcnow()
420 while timeutils.delta_seconds(start,
421 timeutils.utcnow()) < build_timeout:
422 time.sleep(build_interval)
423 try:
424 nested_identifier = self._get_nested_identifier(
425 stack_identifier, res_name)
426 except Exception:
427 # We may have to wait, if the create is in-progress
428 if wait:
429 time.sleep(build_interval)
430 else:
431 raise
432 else:
433 return nested_identifier
434
435 def _get_nested_identifier(self, stack_identifier, res_name):
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000436 rsrc = self.client.resources.get(stack_identifier, res_name)
437 nested_link = [l for l in rsrc.links if l['rel'] == 'nested']
438 nested_href = nested_link[0]['href']
439 nested_id = nested_href.split('/')[-1]
440 nested_identifier = '/'.join(nested_href.split('/')[-2:])
441 self.assertEqual(rsrc.physical_resource_id, nested_id)
442
Sergey Kraynevf07f4712016-02-15 05:24:17 -0500443 nested_stack = self.client.stacks.get(nested_id, resolve_outputs=False)
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000444 nested_identifier2 = '%s/%s' % (nested_stack.stack_name,
445 nested_stack.id)
446 self.assertEqual(nested_identifier, nested_identifier2)
447 parent_id = stack_identifier.split("/")[-1]
448 self.assertEqual(parent_id, nested_stack.parent)
449 return nested_identifier
450
Rabi Mishra8bcff8a2015-09-21 18:15:04 +0530451 def group_nested_identifier(self, stack_identifier,
452 group_name):
453 # Get the nested stack identifier from a group resource
454 rsrc = self.client.resources.get(stack_identifier, group_name)
455 physical_resource_id = rsrc.physical_resource_id
456
Sergey Kraynevf07f4712016-02-15 05:24:17 -0500457 nested_stack = self.client.stacks.get(physical_resource_id,
458 resolve_outputs=False)
Rabi Mishra8bcff8a2015-09-21 18:15:04 +0530459 nested_identifier = '%s/%s' % (nested_stack.stack_name,
460 nested_stack.id)
461 parent_id = stack_identifier.split("/")[-1]
462 self.assertEqual(parent_id, nested_stack.parent)
463 return nested_identifier
464
465 def list_group_resources(self, stack_identifier,
466 group_name, minimal=True):
467 nested_identifier = self.group_nested_identifier(stack_identifier,
468 group_name)
469 if minimal:
470 return self.list_resources(nested_identifier)
471 return self.client.resources.list(nested_identifier)
472
Steven Hardyc9efd972014-11-20 11:31:55 +0000473 def list_resources(self, stack_identifier):
474 resources = self.client.resources.list(stack_identifier)
475 return dict((r.resource_name, r.resource_type) for r in resources)
Steven Hardyf2c82c02014-11-20 14:02:17 +0000476
477 def stack_create(self, stack_name=None, template=None, files=None,
Sabeen Syed277ea692015-02-04 23:30:02 +0000478 parameters=None, environment=None, tags=None,
479 expected_status='CREATE_COMPLETE',
480 disable_rollback=True, enable_cleanup=True):
Steven Hardyf2c82c02014-11-20 14:02:17 +0000481 name = stack_name or self._stack_rand_name()
482 templ = template or self.template
483 templ_files = files or {}
484 params = parameters or {}
485 env = environment or {}
486 self.client.stacks.create(
487 stack_name=name,
488 template=templ,
489 files=templ_files,
Rakesh H Sa3325d62015-04-04 19:42:29 +0530490 disable_rollback=disable_rollback,
Steven Hardyf2c82c02014-11-20 14:02:17 +0000491 parameters=params,
Sabeen Syed277ea692015-02-04 23:30:02 +0000492 environment=env,
493 tags=tags
Steven Hardyf2c82c02014-11-20 14:02:17 +0000494 )
Sergey Kraynevbf67ce32015-04-17 10:54:20 -0400495 if expected_status not in ['ROLLBACK_COMPLETE'] and enable_cleanup:
Steve Bakerdbea6ab2015-08-19 13:37:08 +1200496 self.addCleanup(self._stack_delete, name)
Steven Hardyf2c82c02014-11-20 14:02:17 +0000497
Sergey Kraynevf07f4712016-02-15 05:24:17 -0500498 stack = self.client.stacks.get(name, resolve_outputs=False)
Steven Hardyf2c82c02014-11-20 14:02:17 +0000499 stack_identifier = '%s/%s' % (name, stack.id)
Rakesh H Sa3325d62015-04-04 19:42:29 +0530500 kwargs = {'stack_identifier': stack_identifier,
501 'status': expected_status}
Steve Bakerf6c8f122015-02-10 13:54:46 +1300502 if expected_status:
Rakesh H Sa3325d62015-04-04 19:42:29 +0530503 if expected_status in ['ROLLBACK_COMPLETE']:
504 # To trigger rollback you would intentionally fail the stack
505 # Hence check for rollback failures
506 kwargs['failure_pattern'] = '^ROLLBACK_FAILED$'
507 self._wait_for_stack_status(**kwargs)
Steven Hardyf2c82c02014-11-20 14:02:17 +0000508 return stack_identifier
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000509
510 def stack_adopt(self, stack_name=None, files=None,
511 parameters=None, environment=None, adopt_data=None,
512 wait_for_status='ADOPT_COMPLETE'):
Rabi Mishra477efc92015-07-31 13:01:45 +0530513 if (self.conf.skip_test_stack_action_list and
514 'ADOPT' in self.conf.skip_test_stack_action_list):
Sirushti Murugesan04ee8022015-02-02 23:00:23 +0530515 self.skipTest('Testing Stack adopt disabled in conf, skipping')
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000516 name = stack_name or self._stack_rand_name()
517 templ_files = files or {}
518 params = parameters or {}
519 env = environment or {}
520 self.client.stacks.create(
521 stack_name=name,
522 files=templ_files,
523 disable_rollback=True,
524 parameters=params,
525 environment=env,
526 adopt_stack_data=adopt_data,
527 )
Steve Bakerdbea6ab2015-08-19 13:37:08 +1200528 self.addCleanup(self._stack_delete, name)
Sergey Kraynevf07f4712016-02-15 05:24:17 -0500529 stack = self.client.stacks.get(name, resolve_outputs=False)
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000530 stack_identifier = '%s/%s' % (name, stack.id)
531 self._wait_for_stack_status(stack_identifier, wait_for_status)
532 return stack_identifier
Sirushti Murugesan04ee8022015-02-02 23:00:23 +0530533
534 def stack_abandon(self, stack_id):
Rabi Mishra477efc92015-07-31 13:01:45 +0530535 if (self.conf.skip_test_stack_action_list and
536 'ABANDON' in self.conf.skip_test_stack_action_list):
Steve Bakerdbea6ab2015-08-19 13:37:08 +1200537 self.addCleanup(self._stack_delete, stack_id)
Sirushti Murugesan04ee8022015-02-02 23:00:23 +0530538 self.skipTest('Testing Stack abandon disabled in conf, skipping')
539 info = self.client.stacks.abandon(stack_id=stack_id)
540 return info
Oleksii Chuprykovd9cd9dc2015-02-03 10:34:55 -0500541
542 def stack_suspend(self, stack_identifier):
Rabi Mishra477efc92015-07-31 13:01:45 +0530543 if (self.conf.skip_test_stack_action_list and
544 'SUSPEND' in self.conf.skip_test_stack_action_list):
Steve Bakerdbea6ab2015-08-19 13:37:08 +1200545 self.addCleanup(self._stack_delete, stack_identifier)
Rabi Mishra477efc92015-07-31 13:01:45 +0530546 self.skipTest('Testing Stack suspend disabled in conf, skipping')
Rabi Mishra287ffff2015-08-10 09:52:35 +0530547 stack_name = stack_identifier.split('/')[0]
Thomas Herve3eab2942015-10-22 17:29:21 +0200548 self._handle_in_progress(self.client.actions.suspend, stack_name)
Angus Salkelda7500d12015-04-10 15:44:07 +1000549 # improve debugging by first checking the resource's state.
550 self._wait_for_all_resource_status(stack_identifier,
551 'SUSPEND_COMPLETE')
Oleksii Chuprykovd9cd9dc2015-02-03 10:34:55 -0500552 self._wait_for_stack_status(stack_identifier, 'SUSPEND_COMPLETE')
553
554 def stack_resume(self, stack_identifier):
Rabi Mishra477efc92015-07-31 13:01:45 +0530555 if (self.conf.skip_test_stack_action_list and
556 'RESUME' in self.conf.skip_test_stack_action_list):
Steve Bakerdbea6ab2015-08-19 13:37:08 +1200557 self.addCleanup(self._stack_delete, stack_identifier)
Rabi Mishra477efc92015-07-31 13:01:45 +0530558 self.skipTest('Testing Stack resume disabled in conf, skipping')
Rabi Mishra287ffff2015-08-10 09:52:35 +0530559 stack_name = stack_identifier.split('/')[0]
Thomas Herve3eab2942015-10-22 17:29:21 +0200560 self._handle_in_progress(self.client.actions.resume, stack_name)
Angus Salkelda7500d12015-04-10 15:44:07 +1000561 # improve debugging by first checking the resource's state.
562 self._wait_for_all_resource_status(stack_identifier,
563 'RESUME_COMPLETE')
Oleksii Chuprykovd9cd9dc2015-02-03 10:34:55 -0500564 self._wait_for_stack_status(stack_identifier, 'RESUME_COMPLETE')
Steven Hardy03da0742015-03-19 00:13:17 -0400565
566 def wait_for_event_with_reason(self, stack_identifier, reason,
567 rsrc_name=None, num_expected=1):
568 build_timeout = self.conf.build_timeout
569 build_interval = self.conf.build_interval
570 start = timeutils.utcnow()
571 while timeutils.delta_seconds(start,
572 timeutils.utcnow()) < build_timeout:
573 try:
574 rsrc_events = self.client.events.list(stack_identifier,
575 resource_name=rsrc_name)
576 except heat_exceptions.HTTPNotFound:
577 LOG.debug("No events yet found for %s" % rsrc_name)
578 else:
579 matched = [e for e in rsrc_events
580 if e.resource_status_reason == reason]
581 if len(matched) == num_expected:
582 return matched
583 time.sleep(build_interval)