blob: 4f3d923ea88811a10c45fdbb7578fb58be1642b4 [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
Steve Baker24641292015-03-13 10:47:50 +130021from oslo_log import log as logging
Jens Rosenboom4f069fb2015-02-18 14:19:07 +010022from oslo_utils import timeutils
Pavlo Shchelokovskyy60e0ecd2014-12-14 22:17:21 +020023import six
Sirushti Murugesan4920fda2015-04-22 00:35:26 +053024from six.moves import urllib
Pavlo Shchelokovskyy60e0ecd2014-12-14 22:17:21 +020025import testscenarios
26import testtools
Steve Baker450aa7f2014-08-25 10:37:27 +120027
Steve Baker450aa7f2014-08-25 10:37:27 +120028from heat_integrationtests.common import clients
29from heat_integrationtests.common import config
30from heat_integrationtests.common import exceptions
31from heat_integrationtests.common import remote_client
32
33LOG = logging.getLogger(__name__)
Angus Salkeld24043702014-11-21 08:49:26 +100034_LOG_FORMAT = "%(levelname)8s [%(name)s] %(message)s"
Steve Baker450aa7f2014-08-25 10:37:27 +120035
36
Angus Salkeld08514ad2015-02-06 10:08:31 +100037def call_until_true(duration, sleep_for, func, *args, **kwargs):
Steve Baker450aa7f2014-08-25 10:37:27 +120038 """
39 Call the given function until it returns True (and return True) or
40 until the specified duration (in seconds) elapses (and return
41 False).
42
43 :param func: A zero argument callable that returns True on success.
44 :param duration: The number of seconds for which to attempt a
45 successful call of the function.
46 :param sleep_for: The number of seconds to sleep after an unsuccessful
47 invocation of the function.
48 """
49 now = time.time()
50 timeout = now + duration
51 while now < timeout:
Angus Salkeld08514ad2015-02-06 10:08:31 +100052 if func(*args, **kwargs):
Steve Baker450aa7f2014-08-25 10:37:27 +120053 return True
54 LOG.debug("Sleeping for %d seconds", sleep_for)
55 time.sleep(sleep_for)
56 now = time.time()
57 return False
58
59
60def rand_name(name=''):
61 randbits = str(random.randint(1, 0x7fffffff))
62 if name:
63 return name + '-' + randbits
64 else:
65 return randbits
66
67
Angus Salkeld95f65a22014-11-24 12:38:30 +100068class HeatIntegrationTest(testscenarios.WithScenarios,
69 testtools.TestCase):
Steve Baker450aa7f2014-08-25 10:37:27 +120070
71 def setUp(self):
72 super(HeatIntegrationTest, self).setUp()
73
74 self.conf = config.init_conf()
75
76 self.assertIsNotNone(self.conf.auth_url,
77 'No auth_url configured')
78 self.assertIsNotNone(self.conf.username,
79 'No username configured')
80 self.assertIsNotNone(self.conf.password,
81 'No password configured')
82
83 self.manager = clients.ClientManager(self.conf)
84 self.identity_client = self.manager.identity_client
85 self.orchestration_client = self.manager.orchestration_client
86 self.compute_client = self.manager.compute_client
87 self.network_client = self.manager.network_client
88 self.volume_client = self.manager.volume_client
Angus Salkeld4408da32015-02-03 18:53:30 +100089 self.object_client = self.manager.object_client
Angus Salkeld406bbd52015-05-13 14:24:04 +100090 self.metering_client = self.manager.metering_client
Angus Salkeld24043702014-11-21 08:49:26 +100091 self.useFixture(fixtures.FakeLogger(format=_LOG_FORMAT))
Sirushti Murugesan13a8a172015-04-14 00:30:05 +053092 self.updated_time = {}
Steve Baker450aa7f2014-08-25 10:37:27 +120093
Steve Baker450aa7f2014-08-25 10:37:27 +120094 def get_remote_client(self, server_or_ip, username, private_key=None):
95 if isinstance(server_or_ip, six.string_types):
96 ip = server_or_ip
97 else:
98 network_name_for_ssh = self.conf.network_for_ssh
99 ip = server_or_ip.networks[network_name_for_ssh][0]
100 if private_key is None:
101 private_key = self.keypair.private_key
102 linux_client = remote_client.RemoteClient(ip, username,
103 pkey=private_key,
104 conf=self.conf)
105 try:
106 linux_client.validate_authentication()
107 except exceptions.SSHTimeout:
108 LOG.exception('ssh connection to %s failed' % ip)
109 raise
110
111 return linux_client
112
Anastasia Kuznetsova3e0ab4d2015-03-06 18:10:13 +0400113 def check_connectivity(self, check_ip):
114 def try_connect(ip):
115 try:
Sirushti Murugesan4920fda2015-04-22 00:35:26 +0530116 urllib.request.urlopen('http://%s/' % ip)
Anastasia Kuznetsova3e0ab4d2015-03-06 18:10:13 +0400117 return True
118 except IOError:
119 return False
120
121 timeout = self.conf.connectivity_timeout
122 elapsed_time = 0
123 while not try_connect(check_ip):
124 time.sleep(10)
125 elapsed_time += 10
126 if elapsed_time > timeout:
127 raise exceptions.TimeoutException()
128
Steve Baker450aa7f2014-08-25 10:37:27 +1200129 def _log_console_output(self, servers=None):
130 if not servers:
131 servers = self.compute_client.servers.list()
132 for server in servers:
Steve Baker24641292015-03-13 10:47:50 +1300133 LOG.info('Console output for %s', server.id)
134 LOG.info(server.get_console_output())
Steve Baker450aa7f2014-08-25 10:37:27 +1200135
Sergey Kraynevd6fa5c02015-02-13 03:03:55 -0500136 def _load_template(self, base_file, file_name, sub_dir=None):
137 sub_dir = sub_dir or ''
Steve Baker450aa7f2014-08-25 10:37:27 +1200138 filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
Sergey Kraynevd6fa5c02015-02-13 03:03:55 -0500139 sub_dir, file_name)
Steve Baker450aa7f2014-08-25 10:37:27 +1200140 with open(filepath) as f:
141 return f.read()
142
143 def create_keypair(self, client=None, name=None):
144 if client is None:
145 client = self.compute_client
146 if name is None:
147 name = rand_name('heat-keypair')
148 keypair = client.keypairs.create(name)
149 self.assertEqual(keypair.name, name)
150
151 def delete_keypair():
152 keypair.delete()
153
154 self.addCleanup(delete_keypair)
155 return keypair
156
Sergey Krayneva265c132015-02-13 03:51:03 -0500157 def assign_keypair(self):
158 if self.conf.keypair_name:
159 self.keypair = None
160 self.keypair_name = self.conf.keypair_name
161 else:
162 self.keypair = self.create_keypair()
163 self.keypair_name = self.keypair.id
164
Steve Baker450aa7f2014-08-25 10:37:27 +1200165 @classmethod
166 def _stack_rand_name(cls):
167 return rand_name(cls.__name__)
168
Anastasia Kuznetsova673fc432015-03-12 16:41:36 +0400169 def _get_network(self, net_name=None):
170 if net_name is None:
171 net_name = self.conf.fixed_network_name
Steve Baker450aa7f2014-08-25 10:37:27 +1200172 networks = self.network_client.list_networks()
173 for net in networks['networks']:
Anastasia Kuznetsova673fc432015-03-12 16:41:36 +0400174 if net['name'] == net_name:
Steve Baker450aa7f2014-08-25 10:37:27 +1200175 return net
176
177 @staticmethod
Angus Salkelda89a0282015-07-24 15:47:38 +1000178 def _stack_output(stack, output_key, validate_errors=True):
Steve Baker450aa7f2014-08-25 10:37:27 +1200179 """Return a stack output value for a given key."""
Angus Salkelda89a0282015-07-24 15:47:38 +1000180 value = None
181 for o in stack.outputs:
182 if validate_errors and 'output_error' in o:
183 # scan for errors in the stack output.
184 raise ValueError(
185 'Unexpected output errors in %s : %s' % (
186 output_key, o['output_error']))
187 if o['output_key'] == output_key:
188 value = o['output_value']
189 return value
Steve Baker450aa7f2014-08-25 10:37:27 +1200190
191 def _ping_ip_address(self, ip_address, should_succeed=True):
192 cmd = ['ping', '-c1', '-w1', ip_address]
193
194 def ping():
195 proc = subprocess.Popen(cmd,
196 stdout=subprocess.PIPE,
197 stderr=subprocess.PIPE)
198 proc.wait()
199 return (proc.returncode == 0) == should_succeed
200
201 return call_until_true(
Angus Salkeld08514ad2015-02-06 10:08:31 +1000202 self.conf.build_timeout, 1, ping)
Steve Baker450aa7f2014-08-25 10:37:27 +1200203
Angus Salkelda7500d12015-04-10 15:44:07 +1000204 def _wait_for_all_resource_status(self, stack_identifier,
205 status, failure_pattern='^.*_FAILED$',
206 success_on_not_found=False):
207 for res in self.client.resources.list(stack_identifier):
208 self._wait_for_resource_status(
209 stack_identifier, res.resource_name,
210 status, failure_pattern=failure_pattern,
211 success_on_not_found=success_on_not_found)
212
Steve Baker450aa7f2014-08-25 10:37:27 +1200213 def _wait_for_resource_status(self, stack_identifier, resource_name,
214 status, failure_pattern='^.*_FAILED$',
215 success_on_not_found=False):
216 """Waits for a Resource to reach a given status."""
217 fail_regexp = re.compile(failure_pattern)
218 build_timeout = self.conf.build_timeout
219 build_interval = self.conf.build_interval
220
221 start = timeutils.utcnow()
222 while timeutils.delta_seconds(start,
223 timeutils.utcnow()) < build_timeout:
224 try:
225 res = self.client.resources.get(
226 stack_identifier, resource_name)
227 except heat_exceptions.HTTPNotFound:
228 if success_on_not_found:
229 return
230 # ignore this, as the resource may not have
231 # been created yet
232 else:
233 if res.resource_status == status:
234 return
Sirushti Murugesane5ad25b2015-04-18 23:30:59 +0530235 wait_for_action = status.split('_')[0]
236 resource_action = res.resource_status.split('_')[0]
237 if (resource_action == wait_for_action and
238 fail_regexp.search(res.resource_status)):
Steve Baker450aa7f2014-08-25 10:37:27 +1200239 raise exceptions.StackResourceBuildErrorException(
240 resource_name=res.resource_name,
241 stack_identifier=stack_identifier,
242 resource_status=res.resource_status,
243 resource_status_reason=res.resource_status_reason)
244 time.sleep(build_interval)
245
246 message = ('Resource %s failed to reach %s status within '
247 'the required time (%s s).' %
Anastasia Kuznetsova9a745572015-03-04 13:19:15 +0400248 (resource_name, status, build_timeout))
Steve Baker450aa7f2014-08-25 10:37:27 +1200249 raise exceptions.TimeoutException(message)
250
Sirushti Murugesan13a8a172015-04-14 00:30:05 +0530251 def _verify_status(self, stack, stack_identifier, status, fail_regexp):
252 if stack.stack_status == status:
Sergey Kraynev89082a32015-09-04 04:42:33 -0400253 # Handle UPDATE_COMPLETE/FAILED case: Make sure we don't
254 # wait for a stale UPDATE_COMPLETE/FAILED status.
255 if status in ('UPDATE_FAILED', 'UPDATE_COMPLETE'):
Sirushti Murugesan13a8a172015-04-14 00:30:05 +0530256 if self.updated_time.get(
257 stack_identifier) != stack.updated_time:
258 self.updated_time[stack_identifier] = stack.updated_time
259 return True
260 else:
261 return True
262
Sirushti Murugesane5ad25b2015-04-18 23:30:59 +0530263 wait_for_action = status.split('_')[0]
264 if (stack.action == wait_for_action and
265 fail_regexp.search(stack.stack_status)):
Sergey Kraynev89082a32015-09-04 04:42:33 -0400266 # Handle UPDATE_COMPLETE/UPDATE_FAILED case.
267 if status in ('UPDATE_FAILED', 'UPDATE_COMPLETE'):
Sirushti Murugesan13a8a172015-04-14 00:30:05 +0530268 if self.updated_time.get(
269 stack_identifier) != stack.updated_time:
270 self.updated_time[stack_identifier] = stack.updated_time
271 raise exceptions.StackBuildErrorException(
272 stack_identifier=stack_identifier,
273 stack_status=stack.stack_status,
274 stack_status_reason=stack.stack_status_reason)
275 else:
276 raise exceptions.StackBuildErrorException(
277 stack_identifier=stack_identifier,
278 stack_status=stack.stack_status,
279 stack_status_reason=stack.stack_status_reason)
280
Steve Baker450aa7f2014-08-25 10:37:27 +1200281 def _wait_for_stack_status(self, stack_identifier, status,
Sergey Kraynev89082a32015-09-04 04:42:33 -0400282 failure_pattern=None,
Steve Baker450aa7f2014-08-25 10:37:27 +1200283 success_on_not_found=False):
284 """
285 Waits for a Stack to reach a given status.
286
287 Note this compares the full $action_$status, e.g
288 CREATE_COMPLETE, not just COMPLETE which is exposed
289 via the status property of Stack in heatclient
290 """
Sergey Kraynev89082a32015-09-04 04:42:33 -0400291 if failure_pattern:
292 fail_regexp = re.compile(failure_pattern)
293 elif 'FAILED' in status:
294 # If we're looking for e.g CREATE_FAILED, COMPLETE is unexpected.
295 fail_regexp = re.compile('^.*_COMPLETE$')
296 else:
297 fail_regexp = re.compile('^.*_FAILED$')
Steve Baker450aa7f2014-08-25 10:37:27 +1200298 build_timeout = self.conf.build_timeout
299 build_interval = self.conf.build_interval
300
301 start = timeutils.utcnow()
302 while timeutils.delta_seconds(start,
303 timeutils.utcnow()) < build_timeout:
304 try:
305 stack = self.client.stacks.get(stack_identifier)
306 except heat_exceptions.HTTPNotFound:
307 if success_on_not_found:
308 return
309 # ignore this, as the resource may not have
310 # been created yet
311 else:
Sirushti Murugesan13a8a172015-04-14 00:30:05 +0530312 if self._verify_status(stack, stack_identifier, status,
313 fail_regexp):
Steve Baker450aa7f2014-08-25 10:37:27 +1200314 return
Sirushti Murugesan13a8a172015-04-14 00:30:05 +0530315
Steve Baker450aa7f2014-08-25 10:37:27 +1200316 time.sleep(build_interval)
317
318 message = ('Stack %s failed to reach %s status within '
319 'the required time (%s s).' %
Anastasia Kuznetsova9a745572015-03-04 13:19:15 +0400320 (stack_identifier, status, build_timeout))
Steve Baker450aa7f2014-08-25 10:37:27 +1200321 raise exceptions.TimeoutException(message)
322
323 def _stack_delete(self, stack_identifier):
324 try:
325 self.client.stacks.delete(stack_identifier)
326 except heat_exceptions.HTTPNotFound:
327 pass
328 self._wait_for_stack_status(
329 stack_identifier, 'DELETE_COMPLETE',
330 success_on_not_found=True)
Steven Hardyc9efd972014-11-20 11:31:55 +0000331
Steven Hardy23284b62015-10-01 19:03:42 +0100332 def update_stack(self, stack_identifier, template=None, environment=None,
Sabeen Syed277ea692015-02-04 23:30:02 +0000333 files=None, parameters=None, tags=None,
Rakesh H Sa3325d62015-04-04 19:42:29 +0530334 expected_status='UPDATE_COMPLETE',
Steven Hardy23284b62015-10-01 19:03:42 +0100335 disable_rollback=True,
336 existing=False):
Steven Hardyc9efd972014-11-20 11:31:55 +0000337 env = environment or {}
338 env_files = files or {}
Sergey Kraynevbcc78df2015-02-27 04:34:32 -0500339 parameters = parameters or {}
Steven Hardyc9efd972014-11-20 11:31:55 +0000340 stack_name = stack_identifier.split('/')[0]
Sirushti Murugesan3a195a52015-05-01 09:47:09 +0530341
342 build_timeout = self.conf.build_timeout
343 build_interval = self.conf.build_interval
344 start = timeutils.utcnow()
Sergey Kraynev89082a32015-09-04 04:42:33 -0400345 self.updated_time[stack_identifier] = self.client.stacks.get(
346 stack_identifier).updated_time
Sirushti Murugesan3a195a52015-05-01 09:47:09 +0530347 while timeutils.delta_seconds(start,
348 timeutils.utcnow()) < build_timeout:
349 try:
350 self.client.stacks.update(
351 stack_id=stack_identifier,
352 stack_name=stack_name,
353 template=template,
354 files=env_files,
355 disable_rollback=disable_rollback,
356 parameters=parameters,
Sabeen Syed277ea692015-02-04 23:30:02 +0000357 environment=env,
Steven Hardy23284b62015-10-01 19:03:42 +0100358 tags=tags,
359 existing=existing
Sirushti Murugesan3a195a52015-05-01 09:47:09 +0530360 )
361 except heat_exceptions.HTTPConflict as ex:
362 # FIXME(sirushtim): Wait a little for the stack lock to be
363 # released and hopefully, the stack should be updatable again.
364 if ex.error['error']['type'] != 'ActionInProgress':
365 raise ex
366
367 time.sleep(build_interval)
368 else:
369 break
370
Rakesh H Sa3325d62015-04-04 19:42:29 +0530371 kwargs = {'stack_identifier': stack_identifier,
372 'status': expected_status}
373 if expected_status in ['ROLLBACK_COMPLETE']:
Rakesh H Sa3325d62015-04-04 19:42:29 +0530374 # To trigger rollback you would intentionally fail the stack
375 # Hence check for rollback failures
376 kwargs['failure_pattern'] = '^ROLLBACK_FAILED$'
377
378 self._wait_for_stack_status(**kwargs)
Steven Hardyc9efd972014-11-20 11:31:55 +0000379
Jason Dunsmoreb5aa9022015-09-09 16:57:04 -0500380 def preview_update_stack(self, stack_identifier, template,
381 environment=None, files=None, parameters=None,
382 tags=None, disable_rollback=True):
383 env = environment or {}
384 env_files = files or {}
385 parameters = parameters or {}
386 stack_name = stack_identifier.split('/')[0]
387
388 return self.client.stacks.preview_update(
389 stack_id=stack_identifier,
390 stack_name=stack_name,
391 template=template,
392 files=env_files,
393 disable_rollback=disable_rollback,
394 parameters=parameters,
395 environment=env,
396 tags=tags
397 )
398
Steven Hardy03da0742015-03-19 00:13:17 -0400399 def assert_resource_is_a_stack(self, stack_identifier, res_name,
400 wait=False):
401 build_timeout = self.conf.build_timeout
402 build_interval = self.conf.build_interval
403 start = timeutils.utcnow()
404 while timeutils.delta_seconds(start,
405 timeutils.utcnow()) < build_timeout:
406 time.sleep(build_interval)
407 try:
408 nested_identifier = self._get_nested_identifier(
409 stack_identifier, res_name)
410 except Exception:
411 # We may have to wait, if the create is in-progress
412 if wait:
413 time.sleep(build_interval)
414 else:
415 raise
416 else:
417 return nested_identifier
418
419 def _get_nested_identifier(self, stack_identifier, res_name):
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000420 rsrc = self.client.resources.get(stack_identifier, res_name)
421 nested_link = [l for l in rsrc.links if l['rel'] == 'nested']
422 nested_href = nested_link[0]['href']
423 nested_id = nested_href.split('/')[-1]
424 nested_identifier = '/'.join(nested_href.split('/')[-2:])
425 self.assertEqual(rsrc.physical_resource_id, nested_id)
426
427 nested_stack = self.client.stacks.get(nested_id)
428 nested_identifier2 = '%s/%s' % (nested_stack.stack_name,
429 nested_stack.id)
430 self.assertEqual(nested_identifier, nested_identifier2)
431 parent_id = stack_identifier.split("/")[-1]
432 self.assertEqual(parent_id, nested_stack.parent)
433 return nested_identifier
434
Steven Hardyc9efd972014-11-20 11:31:55 +0000435 def list_resources(self, stack_identifier):
436 resources = self.client.resources.list(stack_identifier)
437 return dict((r.resource_name, r.resource_type) for r in resources)
Steven Hardyf2c82c02014-11-20 14:02:17 +0000438
439 def stack_create(self, stack_name=None, template=None, files=None,
Sabeen Syed277ea692015-02-04 23:30:02 +0000440 parameters=None, environment=None, tags=None,
441 expected_status='CREATE_COMPLETE',
442 disable_rollback=True, enable_cleanup=True):
Steven Hardyf2c82c02014-11-20 14:02:17 +0000443 name = stack_name or self._stack_rand_name()
444 templ = template or self.template
445 templ_files = files or {}
446 params = parameters or {}
447 env = environment or {}
448 self.client.stacks.create(
449 stack_name=name,
450 template=templ,
451 files=templ_files,
Rakesh H Sa3325d62015-04-04 19:42:29 +0530452 disable_rollback=disable_rollback,
Steven Hardyf2c82c02014-11-20 14:02:17 +0000453 parameters=params,
Sabeen Syed277ea692015-02-04 23:30:02 +0000454 environment=env,
455 tags=tags
Steven Hardyf2c82c02014-11-20 14:02:17 +0000456 )
Sergey Kraynevbf67ce32015-04-17 10:54:20 -0400457 if expected_status not in ['ROLLBACK_COMPLETE'] and enable_cleanup:
Steve Bakerdbea6ab2015-08-19 13:37:08 +1200458 self.addCleanup(self._stack_delete, name)
Steven Hardyf2c82c02014-11-20 14:02:17 +0000459
460 stack = self.client.stacks.get(name)
461 stack_identifier = '%s/%s' % (name, stack.id)
Rakesh H Sa3325d62015-04-04 19:42:29 +0530462 kwargs = {'stack_identifier': stack_identifier,
463 'status': expected_status}
Steve Bakerf6c8f122015-02-10 13:54:46 +1300464 if expected_status:
Rakesh H Sa3325d62015-04-04 19:42:29 +0530465 if expected_status in ['ROLLBACK_COMPLETE']:
466 # To trigger rollback you would intentionally fail the stack
467 # Hence check for rollback failures
468 kwargs['failure_pattern'] = '^ROLLBACK_FAILED$'
469 self._wait_for_stack_status(**kwargs)
Steven Hardyf2c82c02014-11-20 14:02:17 +0000470 return stack_identifier
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000471
472 def stack_adopt(self, stack_name=None, files=None,
473 parameters=None, environment=None, adopt_data=None,
474 wait_for_status='ADOPT_COMPLETE'):
Rabi Mishra477efc92015-07-31 13:01:45 +0530475 if (self.conf.skip_test_stack_action_list and
476 'ADOPT' in self.conf.skip_test_stack_action_list):
Sirushti Murugesan04ee8022015-02-02 23:00:23 +0530477 self.skipTest('Testing Stack adopt disabled in conf, skipping')
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000478 name = stack_name or self._stack_rand_name()
479 templ_files = files or {}
480 params = parameters or {}
481 env = environment or {}
482 self.client.stacks.create(
483 stack_name=name,
484 files=templ_files,
485 disable_rollback=True,
486 parameters=params,
487 environment=env,
488 adopt_stack_data=adopt_data,
489 )
Steve Bakerdbea6ab2015-08-19 13:37:08 +1200490 self.addCleanup(self._stack_delete, name)
Angus Salkeld2bd63a42015-01-07 11:11:29 +1000491
492 stack = self.client.stacks.get(name)
493 stack_identifier = '%s/%s' % (name, stack.id)
494 self._wait_for_stack_status(stack_identifier, wait_for_status)
495 return stack_identifier
Sirushti Murugesan04ee8022015-02-02 23:00:23 +0530496
497 def stack_abandon(self, stack_id):
Rabi Mishra477efc92015-07-31 13:01:45 +0530498 if (self.conf.skip_test_stack_action_list and
499 'ABANDON' in self.conf.skip_test_stack_action_list):
Steve Bakerdbea6ab2015-08-19 13:37:08 +1200500 self.addCleanup(self._stack_delete, stack_id)
Sirushti Murugesan04ee8022015-02-02 23:00:23 +0530501 self.skipTest('Testing Stack abandon disabled in conf, skipping')
502 info = self.client.stacks.abandon(stack_id=stack_id)
503 return info
Oleksii Chuprykovd9cd9dc2015-02-03 10:34:55 -0500504
505 def stack_suspend(self, stack_identifier):
Rabi Mishra477efc92015-07-31 13:01:45 +0530506 if (self.conf.skip_test_stack_action_list and
507 'SUSPEND' in self.conf.skip_test_stack_action_list):
Steve Bakerdbea6ab2015-08-19 13:37:08 +1200508 self.addCleanup(self._stack_delete, stack_identifier)
Rabi Mishra477efc92015-07-31 13:01:45 +0530509 self.skipTest('Testing Stack suspend disabled in conf, skipping')
Rabi Mishra287ffff2015-08-10 09:52:35 +0530510 stack_name = stack_identifier.split('/')[0]
511 self.client.actions.suspend(stack_name)
Angus Salkelda7500d12015-04-10 15:44:07 +1000512 # improve debugging by first checking the resource's state.
513 self._wait_for_all_resource_status(stack_identifier,
514 'SUSPEND_COMPLETE')
Oleksii Chuprykovd9cd9dc2015-02-03 10:34:55 -0500515 self._wait_for_stack_status(stack_identifier, 'SUSPEND_COMPLETE')
516
517 def stack_resume(self, stack_identifier):
Rabi Mishra477efc92015-07-31 13:01:45 +0530518 if (self.conf.skip_test_stack_action_list and
519 'RESUME' in self.conf.skip_test_stack_action_list):
Steve Bakerdbea6ab2015-08-19 13:37:08 +1200520 self.addCleanup(self._stack_delete, stack_identifier)
Rabi Mishra477efc92015-07-31 13:01:45 +0530521 self.skipTest('Testing Stack resume disabled in conf, skipping')
Rabi Mishra287ffff2015-08-10 09:52:35 +0530522 stack_name = stack_identifier.split('/')[0]
523 self.client.actions.resume(stack_name)
Angus Salkelda7500d12015-04-10 15:44:07 +1000524 # improve debugging by first checking the resource's state.
525 self._wait_for_all_resource_status(stack_identifier,
526 'RESUME_COMPLETE')
Oleksii Chuprykovd9cd9dc2015-02-03 10:34:55 -0500527 self._wait_for_stack_status(stack_identifier, 'RESUME_COMPLETE')
Steven Hardy03da0742015-03-19 00:13:17 -0400528
529 def wait_for_event_with_reason(self, stack_identifier, reason,
530 rsrc_name=None, num_expected=1):
531 build_timeout = self.conf.build_timeout
532 build_interval = self.conf.build_interval
533 start = timeutils.utcnow()
534 while timeutils.delta_seconds(start,
535 timeutils.utcnow()) < build_timeout:
536 try:
537 rsrc_events = self.client.events.list(stack_identifier,
538 resource_name=rsrc_name)
539 except heat_exceptions.HTTPNotFound:
540 LOG.debug("No events yet found for %s" % rsrc_name)
541 else:
542 matched = [e for e in rsrc_events
543 if e.resource_status_reason == reason]
544 if len(matched) == num_expected:
545 return matched
546 time.sleep(build_interval)