Pass flavor metadata into heat template
[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             'flavor_extra_specs': self.flavor_metadata,
452             'net_name': self.network_name,
453             'subnet_name': self.subnet_name,
454             'inst1_name': self.vm_inst1_name,
455             'inst2_name': self.vm_inst2_name,
456             'keypair_name': self.keypair_name,
457             'external_net_name': self.ext_net_name,
458             'security_group_name': self.sec_grp_name}
459
460         self.heat_tmplt_path = pkg_resources.resource_filename(
461             'snaps.openstack.tests.heat', 'floating_ip_heat_template.yaml')
462
463         self.vm_inst_creators = list()
464
465     def tearDown(self):
466         """
467         Cleans the stack and downloaded stack file
468         """
469         if self.stack_creator:
470             try:
471                 self.stack_creator.clean()
472             except:
473                 pass
474
475         if self.image_creator:
476             try:
477                 self.image_creator.clean()
478             except:
479                 pass
480
481         for vm_inst_creator in self.vm_inst_creators:
482             try:
483                 keypair_settings = vm_inst_creator.keypair_settings
484                 if keypair_settings and keypair_settings.private_filepath:
485                     expanded_path = os.path.expanduser(
486                         keypair_settings.private_filepath)
487                     os.chmod(expanded_path, 0o755)
488                     os.remove(expanded_path)
489             except:
490                 pass
491
492         super(self.__class__, self).__clean__()
493
494     def test_connect_via_ssh_heat_vm(self):
495         """
496         Tests the creation of an OpenStack stack from Heat template file and
497         the retrieval of two VM instance creators and attempt to connect via
498         SSH to the first one with a floating IP.
499         """
500         stack_settings = StackConfig(
501             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
502             template_path=self.heat_tmplt_path,
503             env_values=self.env_values)
504         self.stack_creator = OpenStackHeatStack(
505             self.os_creds, stack_settings,
506             [self.image_creator.image_settings])
507         created_stack = self.stack_creator.create(block=True)
508         self.assertIsNotNone(created_stack)
509
510         self.vm_inst_creators = self.stack_creator.get_vm_inst_creators(
511             heat_keypair_option='private_key')
512         self.assertIsNotNone(self.vm_inst_creators)
513         self.assertEqual(2, len(self.vm_inst_creators))
514
515         for vm_inst_creator in self.vm_inst_creators:
516             if vm_inst_creator.get_vm_inst().name == self.vm_inst1_name:
517                 self.assertTrue(
518                     create_instance_tests.validate_ssh_client(vm_inst_creator))
519             else:
520                 vm_settings = vm_inst_creator.instance_settings
521                 self.assertEqual(0, len(vm_settings.floating_ip_settings))
522
523     def test_connect_via_ssh_heat_vm_derived(self):
524         """
525         Tests the the retrieval of two VM instance creators from a derived
526         OpenStackHeatStack object and attempt to connect via
527         SSH to the first one with a floating IP.
528         """
529         stack_settings = StackConfig(
530             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
531             template_path=self.heat_tmplt_path,
532             env_values=self.env_values)
533         self.stack_creator = OpenStackHeatStack(
534             self.os_creds, stack_settings,
535             [self.image_creator.image_settings])
536         created_stack = self.stack_creator.create(block=True)
537         self.assertIsNotNone(created_stack)
538
539         derived_stack = create_stack.generate_creator(
540             self.os_creds, created_stack,
541             [self.image_creator.image_settings])
542
543         self.vm_inst_creators = derived_stack.get_vm_inst_creators(
544             heat_keypair_option='private_key')
545         self.assertIsNotNone(self.vm_inst_creators)
546         self.assertEqual(2, len(self.vm_inst_creators))
547
548         for vm_inst_creator in self.vm_inst_creators:
549             if vm_inst_creator.get_vm_inst().name == self.vm_inst1_name:
550                 self.assertTrue(
551                     create_instance_tests.validate_ssh_client(vm_inst_creator))
552             else:
553                 vm_settings = vm_inst_creator.instance_settings
554                 self.assertEqual(0, len(vm_settings.floating_ip_settings))
555
556
557 class CreateStackNestedResourceTests(OSIntegrationTestCase):
558     """
559     Tests to ensure that nested heat templates work
560     """
561
562     def setUp(self):
563         self.user_roles = ['heat_stack_owner']
564
565         super(self.__class__, self).__start__()
566
567         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
568
569         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
570         self.stack_creator = None
571
572         self.image_creator = OpenStackImage(
573             self.os_creds, openstack_tests.cirros_image_settings(
574                 name="{}-{}".format(self.guid, 'image'),
575                 image_metadata=self.image_metadata))
576         self.image_creator.create()
577
578         flavor_config = openstack_tests.get_flavor_config(
579             name="{}-{}".format(self.guid, 'flavor-name'), ram=256, disk=10,
580             vcpus=1, metadata=self.flavor_metadata)
581         self.flavor_creator = OpenStackFlavor(
582             self.admin_os_creds, flavor_config)
583         self.flavor_creator.create()
584
585         env_values = {
586             'network_name': self.guid + '-network',
587             'public_network': self.ext_net_name,
588             'agent_image': self.image_creator.image_settings.name,
589             'agent_flavor': self.flavor_creator.flavor_settings.name,
590             'key_name': self.guid + '-key',
591         }
592
593         heat_tmplt_path = pkg_resources.resource_filename(
594             'snaps.openstack.tests.heat', 'agent-group.yaml')
595         heat_resource_path = pkg_resources.resource_filename(
596             'snaps.openstack.tests.heat', 'agent.yaml')
597
598         stack_settings = StackConfig(
599             name="{}-{}".format(
600                 self.__class__.__name__, str(self.guid) + '-stack'),
601             template_path=heat_tmplt_path,
602             resource_files=[heat_resource_path],
603             env_values=env_values)
604
605         self.stack_creator = OpenStackHeatStack(
606             self.os_creds, stack_settings,
607             [self.image_creator.image_settings])
608
609         self.vm_inst_creators = list()
610
611     def tearDown(self):
612         """
613         Cleans the stack and downloaded stack file
614         """
615         if self.stack_creator:
616             try:
617                 self.stack_creator.clean()
618             except:
619                 pass
620
621         if self.image_creator:
622             try:
623                 self.image_creator.clean()
624             except:
625                 pass
626
627         if self.flavor_creator:
628             try:
629                 self.flavor_creator.clean()
630             except:
631                 pass
632
633         for vm_inst_creator in self.vm_inst_creators:
634             try:
635                 keypair_settings = vm_inst_creator.keypair_settings
636                 if keypair_settings and keypair_settings.private_filepath:
637                     expanded_path = os.path.expanduser(
638                         keypair_settings.private_filepath)
639                     os.chmod(expanded_path, 0o755)
640                     os.remove(expanded_path)
641             except:
642                 pass
643
644         super(self.__class__, self).__clean__()
645
646     def test_nested(self):
647         """
648         Tests the creation of an OpenStack stack from Heat template file and
649         the retrieval of two VM instance creators and attempt to connect via
650         SSH to the first one with a floating IP.
651         """
652         created_stack = self.stack_creator.create(block=True)
653         self.assertIsNotNone(created_stack)
654
655         self.vm_inst_creators = self.stack_creator.get_vm_inst_creators(
656             heat_keypair_option='private_key')
657         self.assertIsNotNone(self.vm_inst_creators)
658         self.assertEqual(1, len(self.vm_inst_creators))
659
660         for vm_inst_creator in self.vm_inst_creators:
661             self.assertTrue(
662                 create_instance_tests.validate_ssh_client(vm_inst_creator))
663
664
665 class CreateStackUpdateTests(OSIntegrationTestCase):
666     """
667     Tests to ensure that stack update commands work
668     """
669
670     def setUp(self):
671         self.user_roles = ['heat_stack_owner']
672
673         super(self.__class__, self).__start__()
674
675         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
676
677         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
678         self.stack_creator = None
679
680         self.image_creator = OpenStackImage(
681             self.os_creds, openstack_tests.cirros_image_settings(
682                 name=self.guid + '-image',
683                 image_metadata=self.image_metadata))
684         self.image_creator.create()
685
686         self.flavor_creator = OpenStackFlavor(
687             self.admin_os_creds,
688             FlavorConfig(
689                 name=self.guid + '-flavor-name', ram=256, disk=10, vcpus=1))
690         self.flavor_creator.create()
691
692         env_values = {
693             'network_name': self.guid + '-network',
694             'public_network': self.ext_net_name,
695             'agent_image': self.image_creator.image_settings.name,
696             'agent_flavor': self.flavor_creator.flavor_settings.name,
697             'key_name': self.guid + '-key',
698         }
699
700         heat_tmplt_path = pkg_resources.resource_filename(
701             'snaps.openstack.tests.heat', 'agent-group.yaml')
702         heat_resource_path = pkg_resources.resource_filename(
703             'snaps.openstack.tests.heat', 'agent.yaml')
704
705         stack_settings = StackConfig(
706             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
707             template_path=heat_tmplt_path,
708             resource_files=[heat_resource_path],
709             env_values=env_values)
710
711         self.stack_creator = OpenStackHeatStack(
712             self.os_creds, stack_settings,
713             [self.image_creator.image_settings])
714
715         self.vm_inst_creators = list()
716
717     def tearDown(self):
718         """
719         Cleans the stack and downloaded stack file
720         """
721         if self.stack_creator:
722             try:
723                 self.stack_creator.clean()
724             except:
725                 pass
726
727         if self.image_creator:
728             try:
729                 self.image_creator.clean()
730             except:
731                 pass
732
733         if self.flavor_creator:
734             try:
735                 self.flavor_creator.clean()
736             except:
737                 pass
738
739         for vm_inst_creator in self.vm_inst_creators:
740             try:
741                 keypair_settings = vm_inst_creator.keypair_settings
742                 if keypair_settings and keypair_settings.private_filepath:
743                     expanded_path = os.path.expanduser(
744                         keypair_settings.private_filepath)
745                     os.chmod(expanded_path, 0o755)
746                     os.remove(expanded_path)
747             except:
748                 pass
749
750         super(self.__class__, self).__clean__()
751
752     def test_update(self):
753         """
754         Tests the update of an OpenStack stack from Heat template file
755         by changing the number of VM instances from 1 to 2, and
756         the retrieval of two VM instance creators and attempt to connect via
757         SSH to the first one with a floating IP.
758         """
759         created_stack = self.stack_creator.create(block=True)
760         self.assertIsNotNone(created_stack)
761
762         self.vm_inst_creators = self.stack_creator.get_vm_inst_creators(
763             heat_keypair_option='private_key')
764         self.assertIsNotNone(self.vm_inst_creators)
765         self.assertEqual(1, len(self.vm_inst_creators))
766
767         for vm_inst_creator in self.vm_inst_creators:
768             self.assertTrue(
769                 create_instance_tests.validate_ssh_client(vm_inst_creator))
770
771         env_values = {
772             'network_name': self.guid + '-network',
773             'public_network': self.ext_net_name,
774             'agent_count': 2,
775             'agent_image': self.image_creator.image_settings.name,
776             'agent_flavor': self.flavor_creator.flavor_settings.name,
777             'key_name': self.guid + '-key',
778         }
779
780         updated_stack = self.stack_creator.update(env_values, block=True)
781         self.assertIsNotNone(updated_stack)
782         self.assertEqual(STATUS_UPDATE_COMPLETE, updated_stack.status)
783
784         self.vm_inst_creators = self.stack_creator.get_vm_inst_creators(
785             heat_keypair_option='private_key')
786         self.assertIsNotNone(self.vm_inst_creators)
787         self.assertEqual(2, len(self.vm_inst_creators))
788
789         for vm_inst_creator in self.vm_inst_creators:
790             self.assertTrue(
791                 create_instance_tests.validate_ssh_client(vm_inst_creator))
792
793
794 class CreateStackRouterTests(OSIntegrationTestCase):
795     """
796     Tests for the CreateStack class defined in create_stack.py where the
797     target is a Network, Subnet, and Router
798     """
799
800     def setUp(self):
801         """
802         Instantiates the CreateStack object that is responsible for downloading
803         and creating an OS stack file within OpenStack
804         """
805         self.user_roles = ['heat_stack_owner']
806
807         super(self.__class__, self).__start__()
808
809         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
810
811         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
812         self.neutron = neutron_utils.neutron_client(
813             self.os_creds, self.os_session)
814         self.stack_creator = None
815
816         self.net_name = self.guid + '-net'
817         self.subnet_name = self.guid + '-subnet'
818         self.router_name = self.guid + '-router'
819
820         self.env_values = {
821             'net_name': self.net_name,
822             'subnet_name': self.subnet_name,
823             'router_name': self.router_name,
824             'external_net_name': self.ext_net_name}
825
826         self.heat_tmplt_path = pkg_resources.resource_filename(
827             'snaps.openstack.tests.heat', 'router_heat_template.yaml')
828
829         stack_settings = StackConfig(
830             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
831             template_path=self.heat_tmplt_path,
832             env_values=self.env_values)
833         self.stack_creator = OpenStackHeatStack(
834             self.os_creds, stack_settings)
835         self.created_stack = self.stack_creator.create(block=True)
836         self.assertIsNotNone(self.created_stack)
837
838     def tearDown(self):
839         """
840         Cleans the stack and downloaded stack file
841         """
842         if self.stack_creator:
843             try:
844                 self.stack_creator.clean()
845             except:
846                 pass
847
848         super(self.__class__, self).__clean__()
849
850     def test_retrieve_router_creator(self):
851         """
852         Tests the creation of an OpenStack stack from Heat template file and
853         the retrieval of an OpenStackRouter creator/state machine instance
854         """
855         router_creators = self.stack_creator.get_router_creators()
856         self.assertEqual(1, len(router_creators))
857
858         creator = router_creators[0]
859         self.assertEqual(self.router_name, creator.router_settings.name)
860
861         router = creator.get_router()
862
863         ext_net = neutron_utils.get_network(
864             self.neutron, self.keystone, network_name=self.ext_net_name)
865         self.assertEqual(ext_net.id, router.external_network_id)
866
867
868 class CreateStackVolumeTests(OSIntegrationTestCase):
869     """
870     Tests to ensure that floating IPs can be accessed via an
871     OpenStackVolume object obtained from the OpenStackHeatStack instance
872     """
873
874     def setUp(self):
875
876         self.user_roles = ['heat_stack_owner', 'admin']
877
878         super(self.__class__, self).__start__()
879
880         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
881
882         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
883         self.stack_creator = None
884
885         self.volume_name = self.guid + '-volume'
886         self.volume_type_name = self.guid + '-volume-type'
887
888         self.env_values = {
889             'volume_name': self.volume_name,
890             'volume_type_name': self.volume_type_name}
891
892         self.heat_tmplt_path = pkg_resources.resource_filename(
893             'snaps.openstack.tests.heat', 'volume_heat_template.yaml')
894
895         stack_settings = StackConfig(
896             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
897             template_path=self.heat_tmplt_path,
898             env_values=self.env_values)
899         self.stack_creator = OpenStackHeatStack(
900             self.os_creds, stack_settings)
901         self.created_stack = self.stack_creator.create(block=True)
902         self.assertIsNotNone(self.created_stack)
903
904     def tearDown(self):
905         """
906         Cleans the stack and downloaded stack file
907         """
908         if self.stack_creator:
909             try:
910                 self.stack_creator.clean()
911             except:
912                 pass
913
914         super(self.__class__, self).__clean__()
915
916     def test_retrieve_volume_creator(self):
917         """
918         Tests the creation of an OpenStack stack from Heat template file and
919         the retrieval of an OpenStackVolume creator/state machine instance
920         """
921         volume_creators = self.stack_creator.get_volume_creators()
922         self.assertEqual(1, len(volume_creators))
923
924         creator = volume_creators[0]
925         self.assertEqual(self.volume_name, creator.volume_settings.name)
926         self.assertEqual(self.volume_name, creator.get_volume().name)
927         self.assertEqual(self.volume_type_name,
928                          creator.volume_settings.type_name)
929         self.assertEqual(self.volume_type_name, creator.get_volume().type)
930         self.assertEqual(1, creator.volume_settings.size)
931         self.assertEqual(1, creator.get_volume().size)
932
933     def test_retrieve_volume_type_creator(self):
934         """
935         Tests the creation of an OpenStack stack from Heat template file and
936         the retrieval of an OpenStackVolume creator/state machine instance
937         """
938         volume_type_creators = self.stack_creator.get_volume_type_creators()
939         self.assertEqual(1, len(volume_type_creators))
940
941         creator = volume_type_creators[0]
942         self.assertIsNotNone(creator)
943
944         volume_type = creator.get_volume_type()
945         self.assertIsNotNone(volume_type)
946
947         self.assertEqual(self.volume_type_name, volume_type.name)
948         self.assertTrue(volume_type.public)
949         self.assertIsNone(volume_type.qos_spec)
950
951         # TODO - Add encryption back and find out why it broke in Pike
952         # encryption = volume_type.encryption
953         # self.assertIsNotNone(encryption)
954         # self.assertIsNone(encryption.cipher)
955         # self.assertEqual('front-end', encryption.control_location)
956         # self.assertIsNone(encryption.key_size)
957         # self.assertEqual(u'nova.volume.encryptors.luks.LuksEncryptor',
958         #                  encryption.provider)
959         # self.assertEqual(volume_type.id, encryption.volume_type_id)
960
961
962 class CreateStackFlavorTests(OSIntegrationTestCase):
963     """
964     Tests to ensure that floating IPs can be accessed via an
965     OpenStackFlavor object obtained from the OpenStackHeatStack instance
966     """
967
968     def setUp(self):
969
970         self.user_roles = ['heat_stack_owner', 'admin']
971
972         super(self.__class__, self).__start__()
973
974         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
975
976         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
977         self.stack_creator = None
978
979         self.heat_tmplt_path = pkg_resources.resource_filename(
980             'snaps.openstack.tests.heat', 'flavor_heat_template.yaml')
981
982         stack_settings = StackConfig(
983             name=self.guid + '-stack',
984             template_path=self.heat_tmplt_path)
985         self.stack_creator = OpenStackHeatStack(
986             self.os_creds, stack_settings)
987         self.created_stack = self.stack_creator.create(block=True)
988         self.assertIsNotNone(self.created_stack)
989
990     def tearDown(self):
991         """
992         Cleans the stack and downloaded stack file
993         """
994         if self.stack_creator:
995             try:
996                 self.stack_creator.clean()
997             except:
998                 pass
999
1000         super(self.__class__, self).__clean__()
1001
1002     def test_retrieve_flavor_creator(self):
1003         """
1004         Tests the creation of an OpenStack stack from Heat template file and
1005         the retrieval of an OpenStackVolume creator/state machine instance
1006         """
1007         flavor_creators = self.stack_creator.get_flavor_creators()
1008         self.assertEqual(1, len(flavor_creators))
1009
1010         creator = flavor_creators[0]
1011         self.assertTrue(creator.get_flavor().name.startswith(self.guid))
1012         self.assertEqual(1024, creator.get_flavor().ram)
1013         self.assertEqual(200, creator.get_flavor().disk)
1014         self.assertEqual(8, creator.get_flavor().vcpus)
1015         self.assertEqual(0, creator.get_flavor().ephemeral)
1016         self.assertIsNone(creator.get_flavor().swap)
1017         self.assertEqual(1.0, creator.get_flavor().rxtx_factor)
1018         self.assertTrue(creator.get_flavor().is_public)
1019
1020
1021 class CreateStackKeypairTests(OSIntegrationTestCase):
1022     """
1023     Tests to ensure that floating IPs can be accessed via an
1024     OpenStackKeypair object obtained from the OpenStackHeatStack instance
1025     """
1026
1027     def setUp(self):
1028
1029         self.user_roles = ['heat_stack_owner']
1030
1031         super(self.__class__, self).__start__()
1032
1033         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
1034
1035         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
1036         self.nova = nova_utils.nova_client(self.os_creds, self.os_session)
1037         self.stack_creator = None
1038
1039         self.keypair_name = self.guid + '-kp'
1040
1041         self.env_values = {
1042             'keypair_name': self.keypair_name}
1043
1044         self.heat_tmplt_path = pkg_resources.resource_filename(
1045             'snaps.openstack.tests.heat', 'keypair_heat_template.yaml')
1046
1047         stack_settings = StackConfig(
1048             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
1049             template_path=self.heat_tmplt_path,
1050             env_values=self.env_values)
1051         self.stack_creator = OpenStackHeatStack(
1052             self.os_creds, stack_settings)
1053         self.created_stack = self.stack_creator.create(block=True)
1054         self.assertIsNotNone(self.created_stack)
1055
1056         self.keypair_creators = list()
1057
1058     def tearDown(self):
1059         """
1060         Cleans the stack and downloaded stack file
1061         """
1062         if self.stack_creator:
1063             try:
1064                 self.stack_creator.clean()
1065             except:
1066                 pass
1067         for keypair_creator in self.keypair_creators:
1068             try:
1069                 keypair_creator.clean()
1070             except:
1071                 pass
1072
1073         super(self.__class__, self).__clean__()
1074
1075     def test_retrieve_keypair_creator(self):
1076         """
1077         Tests the creation of an OpenStack stack from Heat template file and
1078         the retrieval of an OpenStackKeypair creator/state machine instance
1079         """
1080         self.kp_creators = self.stack_creator.get_keypair_creators(
1081             'private_key')
1082         self.assertEqual(1, len(self.kp_creators))
1083
1084         self.keypair_creator = self.kp_creators[0]
1085
1086         self.assertEqual(self.keypair_name,
1087                          self.keypair_creator.get_keypair().name)
1088         self.assertIsNotNone(
1089             self.keypair_creator.keypair_settings.private_filepath)
1090
1091         private_file_contents = file_utils.read_file(
1092             self.keypair_creator.keypair_settings.private_filepath)
1093         self.assertTrue(private_file_contents.startswith(
1094             '-----BEGIN RSA PRIVATE KEY-----'))
1095
1096         keypair = nova_utils.get_keypair_by_id(
1097             self.nova, self.keypair_creator.get_keypair().id)
1098         self.assertIsNotNone(keypair)
1099         self.assertEqual(self.keypair_creator.get_keypair(), keypair)
1100
1101
1102 class CreateStackSecurityGroupTests(OSIntegrationTestCase):
1103     """
1104     Tests for the OpenStackHeatStack class to ensure it returns an
1105     OpenStackSecurityGroup object
1106     """
1107
1108     def setUp(self):
1109         """
1110         Instantiates the CreateStack object that is responsible for downloading
1111         and creating an OS stack file within OpenStack
1112         """
1113         self.user_roles = ['heat_stack_owner']
1114
1115         super(self.__class__, self).__start__()
1116
1117         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
1118
1119         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
1120         self.nova = nova_utils.nova_client(self.os_creds, self.os_session)
1121         self.stack_creator = None
1122
1123         self.security_group_name = self.guid + '-sec-grp'
1124
1125         self.env_values = {
1126             'security_group_name': self.security_group_name}
1127
1128         self.heat_tmplt_path = pkg_resources.resource_filename(
1129             'snaps.openstack.tests.heat', 'security_group_heat_template.yaml')
1130
1131         stack_settings = StackConfig(
1132             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
1133             template_path=self.heat_tmplt_path,
1134             env_values=self.env_values)
1135         self.stack_creator = OpenStackHeatStack(
1136             self.os_creds, stack_settings)
1137         self.created_stack = self.stack_creator.create(block=True)
1138         self.assertIsNotNone(self.created_stack)
1139
1140     def tearDown(self):
1141         """
1142         Cleans the stack and downloaded stack file
1143         """
1144         if self.stack_creator:
1145             try:
1146                 self.stack_creator.clean()
1147             except:
1148                 pass
1149
1150         super(self.__class__, self).__clean__()
1151
1152     def test_retrieve_security_group_creator(self):
1153         """
1154         Tests the creation of an OpenStack stack from Heat template file and
1155         the retrieval of an OpenStackSecurityGroup creator/state machine
1156         instance
1157         """
1158         sec_grp_creators = self.stack_creator.get_security_group_creators()
1159         self.assertEqual(1, len(sec_grp_creators))
1160
1161         creator = sec_grp_creators[0]
1162         sec_grp = creator.get_security_group()
1163
1164         self.assertEqual(self.security_group_name, sec_grp.name)
1165         self.assertEqual('Test description', sec_grp.description)
1166         self.assertEqual(2, len(sec_grp.rules))
1167
1168         has_ssh_rule = False
1169         has_icmp_rule = False
1170
1171         for rule in sec_grp.rules:
1172             if (rule.security_group_id == sec_grp.id
1173                     and rule.direction == 'egress'
1174                     and rule.ethertype == 'IPv4'
1175                     and rule.port_range_min == 22
1176                     and rule.port_range_max == 22
1177                     and rule.protocol == 'tcp'
1178                     and rule.remote_group_id is None
1179                     and rule.remote_ip_prefix == '0.0.0.0/0'):
1180                 has_ssh_rule = True
1181             if (rule.security_group_id == sec_grp.id
1182                     and rule.direction == 'ingress'
1183                     and rule.ethertype == 'IPv4'
1184                     and rule.port_range_min is None
1185                     and rule.port_range_max is None
1186                     and rule.protocol == 'icmp'
1187                     and rule.remote_group_id is None
1188                     and rule.remote_ip_prefix == '0.0.0.0/0'):
1189                 has_icmp_rule = True
1190
1191         self.assertTrue(has_ssh_rule)
1192         self.assertTrue(has_icmp_rule)
1193
1194
1195 class CreateStackNegativeTests(OSIntegrationTestCase):
1196     """
1197     Negative test cases for the OpenStackHeatStack class with poor
1198     configuration
1199     """
1200
1201     def setUp(self):
1202         self.user_roles = ['heat_stack_owner']
1203
1204         super(self.__class__, self).__start__()
1205
1206         self.stack_name = self.__class__.__name__ + '-' + str(uuid.uuid4())
1207         self.stack_creator = None
1208         self.heat_tmplt_path = pkg_resources.resource_filename(
1209             'snaps.openstack.tests.heat', 'test_heat_template.yaml')
1210
1211     def tearDown(self):
1212         if self.stack_creator:
1213             self.stack_creator.clean()
1214
1215         super(self.__class__, self).__clean__()
1216
1217     def test_missing_dependencies(self):
1218         """
1219         Expect an StackCreationError when the stack file does not exist
1220         """
1221         stack_settings = StackConfig(name=self.stack_name,
1222                                      template_path=self.heat_tmplt_path)
1223         self.stack_creator = OpenStackHeatStack(
1224             self.os_creds, stack_settings)
1225         with self.assertRaises(HTTPBadRequest):
1226             self.stack_creator.create(block=True)
1227
1228     def test_bad_stack_file(self):
1229         """
1230         Expect an StackCreationError when the stack file does not exist
1231         """
1232         stack_settings = StackConfig(
1233             name=self.stack_name, template_path='foo')
1234         self.stack_creator = OpenStackHeatStack(
1235             self.os_creds, stack_settings)
1236         with self.assertRaises(IOError):
1237             self.stack_creator.create(block=True)
1238
1239
1240 class CreateStackFailureTests(OSIntegrationTestCase):
1241     """
1242     Tests for the OpenStackHeatStack class defined in create_stack.py for
1243     when failures occur. Failures are being triggered by allocating 1 million
1244     CPUs.
1245     """
1246
1247     def setUp(self):
1248         self.user_roles = ['heat_stack_owner']
1249
1250         super(self.__class__, self).__start__()
1251
1252         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
1253
1254         self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
1255         self.stack_creator = None
1256
1257         self.tmp_file = file_utils.save_string_to_file(
1258             ' ', str(uuid.uuid4()) + '-bad-image')
1259         self.image_creator = OpenStackImage(
1260             self.os_creds, ImageConfig(
1261                 name=self.guid + 'image', image_file=self.tmp_file.name,
1262                 image_user='foo', img_format='qcow2'))
1263         self.image_creator.create()
1264
1265         # Create Flavor
1266         self.flavor_creator = OpenStackFlavor(
1267             self.admin_os_creds,
1268             FlavorConfig(
1269                 name=self.guid + '-flavor-name', ram=256, disk=10,
1270                 vcpus=1000000))
1271         self.flavor_creator.create()
1272
1273         self.network_name = self.guid + '-net'
1274         self.subnet_name = self.guid + '-subnet'
1275         self.vm_inst_name = self.guid + '-inst'
1276
1277         self.env_values = {
1278             'image_name': self.image_creator.image_settings.name,
1279             'flavor_name': self.flavor_creator.flavor_settings.name,
1280             'net_name': self.network_name,
1281             'subnet_name': self.subnet_name,
1282             'inst_name': self.vm_inst_name}
1283
1284         self.heat_tmplt_path = pkg_resources.resource_filename(
1285             'snaps.openstack.tests.heat', 'test_heat_template.yaml')
1286
1287     def tearDown(self):
1288         """
1289         Cleans the stack and downloaded stack file
1290         """
1291         if self.stack_creator:
1292             try:
1293                 self.stack_creator.clean()
1294             except:
1295                 pass
1296
1297         if self.image_creator:
1298             try:
1299                 self.image_creator.clean()
1300             except:
1301                 pass
1302
1303         if self.flavor_creator:
1304             try:
1305                 self.flavor_creator.clean()
1306             except:
1307                 pass
1308
1309         if self.tmp_file:
1310             try:
1311                 os.remove(self.tmp_file.name)
1312             except:
1313                 pass
1314
1315         super(self.__class__, self).__clean__()
1316
1317     def test_stack_failure(self):
1318         """
1319         Tests the creation of an OpenStack stack from Heat template file that
1320         should always fail due to too many CPU cores
1321         """
1322         # Create Stack
1323         # Set the default stack settings, then set any custom parameters sent
1324         # from the app
1325         stack_settings = StackConfig(
1326             name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
1327             template_path=self.heat_tmplt_path,
1328             env_values=self.env_values)
1329         self.stack_creator = OpenStackHeatStack(
1330             self.os_creds, stack_settings)
1331
1332         with self.assertRaises(StackError):
1333             try:
1334                 self.stack_creator.create(block=True)
1335             except StackError:
1336                 resources = heat_utils.get_resources(
1337                     self.heat_cli, self.stack_creator.get_stack().id)
1338
1339                 found = False
1340                 for resource in resources:
1341                     if (resource.status ==
1342                             snaps.config.stack.STATUS_CREATE_COMPLETE):
1343                         found = True
1344                 self.assertTrue(found)
1345                 raise