be4d6ea87748809499e2a554707e5eac2d42cc30
[snaps.git] / snaps / openstack / tests / create_stack_tests.py
1 # Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
2 #                    and others.  All rights reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 import os
16 import time
17
18 import pkg_resources
19 from heatclient.exc import HTTPBadRequest
20
21 import snaps
22 from snaps import file_utils
23 from snaps.config.flavor import FlavorConfig
24 from snaps.config.image import ImageConfig
25 from snaps.config.stack import (StackConfigError, StackConfig,
26     STATUS_UPDATE_COMPLETE)
27 from snaps.openstack.create_flavor import OpenStackFlavor
28 from snaps.openstack.create_image import OpenStackImage
29
30 try:
31     from urllib.request import URLError
32 except ImportError:
33     from urllib2 import URLError
34
35 import logging
36 import unittest
37 import uuid
38
39 from snaps.openstack import create_stack
40 from snaps.openstack.create_stack import (
41     StackSettings, StackCreationError, StackError, OpenStackHeatStack)
42 from snaps.openstack.tests import openstack_tests, create_instance_tests
43 from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase
44 from snaps.openstack.utils import (
45     heat_utils, neutron_utils, nova_utils, keystone_utils)
46
47 __author__ = 'spisarski'
48
49 logger = logging.getLogger('create_stack_tests')
50
51
52 class StackSettingsUnitTests(unittest.TestCase):
53     """
54     Tests the construction of the StackSettings class
55     """
56
57     def test_no_params(self):
58         with self.assertRaises(StackConfigError):
59             StackSettings()
60
61     def test_empty_config(self):
62         with self.assertRaises(StackConfigError):
63             StackSettings(**dict())
64
65     def test_name_only(self):
66         with self.assertRaises(StackConfigError):
67             StackSettings(name='foo')
68
69     def test_config_with_name_only(self):
70         with self.assertRaises(StackConfigError):
71             StackSettings(**{'name': 'foo'})
72
73     def test_config_minimum_template(self):
74         settings = StackSettings(**{'name': 'stack', 'template': 'foo'})
75         self.assertEqual('stack', settings.name)
76         self.assertEqual('foo', settings.template)
77         self.assertIsNone(settings.template_path)
78         self.assertIsNone(settings.env_values)
79         self.assertEqual(snaps.config.stack.STACK_COMPLETE_TIMEOUT,
80                          settings.stack_create_timeout)
81
82     def test_config_minimum_template_path(self):
83         settings = StackSettings(**{'name': 'stack', 'template_path': 'foo'})
84         self.assertEqual('stack', settings.name)
85         self.assertIsNone(settings.template)
86         self.assertEqual('foo', settings.template_path)
87         self.assertIsNone(settings.env_values)
88         self.assertEqual(snaps.config.stack.STACK_COMPLETE_TIMEOUT,
89                          settings.stack_create_timeout)
90
91     def test_minimum_template(self):
92         settings = StackSettings(name='stack', template='foo')
93         self.assertEqual('stack', settings.name)
94         self.assertEqual('foo', settings.template)
95         self.assertIsNone(settings.template_path)
96         self.assertIsNone(settings.env_values)
97         self.assertEqual(snaps.config.stack.STACK_COMPLETE_TIMEOUT,
98                          settings.stack_create_timeout)
99
100     def test_minimum_template_path(self):
101         settings = StackSettings(name='stack', template_path='foo')
102         self.assertEqual('stack', settings.name)
103         self.assertEqual('foo', settings.template_path)
104         self.assertIsNone(settings.template)
105         self.assertIsNone(settings.env_values)
106         self.assertEqual(snaps.config.stack.STACK_COMPLETE_TIMEOUT,
107                          settings.stack_create_timeout)
108
109     def test_all(self):
110         env_values = {'foo': 'bar'}
111         settings = StackSettings(name='stack', template='bar',
112                                  template_path='foo', env_values=env_values,
113                                  stack_create_timeout=999)
114         self.assertEqual('stack', settings.name)
115         self.assertEqual('bar', settings.template)
116         self.assertEqual('foo', settings.template_path)
117         self.assertEqual(env_values, settings.env_values)
118         self.assertEqual(999, settings.stack_create_timeout)
119
120     def test_config_all(self):
121         env_values = {'foo': 'bar'}
122         settings = StackSettings(
123             **{'name': 'stack', 'template': 'bar', 'template_path': 'foo',
124                'env_values': env_values, 'stack_create_timeout': 999})
125         self.assertEqual('stack', settings.name)
126         self.assertEqual('bar', settings.template)
127         self.assertEqual('foo', settings.template_path)
128         self.assertEqual(env_values, settings.env_values)
129         self.assertEqual(999, settings.stack_create_timeout)
130
131
132 class CreateStackSuccessTests(OSIntegrationTestCase):
133     """
134     Tests for the OpenStackHeatStack class defined in create_stack.py
135     """
136
137     def setUp(self):
138         self.user_roles = ['heat_stack_owner']
139
140         super(self.__class__, self).__start__()
141
142         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
143
144         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
145         self.stack_creator = None
146
147         self.image_creator = OpenStackImage(
148             self.os_creds, openstack_tests.cirros_image_settings(
149                 name=self.guid + '-image',
150                 image_metadata=self.image_metadata))
151         self.image_creator.create()
152
153         # Create Flavor
154         flavor_config = openstack_tests.get_flavor_config(
155             name=self.guid + '-flavor-name', ram=256, disk=10,
156             vcpus=1, metadata=self.flavor_metadata)
157         self.flavor_creator = OpenStackFlavor(
158             self.admin_os_creds, flavor_config)
159         self.flavor_creator.create()
160
161         self.network_name = self.guid + '-net'
162         self.subnet_name = self.guid + '-subnet'
163         self.vm_inst_name = self.guid + '-inst'
164
165         self.env_values = {
166             'image_name': self.image_creator.image_settings.name,
167             'flavor_name': self.flavor_creator.flavor_settings.name,
168             'net_name': self.network_name,
169             'subnet_name': self.subnet_name,
170             'inst_name': self.vm_inst_name}
171
172         self.heat_tmplt_path = pkg_resources.resource_filename(
173             'snaps.openstack.tests.heat', 'test_heat_template.yaml')
174
175     def tearDown(self):
176         """
177         Cleans the stack and downloaded stack file
178         """
179         if self.stack_creator:
180             try:
181                 self.stack_creator.clean()
182             except:
183                 pass
184
185         if self.image_creator:
186             try:
187                 self.image_creator.clean()
188             except:
189                 pass
190
191         if self.flavor_creator:
192             try:
193                 self.flavor_creator.clean()
194             except:
195                 pass
196
197         super(self.__class__, self).__clean__()
198
199     def test_create_stack_template_file(self):
200         """
201         Tests the creation of an OpenStack stack from Heat template file.
202         """
203         # Create Stack
204         # Set the default stack settings, then set any custom parameters sent
205         # from the app
206         stack_settings = StackConfig(
207             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
208             template_path=self.heat_tmplt_path,
209             env_values=self.env_values)
210         self.stack_creator = OpenStackHeatStack(
211             self.os_creds, stack_settings)
212         created_stack = self.stack_creator.create(block=True)
213         self.assertIsNotNone(created_stack)
214
215         retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
216                                                      created_stack.id)
217         self.assertIsNotNone(retrieved_stack)
218         self.assertEqual(created_stack.name, retrieved_stack.name)
219         self.assertEqual(created_stack.id, retrieved_stack.id)
220         self.assertEqual(0, len(self.stack_creator.get_outputs()))
221
222         derived_creator = create_stack.generate_creator(
223             self.os_creds, retrieved_stack,
224             [self.image_creator.image_settings])
225         derived_stack = derived_creator.get_stack()
226         self.assertEqual(retrieved_stack, derived_stack)
227
228     def test_create_stack_short_timeout(self):
229         """
230         Tests the creation of an OpenStack stack from Heat template file.
231         """
232         # Create Stack
233         # Set the default stack settings, then set any custom parameters sent
234         # from the app
235         stack_settings = StackConfig(
236             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
237             template_path=self.heat_tmplt_path,
238             env_values=self.env_values, stack_create_timeout=0)
239
240         self.stack_creator = OpenStackHeatStack(
241             self.os_creds, stack_settings)
242         with self.assertRaises(StackCreationError):
243             self.stack_creator.create(block=True)
244
245     def test_create_stack_template_dict(self):
246         """
247         Tests the creation of an OpenStack stack from a heat dict() object.
248         """
249         # Create Stack
250         # Set the default stack settings, then set any custom parameters sent
251         # from the app
252         template_dict = heat_utils.parse_heat_template_str(
253             file_utils.read_file(self.heat_tmplt_path))
254         stack_settings = StackConfig(
255             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
256             template=template_dict,
257             env_values=self.env_values)
258         self.stack_creator = OpenStackHeatStack(
259             self.os_creds, stack_settings)
260         created_stack = self.stack_creator.create(block=True)
261         self.assertIsNotNone(created_stack)
262
263         retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
264                                                      created_stack.id)
265         self.assertIsNotNone(retrieved_stack)
266         self.assertEqual(created_stack.name, retrieved_stack.name)
267         self.assertEqual(created_stack.id, retrieved_stack.id)
268         self.assertEqual(0, len(self.stack_creator.get_outputs()))
269
270     def test_create_delete_stack(self):
271         """
272         Tests the creation then deletion of an OpenStack stack to ensure
273         clean() does not raise an Exception.
274         """
275         # Create Stack
276         template_dict = heat_utils.parse_heat_template_str(
277             file_utils.read_file(self.heat_tmplt_path))
278         stack_settings = StackConfig(
279             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
280             template=template_dict,
281             env_values=self.env_values)
282         self.stack_creator = OpenStackHeatStack(
283             self.os_creds, stack_settings)
284         created_stack = self.stack_creator.create(block=True)
285         self.assertIsNotNone(created_stack)
286
287         retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
288                                                      created_stack.id)
289         self.assertIsNotNone(retrieved_stack)
290         self.assertEqual(created_stack.name, retrieved_stack.name)
291         self.assertEqual(created_stack.id, retrieved_stack.id)
292         self.assertEqual(0, len(self.stack_creator.get_outputs()))
293         self.assertEqual(snaps.config.stack.STATUS_CREATE_COMPLETE,
294                          self.stack_creator.get_status())
295
296         # Delete Stack manually
297         heat_utils.delete_stack(self.heat_cli, created_stack)
298
299         end_time = time.time() + 90
300         deleted = False
301         while time.time() < end_time:
302             status = heat_utils.get_stack_status(self.heat_cli,
303                                                  retrieved_stack.id)
304             if status == snaps.config.stack.STATUS_DELETE_COMPLETE:
305                 deleted = True
306                 break
307
308         self.assertTrue(deleted)
309
310         # Must not throw an exception when attempting to cleanup non-existent
311         # stack
312         self.stack_creator.clean()
313         self.assertIsNone(self.stack_creator.get_stack())
314
315     def test_create_same_stack(self):
316         """
317         Tests the creation of an OpenStack stack when the stack already exists.
318         """
319         # Create Stack
320         template_dict = heat_utils.parse_heat_template_str(
321             file_utils.read_file(self.heat_tmplt_path))
322         stack_settings = StackConfig(
323             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
324             template=template_dict,
325             env_values=self.env_values)
326         self.stack_creator = OpenStackHeatStack(
327             self.os_creds, stack_settings)
328         created_stack1 = self.stack_creator.create(block=True)
329
330         retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
331                                                      created_stack1.id)
332         self.assertIsNotNone(retrieved_stack)
333         self.assertEqual(created_stack1.name, retrieved_stack.name)
334         self.assertEqual(created_stack1.id, retrieved_stack.id)
335         self.assertEqual(0, len(self.stack_creator.get_outputs()))
336
337         # Should be retrieving the instance data
338         stack_creator2 = OpenStackHeatStack(self.os_creds, stack_settings)
339         stack2 = stack_creator2.create(block=True)
340         self.assertEqual(created_stack1.id, stack2.id)
341
342     def test_retrieve_network_creators(self):
343         """
344         Tests the creation of an OpenStack stack from Heat template file and
345         the retrieval of the network creator.
346         """
347         stack_settings = StackConfig(
348             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
349             template_path=self.heat_tmplt_path,
350             env_values=self.env_values)
351         self.stack_creator = OpenStackHeatStack(
352             self.os_creds, stack_settings)
353         created_stack = self.stack_creator.create(block=True)
354         self.assertIsNotNone(created_stack)
355
356         net_creators = self.stack_creator.get_network_creators()
357         self.assertIsNotNone(net_creators)
358         self.assertEqual(1, len(net_creators))
359         self.assertEqual(self.network_name, net_creators[0].get_network().name)
360
361         # Need to use 'admin' creds as heat creates objects under it's own
362         # project/tenant
363         neutron = neutron_utils.neutron_client(
364             self.os_creds, self.os_session)
365         keystone = keystone_utils.keystone_client(
366             self.os_creds, self.os_session)
367         net_by_name = neutron_utils.get_network(
368             neutron, keystone, network_name=net_creators[0].get_network().name)
369         self.assertEqual(net_creators[0].get_network(), net_by_name)
370         self.assertIsNotNone(neutron_utils.get_network_by_id(
371             neutron, net_creators[0].get_network().id))
372
373         self.assertEqual(1, len(net_creators[0].get_network().subnets))
374         subnet = net_creators[0].get_network().subnets[0]
375         subnet_by_name = neutron_utils.get_subnet(
376             neutron, net_creators[0].get_network(), subnet_name=subnet.name)
377         self.assertEqual(subnet, subnet_by_name)
378
379         subnet_by_id = neutron_utils.get_subnet_by_id(neutron, subnet.id)
380         self.assertIsNotNone(subnet_by_id)
381         self.assertEqual(subnet_by_name, subnet_by_id)
382
383     def test_retrieve_vm_inst_creators(self):
384         """
385         Tests the creation of an OpenStack stack from Heat template file and
386         the retrieval of the network creator.
387         """
388         stack_settings = StackConfig(
389             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
390             template_path=self.heat_tmplt_path,
391             env_values=self.env_values)
392         self.stack_creator = OpenStackHeatStack(
393             self.os_creds, stack_settings)
394         created_stack = self.stack_creator.create(block=True)
395         self.assertIsNotNone(created_stack)
396
397         vm_inst_creators = self.stack_creator.get_vm_inst_creators()
398         self.assertIsNotNone(vm_inst_creators)
399         self.assertEqual(1, len(vm_inst_creators))
400         self.assertEqual(self.vm_inst_name,
401                          vm_inst_creators[0].get_vm_inst().name)
402
403         nova = nova_utils.nova_client(self.os_creds, self.os_session)
404         neutron = neutron_utils.neutron_client(self.os_creds, self.os_session)
405         keystone = keystone_utils.keystone_client(self.os_creds, self.os_session)
406         vm_inst_by_name = nova_utils.get_server(
407             nova, neutron, keystone,
408             server_name=vm_inst_creators[0].get_vm_inst().name)
409
410         self.assertEqual(vm_inst_creators[0].get_vm_inst(), vm_inst_by_name)
411         self.assertIsNotNone(nova_utils.get_server_object_by_id(
412             nova, neutron, keystone, vm_inst_creators[0].get_vm_inst().id))
413
414
415 class CreateStackFloatingIpTests(OSIntegrationTestCase):
416     """
417     Tests to ensure that floating IPs can be accessed via an
418     OpenStackVmInstance object obtained from the OpenStackHeatStack instance
419     """
420
421     def setUp(self):
422         self.user_roles = ['heat_stack_owner', 'admin']
423
424         super(self.__class__, self).__start__()
425
426         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
427
428         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
429         self.stack_creator = None
430
431         self.image_creator = OpenStackImage(
432             self.os_creds, openstack_tests.cirros_image_settings(
433                 name=self.guid + '-image',
434                 image_metadata=self.image_metadata))
435         self.image_creator.create()
436
437         self.network_name = self.guid + '-net'
438         self.subnet_name = self.guid + '-subnet'
439         self.flavor1_name = self.guid + '-flavor1'
440         self.flavor2_name = self.guid + '-flavor2'
441         self.sec_grp_name = self.guid + '-sec_grp'
442         self.vm_inst1_name = self.guid + '-inst1'
443         self.vm_inst2_name = self.guid + '-inst2'
444         self.keypair_name = self.guid + '-kp'
445
446         self.env_values = {
447             'image1_name': self.image_creator.image_settings.name,
448             'image2_name': self.image_creator.image_settings.name,
449             'flavor1_name': self.flavor1_name,
450             'flavor2_name': self.flavor2_name,
451             'net_name': self.network_name,
452             'subnet_name': self.subnet_name,
453             'inst1_name': self.vm_inst1_name,
454             'inst2_name': self.vm_inst2_name,
455             'keypair_name': self.keypair_name,
456             'external_net_name': self.ext_net_name,
457             'security_group_name': self.sec_grp_name}
458
459         self.heat_tmplt_path = pkg_resources.resource_filename(
460             'snaps.openstack.tests.heat', 'floating_ip_heat_template.yaml')
461
462         self.vm_inst_creators = list()
463
464     def tearDown(self):
465         """
466         Cleans the stack and downloaded stack file
467         """
468         if self.stack_creator:
469             try:
470                 self.stack_creator.clean()
471             except:
472                 pass
473
474         if self.image_creator:
475             try:
476                 self.image_creator.clean()
477             except:
478                 pass
479
480         for vm_inst_creator in self.vm_inst_creators:
481             try:
482                 keypair_settings = vm_inst_creator.keypair_settings
483                 if keypair_settings and keypair_settings.private_filepath:
484                     expanded_path = os.path.expanduser(
485                         keypair_settings.private_filepath)
486                     os.chmod(expanded_path, 0o755)
487                     os.remove(expanded_path)
488             except:
489                 pass
490
491         super(self.__class__, self).__clean__()
492
493     def test_connect_via_ssh_heat_vm(self):
494         """
495         Tests the creation of an OpenStack stack from Heat template file and
496         the retrieval of two VM instance creators and attempt to connect via
497         SSH to the first one with a floating IP.
498         """
499         stack_settings = StackConfig(
500             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
501             template_path=self.heat_tmplt_path,
502             env_values=self.env_values)
503         self.stack_creator = OpenStackHeatStack(
504             self.os_creds, stack_settings,
505             [self.image_creator.image_settings])
506         created_stack = self.stack_creator.create(block=True)
507         self.assertIsNotNone(created_stack)
508
509         self.vm_inst_creators = self.stack_creator.get_vm_inst_creators(
510             heat_keypair_option='private_key')
511         self.assertIsNotNone(self.vm_inst_creators)
512         self.assertEqual(2, len(self.vm_inst_creators))
513
514         for vm_inst_creator in self.vm_inst_creators:
515             if vm_inst_creator.get_vm_inst().name == self.vm_inst1_name:
516                 self.assertTrue(
517                     create_instance_tests.validate_ssh_client(vm_inst_creator))
518             else:
519                 vm_settings = vm_inst_creator.instance_settings
520                 self.assertEqual(0, len(vm_settings.floating_ip_settings))
521
522     def test_connect_via_ssh_heat_vm_derived(self):
523         """
524         Tests the the retrieval of two VM instance creators from a derived
525         OpenStackHeatStack object and attempt to connect via
526         SSH to the first one with a floating IP.
527         """
528         stack_settings = StackConfig(
529             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
530             template_path=self.heat_tmplt_path,
531             env_values=self.env_values)
532         self.stack_creator = OpenStackHeatStack(
533             self.os_creds, stack_settings,
534             [self.image_creator.image_settings])
535         created_stack = self.stack_creator.create(block=True)
536         self.assertIsNotNone(created_stack)
537
538         derived_stack = create_stack.generate_creator(
539             self.os_creds, created_stack,
540             [self.image_creator.image_settings])
541
542         self.vm_inst_creators = derived_stack.get_vm_inst_creators(
543             heat_keypair_option='private_key')
544         self.assertIsNotNone(self.vm_inst_creators)
545         self.assertEqual(2, len(self.vm_inst_creators))
546
547         for vm_inst_creator in self.vm_inst_creators:
548             if vm_inst_creator.get_vm_inst().name == self.vm_inst1_name:
549                 self.assertTrue(
550                     create_instance_tests.validate_ssh_client(vm_inst_creator))
551             else:
552                 vm_settings = vm_inst_creator.instance_settings
553                 self.assertEqual(0, len(vm_settings.floating_ip_settings))
554
555
556 class CreateStackNestedResourceTests(OSIntegrationTestCase):
557     """
558     Tests to ensure that nested heat templates work
559     """
560
561     def setUp(self):
562         self.user_roles = ['heat_stack_owner']
563
564         super(self.__class__, self).__start__()
565
566         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
567
568         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
569         self.stack_creator = None
570
571         self.image_creator = OpenStackImage(
572             self.os_creds, openstack_tests.cirros_image_settings(
573                 name="{}-{}".format(self.guid, 'image'),
574                 image_metadata=self.image_metadata))
575         self.image_creator.create()
576
577         flavor_config = openstack_tests.get_flavor_config(
578             name="{}-{}".format(self.guid, 'flavor-name'), ram=256, disk=10,
579             vcpus=1, metadata=self.flavor_metadata)
580         self.flavor_creator = OpenStackFlavor(
581             self.admin_os_creds, flavor_config)
582         self.flavor_creator.create()
583
584         env_values = {
585             'network_name': self.guid + '-network',
586             'public_network': self.ext_net_name,
587             'agent_image': self.image_creator.image_settings.name,
588             'agent_flavor': self.flavor_creator.flavor_settings.name,
589             'key_name': self.guid + '-key',
590         }
591
592         heat_tmplt_path = pkg_resources.resource_filename(
593             'snaps.openstack.tests.heat', 'agent-group.yaml')
594         heat_resource_path = pkg_resources.resource_filename(
595             'snaps.openstack.tests.heat', 'agent.yaml')
596
597         stack_settings = StackConfig(
598             name="{}-{}".format(
599                 self.__class__.__name__, str(self.guid) + '-stack'),
600             template_path=heat_tmplt_path,
601             resource_files=[heat_resource_path],
602             env_values=env_values)
603
604         self.stack_creator = OpenStackHeatStack(
605             self.os_creds, stack_settings,
606             [self.image_creator.image_settings])
607
608         self.vm_inst_creators = list()
609
610     def tearDown(self):
611         """
612         Cleans the stack and downloaded stack file
613         """
614         if self.stack_creator:
615             try:
616                 self.stack_creator.clean()
617             except:
618                 pass
619
620         if self.image_creator:
621             try:
622                 self.image_creator.clean()
623             except:
624                 pass
625
626         if self.flavor_creator:
627             try:
628                 self.flavor_creator.clean()
629             except:
630                 pass
631
632         for vm_inst_creator in self.vm_inst_creators:
633             try:
634                 keypair_settings = vm_inst_creator.keypair_settings
635                 if keypair_settings and keypair_settings.private_filepath:
636                     expanded_path = os.path.expanduser(
637                         keypair_settings.private_filepath)
638                     os.chmod(expanded_path, 0o755)
639                     os.remove(expanded_path)
640             except:
641                 pass
642
643         super(self.__class__, self).__clean__()
644
645     def test_nested(self):
646         """
647         Tests the creation of an OpenStack stack from Heat template file and
648         the retrieval of two VM instance creators and attempt to connect via
649         SSH to the first one with a floating IP.
650         """
651         created_stack = self.stack_creator.create(block=True)
652         self.assertIsNotNone(created_stack)
653
654         self.vm_inst_creators = self.stack_creator.get_vm_inst_creators(
655             heat_keypair_option='private_key')
656         self.assertIsNotNone(self.vm_inst_creators)
657         self.assertEqual(1, len(self.vm_inst_creators))
658
659         for vm_inst_creator in self.vm_inst_creators:
660             self.assertTrue(
661                 create_instance_tests.validate_ssh_client(vm_inst_creator))
662
663
664 class CreateStackUpdateTests(OSIntegrationTestCase):
665     """
666     Tests to ensure that stack update commands work
667     """
668
669     def setUp(self):
670         self.user_roles = ['heat_stack_owner']
671
672         super(self.__class__, self).__start__()
673
674         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
675
676         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
677         self.stack_creator = None
678
679         self.image_creator = OpenStackImage(
680             self.os_creds, openstack_tests.cirros_image_settings(
681                 name=self.guid + '-image',
682                 image_metadata=self.image_metadata))
683         self.image_creator.create()
684
685         self.flavor_creator = OpenStackFlavor(
686             self.admin_os_creds,
687             FlavorConfig(
688                 name=self.guid + '-flavor-name', ram=256, disk=10, vcpus=1))
689         self.flavor_creator.create()
690
691         env_values = {
692             'network_name': self.guid + '-network',
693             'public_network': self.ext_net_name,
694             'agent_image': self.image_creator.image_settings.name,
695             'agent_flavor': self.flavor_creator.flavor_settings.name,
696             'key_name': self.guid + '-key',
697         }
698
699         heat_tmplt_path = pkg_resources.resource_filename(
700             'snaps.openstack.tests.heat', 'agent-group.yaml')
701         heat_resource_path = pkg_resources.resource_filename(
702             'snaps.openstack.tests.heat', 'agent.yaml')
703
704         stack_settings = StackConfig(
705             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
706             template_path=heat_tmplt_path,
707             resource_files=[heat_resource_path],
708             env_values=env_values)
709
710         self.stack_creator = OpenStackHeatStack(
711             self.os_creds, stack_settings,
712             [self.image_creator.image_settings])
713
714         self.vm_inst_creators = list()
715
716     def tearDown(self):
717         """
718         Cleans the stack and downloaded stack file
719         """
720         if self.stack_creator:
721             try:
722                 self.stack_creator.clean()
723             except:
724                 pass
725
726         if self.image_creator:
727             try:
728                 self.image_creator.clean()
729             except:
730                 pass
731
732         if self.flavor_creator:
733             try:
734                 self.flavor_creator.clean()
735             except:
736                 pass
737
738         for vm_inst_creator in self.vm_inst_creators:
739             try:
740                 keypair_settings = vm_inst_creator.keypair_settings
741                 if keypair_settings and keypair_settings.private_filepath:
742                     expanded_path = os.path.expanduser(
743                         keypair_settings.private_filepath)
744                     os.chmod(expanded_path, 0o755)
745                     os.remove(expanded_path)
746             except:
747                 pass
748
749         super(self.__class__, self).__clean__()
750
751     def test_update(self):
752         """
753         Tests the update of an OpenStack stack from Heat template file
754         by changing the number of VM instances from 1 to 2, and
755         the retrieval of two VM instance creators and attempt to connect via
756         SSH to the first one with a floating IP.
757         """
758         created_stack = self.stack_creator.create(block=True)
759         self.assertIsNotNone(created_stack)
760
761         self.vm_inst_creators = self.stack_creator.get_vm_inst_creators(
762             heat_keypair_option='private_key')
763         self.assertIsNotNone(self.vm_inst_creators)
764         self.assertEqual(1, len(self.vm_inst_creators))
765
766         for vm_inst_creator in self.vm_inst_creators:
767             self.assertTrue(
768                 create_instance_tests.validate_ssh_client(vm_inst_creator))
769
770         env_values = {
771             'network_name': self.guid + '-network',
772             'public_network': self.ext_net_name,
773             'agent_count': 2,
774             'agent_image': self.image_creator.image_settings.name,
775             'agent_flavor': self.flavor_creator.flavor_settings.name,
776             'key_name': self.guid + '-key',
777         }
778
779         updated_stack = self.stack_creator.update(env_values, block=True)
780         self.assertIsNotNone(updated_stack)
781         self.assertEqual(STATUS_UPDATE_COMPLETE, updated_stack.status)
782
783         self.vm_inst_creators = self.stack_creator.get_vm_inst_creators(
784             heat_keypair_option='private_key')
785         self.assertIsNotNone(self.vm_inst_creators)
786         self.assertEqual(2, len(self.vm_inst_creators))
787
788         for vm_inst_creator in self.vm_inst_creators:
789             self.assertTrue(
790                 create_instance_tests.validate_ssh_client(vm_inst_creator))
791
792
793 class CreateStackRouterTests(OSIntegrationTestCase):
794     """
795     Tests for the CreateStack class defined in create_stack.py where the
796     target is a Network, Subnet, and Router
797     """
798
799     def setUp(self):
800         """
801         Instantiates the CreateStack object that is responsible for downloading
802         and creating an OS stack file within OpenStack
803         """
804         self.user_roles = ['heat_stack_owner']
805
806         super(self.__class__, self).__start__()
807
808         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
809
810         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
811         self.neutron = neutron_utils.neutron_client(
812             self.os_creds, self.os_session)
813         self.stack_creator = None
814
815         self.net_name = self.guid + '-net'
816         self.subnet_name = self.guid + '-subnet'
817         self.router_name = self.guid + '-router'
818
819         self.env_values = {
820             'net_name': self.net_name,
821             'subnet_name': self.subnet_name,
822             'router_name': self.router_name,
823             'external_net_name': self.ext_net_name}
824
825         self.heat_tmplt_path = pkg_resources.resource_filename(
826             'snaps.openstack.tests.heat', 'router_heat_template.yaml')
827
828         stack_settings = StackConfig(
829             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
830             template_path=self.heat_tmplt_path,
831             env_values=self.env_values)
832         self.stack_creator = OpenStackHeatStack(
833             self.os_creds, stack_settings)
834         self.created_stack = self.stack_creator.create(block=True)
835         self.assertIsNotNone(self.created_stack)
836
837     def tearDown(self):
838         """
839         Cleans the stack and downloaded stack file
840         """
841         if self.stack_creator:
842             try:
843                 self.stack_creator.clean()
844             except:
845                 pass
846
847         super(self.__class__, self).__clean__()
848
849     def test_retrieve_router_creator(self):
850         """
851         Tests the creation of an OpenStack stack from Heat template file and
852         the retrieval of an OpenStackRouter creator/state machine instance
853         """
854         router_creators = self.stack_creator.get_router_creators()
855         self.assertEqual(1, len(router_creators))
856
857         creator = router_creators[0]
858         self.assertEqual(self.router_name, creator.router_settings.name)
859
860         router = creator.get_router()
861
862         ext_net = neutron_utils.get_network(
863             self.neutron, self.keystone, network_name=self.ext_net_name)
864         self.assertEqual(ext_net.id, router.external_network_id)
865
866
867 class CreateStackVolumeTests(OSIntegrationTestCase):
868     """
869     Tests to ensure that floating IPs can be accessed via an
870     OpenStackVolume object obtained from the OpenStackHeatStack instance
871     """
872
873     def setUp(self):
874
875         self.user_roles = ['heat_stack_owner', 'admin']
876
877         super(self.__class__, self).__start__()
878
879         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
880
881         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
882         self.stack_creator = None
883
884         self.volume_name = self.guid + '-volume'
885         self.volume_type_name = self.guid + '-volume-type'
886
887         self.env_values = {
888             'volume_name': self.volume_name,
889             'volume_type_name': self.volume_type_name}
890
891         self.heat_tmplt_path = pkg_resources.resource_filename(
892             'snaps.openstack.tests.heat', 'volume_heat_template.yaml')
893
894         stack_settings = StackConfig(
895             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
896             template_path=self.heat_tmplt_path,
897             env_values=self.env_values)
898         self.stack_creator = OpenStackHeatStack(
899             self.os_creds, stack_settings)
900         self.created_stack = self.stack_creator.create(block=True)
901         self.assertIsNotNone(self.created_stack)
902
903     def tearDown(self):
904         """
905         Cleans the stack and downloaded stack file
906         """
907         if self.stack_creator:
908             try:
909                 self.stack_creator.clean()
910             except:
911                 pass
912
913         super(self.__class__, self).__clean__()
914
915     def test_retrieve_volume_creator(self):
916         """
917         Tests the creation of an OpenStack stack from Heat template file and
918         the retrieval of an OpenStackVolume creator/state machine instance
919         """
920         volume_creators = self.stack_creator.get_volume_creators()
921         self.assertEqual(1, len(volume_creators))
922
923         creator = volume_creators[0]
924         self.assertEqual(self.volume_name, creator.volume_settings.name)
925         self.assertEqual(self.volume_name, creator.get_volume().name)
926         self.assertEqual(self.volume_type_name,
927                          creator.volume_settings.type_name)
928         self.assertEqual(self.volume_type_name, creator.get_volume().type)
929         self.assertEqual(1, creator.volume_settings.size)
930         self.assertEqual(1, creator.get_volume().size)
931
932     def test_retrieve_volume_type_creator(self):
933         """
934         Tests the creation of an OpenStack stack from Heat template file and
935         the retrieval of an OpenStackVolume creator/state machine instance
936         """
937         volume_type_creators = self.stack_creator.get_volume_type_creators()
938         self.assertEqual(1, len(volume_type_creators))
939
940         creator = volume_type_creators[0]
941         self.assertIsNotNone(creator)
942
943         volume_type = creator.get_volume_type()
944         self.assertIsNotNone(volume_type)
945
946         self.assertEqual(self.volume_type_name, volume_type.name)
947         self.assertTrue(volume_type.public)
948         self.assertIsNone(volume_type.qos_spec)
949
950         # TODO - Add encryption back and find out why it broke in Pike
951         # encryption = volume_type.encryption
952         # self.assertIsNotNone(encryption)
953         # self.assertIsNone(encryption.cipher)
954         # self.assertEqual('front-end', encryption.control_location)
955         # self.assertIsNone(encryption.key_size)
956         # self.assertEqual(u'nova.volume.encryptors.luks.LuksEncryptor',
957         #                  encryption.provider)
958         # self.assertEqual(volume_type.id, encryption.volume_type_id)
959
960
961 class CreateStackFlavorTests(OSIntegrationTestCase):
962     """
963     Tests to ensure that floating IPs can be accessed via an
964     OpenStackFlavor object obtained from the OpenStackHeatStack instance
965     """
966
967     def setUp(self):
968
969         self.user_roles = ['heat_stack_owner', 'admin']
970
971         super(self.__class__, self).__start__()
972
973         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
974
975         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
976         self.stack_creator = None
977
978         self.heat_tmplt_path = pkg_resources.resource_filename(
979             'snaps.openstack.tests.heat', 'flavor_heat_template.yaml')
980
981         stack_settings = StackConfig(
982             name=self.guid + '-stack',
983             template_path=self.heat_tmplt_path)
984         self.stack_creator = OpenStackHeatStack(
985             self.os_creds, stack_settings)
986         self.created_stack = self.stack_creator.create(block=True)
987         self.assertIsNotNone(self.created_stack)
988
989     def tearDown(self):
990         """
991         Cleans the stack and downloaded stack file
992         """
993         if self.stack_creator:
994             try:
995                 self.stack_creator.clean()
996             except:
997                 pass
998
999         super(self.__class__, self).__clean__()
1000
1001     def test_retrieve_flavor_creator(self):
1002         """
1003         Tests the creation of an OpenStack stack from Heat template file and
1004         the retrieval of an OpenStackVolume creator/state machine instance
1005         """
1006         flavor_creators = self.stack_creator.get_flavor_creators()
1007         self.assertEqual(1, len(flavor_creators))
1008
1009         creator = flavor_creators[0]
1010         self.assertTrue(creator.get_flavor().name.startswith(self.guid))
1011         self.assertEqual(1024, creator.get_flavor().ram)
1012         self.assertEqual(200, creator.get_flavor().disk)
1013         self.assertEqual(8, creator.get_flavor().vcpus)
1014         self.assertEqual(0, creator.get_flavor().ephemeral)
1015         self.assertIsNone(creator.get_flavor().swap)
1016         self.assertEqual(1.0, creator.get_flavor().rxtx_factor)
1017         self.assertTrue(creator.get_flavor().is_public)
1018
1019
1020 class CreateStackKeypairTests(OSIntegrationTestCase):
1021     """
1022     Tests to ensure that floating IPs can be accessed via an
1023     OpenStackKeypair object obtained from the OpenStackHeatStack instance
1024     """
1025
1026     def setUp(self):
1027
1028         self.user_roles = ['heat_stack_owner']
1029
1030         super(self.__class__, self).__start__()
1031
1032         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
1033
1034         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
1035         self.nova = nova_utils.nova_client(self.os_creds, self.os_session)
1036         self.stack_creator = None
1037
1038         self.keypair_name = self.guid + '-kp'
1039
1040         self.env_values = {
1041             'keypair_name': self.keypair_name}
1042
1043         self.heat_tmplt_path = pkg_resources.resource_filename(
1044             'snaps.openstack.tests.heat', 'keypair_heat_template.yaml')
1045
1046         stack_settings = StackConfig(
1047             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
1048             template_path=self.heat_tmplt_path,
1049             env_values=self.env_values)
1050         self.stack_creator = OpenStackHeatStack(
1051             self.os_creds, stack_settings)
1052         self.created_stack = self.stack_creator.create(block=True)
1053         self.assertIsNotNone(self.created_stack)
1054
1055         self.keypair_creators = list()
1056
1057     def tearDown(self):
1058         """
1059         Cleans the stack and downloaded stack file
1060         """
1061         if self.stack_creator:
1062             try:
1063                 self.stack_creator.clean()
1064             except:
1065                 pass
1066         for keypair_creator in self.keypair_creators:
1067             try:
1068                 keypair_creator.clean()
1069             except:
1070                 pass
1071
1072         super(self.__class__, self).__clean__()
1073
1074     def test_retrieve_keypair_creator(self):
1075         """
1076         Tests the creation of an OpenStack stack from Heat template file and
1077         the retrieval of an OpenStackKeypair creator/state machine instance
1078         """
1079         self.kp_creators = self.stack_creator.get_keypair_creators(
1080             'private_key')
1081         self.assertEqual(1, len(self.kp_creators))
1082
1083         self.keypair_creator = self.kp_creators[0]
1084
1085         self.assertEqual(self.keypair_name,
1086                          self.keypair_creator.get_keypair().name)
1087         self.assertIsNotNone(
1088             self.keypair_creator.keypair_settings.private_filepath)
1089
1090         private_file_contents = file_utils.read_file(
1091             self.keypair_creator.keypair_settings.private_filepath)
1092         self.assertTrue(private_file_contents.startswith(
1093             '-----BEGIN RSA PRIVATE KEY-----'))
1094
1095         keypair = nova_utils.get_keypair_by_id(
1096             self.nova, self.keypair_creator.get_keypair().id)
1097         self.assertIsNotNone(keypair)
1098         self.assertEqual(self.keypair_creator.get_keypair(), keypair)
1099
1100
1101 class CreateStackSecurityGroupTests(OSIntegrationTestCase):
1102     """
1103     Tests for the OpenStackHeatStack class to ensure it returns an
1104     OpenStackSecurityGroup object
1105     """
1106
1107     def setUp(self):
1108         """
1109         Instantiates the CreateStack object that is responsible for downloading
1110         and creating an OS stack file within OpenStack
1111         """
1112         self.user_roles = ['heat_stack_owner']
1113
1114         super(self.__class__, self).__start__()
1115
1116         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
1117
1118         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
1119         self.nova = nova_utils.nova_client(self.os_creds, self.os_session)
1120         self.stack_creator = None
1121
1122         self.security_group_name = self.guid + '-sec-grp'
1123
1124         self.env_values = {
1125             'security_group_name': self.security_group_name}
1126
1127         self.heat_tmplt_path = pkg_resources.resource_filename(
1128             'snaps.openstack.tests.heat', 'security_group_heat_template.yaml')
1129
1130         stack_settings = StackConfig(
1131             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
1132             template_path=self.heat_tmplt_path,
1133             env_values=self.env_values)
1134         self.stack_creator = OpenStackHeatStack(
1135             self.os_creds, stack_settings)
1136         self.created_stack = self.stack_creator.create(block=True)
1137         self.assertIsNotNone(self.created_stack)
1138
1139     def tearDown(self):
1140         """
1141         Cleans the stack and downloaded stack file
1142         """
1143         if self.stack_creator:
1144             try:
1145                 self.stack_creator.clean()
1146             except:
1147                 pass
1148
1149         super(self.__class__, self).__clean__()
1150
1151     def test_retrieve_security_group_creator(self):
1152         """
1153         Tests the creation of an OpenStack stack from Heat template file and
1154         the retrieval of an OpenStackSecurityGroup creator/state machine
1155         instance
1156         """
1157         sec_grp_creators = self.stack_creator.get_security_group_creators()
1158         self.assertEqual(1, len(sec_grp_creators))
1159
1160         creator = sec_grp_creators[0]
1161         sec_grp = creator.get_security_group()
1162
1163         self.assertEqual(self.security_group_name, sec_grp.name)
1164         self.assertEqual('Test description', sec_grp.description)
1165         self.assertEqual(2, len(sec_grp.rules))
1166
1167         has_ssh_rule = False
1168         has_icmp_rule = False
1169
1170         for rule in sec_grp.rules:
1171             if (rule.security_group_id == sec_grp.id
1172                     and rule.direction == 'egress'
1173                     and rule.ethertype == 'IPv4'
1174                     and rule.port_range_min == 22
1175                     and rule.port_range_max == 22
1176                     and rule.protocol == 'tcp'
1177                     and rule.remote_group_id is None
1178                     and rule.remote_ip_prefix == '0.0.0.0/0'):
1179                 has_ssh_rule = True
1180             if (rule.security_group_id == sec_grp.id
1181                     and rule.direction == 'ingress'
1182                     and rule.ethertype == 'IPv4'
1183                     and rule.port_range_min is None
1184                     and rule.port_range_max is None
1185                     and rule.protocol == 'icmp'
1186                     and rule.remote_group_id is None
1187                     and rule.remote_ip_prefix == '0.0.0.0/0'):
1188                 has_icmp_rule = True
1189
1190         self.assertTrue(has_ssh_rule)
1191         self.assertTrue(has_icmp_rule)
1192
1193
1194 class CreateStackNegativeTests(OSIntegrationTestCase):
1195     """
1196     Negative test cases for the OpenStackHeatStack class with poor
1197     configuration
1198     """
1199
1200     def setUp(self):
1201         self.user_roles = ['heat_stack_owner']
1202
1203         super(self.__class__, self).__start__()
1204
1205         self.stack_name = self.__class__.__name__ + '-' + str(uuid.uuid4())
1206         self.stack_creator = None
1207         self.heat_tmplt_path = pkg_resources.resource_filename(
1208             'snaps.openstack.tests.heat', 'test_heat_template.yaml')
1209
1210     def tearDown(self):
1211         if self.stack_creator:
1212             self.stack_creator.clean()
1213
1214         super(self.__class__, self).__clean__()
1215
1216     def test_missing_dependencies(self):
1217         """
1218         Expect an StackCreationError when the stack file does not exist
1219         """
1220         stack_settings = StackConfig(name=self.stack_name,
1221                                      template_path=self.heat_tmplt_path)
1222         self.stack_creator = OpenStackHeatStack(
1223             self.os_creds, stack_settings)
1224         with self.assertRaises(HTTPBadRequest):
1225             self.stack_creator.create(block=True)
1226
1227     def test_bad_stack_file(self):
1228         """
1229         Expect an StackCreationError when the stack file does not exist
1230         """
1231         stack_settings = StackConfig(
1232             name=self.stack_name, template_path='foo')
1233         self.stack_creator = OpenStackHeatStack(
1234             self.os_creds, stack_settings)
1235         with self.assertRaises(IOError):
1236             self.stack_creator.create(block=True)
1237
1238
1239 class CreateStackFailureTests(OSIntegrationTestCase):
1240     """
1241     Tests for the OpenStackHeatStack class defined in create_stack.py for
1242     when failures occur. Failures are being triggered by allocating 1 million
1243     CPUs.
1244     """
1245
1246     def setUp(self):
1247         self.user_roles = ['heat_stack_owner']
1248
1249         super(self.__class__, self).__start__()
1250
1251         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
1252
1253         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
1254         self.stack_creator = None
1255
1256         self.tmp_file = file_utils.save_string_to_file(
1257             ' ', str(uuid.uuid4()) + '-bad-image')
1258         self.image_creator = OpenStackImage(
1259             self.os_creds, ImageConfig(
1260                 name=self.guid + 'image', image_file=self.tmp_file.name,
1261                 image_user='foo', img_format='qcow2'))
1262         self.image_creator.create()
1263
1264         # Create Flavor
1265         self.flavor_creator = OpenStackFlavor(
1266             self.admin_os_creds,
1267             FlavorConfig(
1268                 name=self.guid + '-flavor-name', ram=256, disk=10,
1269                 vcpus=1000000))
1270         self.flavor_creator.create()
1271
1272         self.network_name = self.guid + '-net'
1273         self.subnet_name = self.guid + '-subnet'
1274         self.vm_inst_name = self.guid + '-inst'
1275
1276         self.env_values = {
1277             'image_name': self.image_creator.image_settings.name,
1278             'flavor_name': self.flavor_creator.flavor_settings.name,
1279             'net_name': self.network_name,
1280             'subnet_name': self.subnet_name,
1281             'inst_name': self.vm_inst_name}
1282
1283         self.heat_tmplt_path = pkg_resources.resource_filename(
1284             'snaps.openstack.tests.heat', 'test_heat_template.yaml')
1285
1286     def tearDown(self):
1287         """
1288         Cleans the stack and downloaded stack file
1289         """
1290         if self.stack_creator:
1291             try:
1292                 self.stack_creator.clean()
1293             except:
1294                 pass
1295
1296         if self.image_creator:
1297             try:
1298                 self.image_creator.clean()
1299             except:
1300                 pass
1301
1302         if self.flavor_creator:
1303             try:
1304                 self.flavor_creator.clean()
1305             except:
1306                 pass
1307
1308         if self.tmp_file:
1309             try:
1310                 os.remove(self.tmp_file.name)
1311             except:
1312                 pass
1313
1314         super(self.__class__, self).__clean__()
1315
1316     def test_stack_failure(self):
1317         """
1318         Tests the creation of an OpenStack stack from Heat template file that
1319         should always fail due to too many CPU cores
1320         """
1321         # Create Stack
1322         # Set the default stack settings, then set any custom parameters sent
1323         # from the app
1324         stack_settings = StackConfig(
1325             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
1326             template_path=self.heat_tmplt_path,
1327             env_values=self.env_values)
1328         self.stack_creator = OpenStackHeatStack(
1329             self.os_creds, stack_settings)
1330
1331         with self.assertRaises(StackError):
1332             try:
1333                 self.stack_creator.create(block=True)
1334             except StackError:
1335                 resources = heat_utils.get_resources(
1336                     self.heat_cli, self.stack_creator.get_stack().id)
1337
1338                 found = False
1339                 for resource in resources:
1340                     if (resource.status ==
1341                             snaps.config.stack.STATUS_CREATE_COMPLETE):
1342                         found = True
1343                 self.assertTrue(found)
1344                 raise