Pass flavor metadata into heat template
[snaps.git] / snaps / openstack / tests / create_stack_tests.py
index e665f82..7da5bc7 100644 (file)
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-import pkg_resources
+import os
 import time
 
+import pkg_resources
 from heatclient.exc import HTTPBadRequest
-from snaps import file_utils
-
-from snaps.openstack.create_flavor import OpenStackFlavor, FlavorSettings
 
+import snaps
+from snaps import file_utils
+from snaps.config.flavor import FlavorConfig
+from snaps.config.image import ImageConfig
+from snaps.config.stack import (StackConfigError, StackConfig,
+    STATUS_UPDATE_COMPLETE)
+from snaps.openstack.create_flavor import OpenStackFlavor
 from snaps.openstack.create_image import OpenStackImage
 
 try:
@@ -32,10 +37,12 @@ import unittest
 import uuid
 
 from snaps.openstack import create_stack
-from snaps.openstack.create_stack import StackSettings, StackSettingsError
-from snaps.openstack.tests import openstack_tests
+from snaps.openstack.create_stack import (
+    StackSettings, StackCreationError, StackError, OpenStackHeatStack)
+from snaps.openstack.tests import openstack_tests, create_instance_tests
 from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase
-from snaps.openstack.utils import heat_utils
+from snaps.openstack.utils import (
+    heat_utils, neutron_utils, nova_utils, keystone_utils)
 
 __author__ = 'spisarski'
 
@@ -46,37 +53,40 @@ class StackSettingsUnitTests(unittest.TestCase):
     """
     Tests the construction of the StackSettings class
     """
+
     def test_no_params(self):
-        with self.assertRaises(StackSettingsError):
+        with self.assertRaises(StackConfigError):
             StackSettings()
 
     def test_empty_config(self):
-        with self.assertRaises(StackSettingsError):
-            StackSettings(config=dict())
+        with self.assertRaises(StackConfigError):
+            StackSettings(**dict())
 
     def test_name_only(self):
-        with self.assertRaises(StackSettingsError):
+        with self.assertRaises(StackConfigError):
             StackSettings(name='foo')
 
     def test_config_with_name_only(self):
-        with self.assertRaises(StackSettingsError):
-            StackSettings(config={'name': 'foo'})
+        with self.assertRaises(StackConfigError):
+            StackSettings(**{'name': 'foo'})
 
     def test_config_minimum_template(self):
-        settings = StackSettings(config={'name': 'stack', 'template': 'foo'})
+        settings = StackSettings(**{'name': 'stack', 'template': 'foo'})
         self.assertEqual('stack', settings.name)
         self.assertEqual('foo', settings.template)
         self.assertIsNone(settings.template_path)
         self.assertIsNone(settings.env_values)
-        self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT, settings.stack_create_timeout)
+        self.assertEqual(snaps.config.stack.STACK_COMPLETE_TIMEOUT,
+                         settings.stack_create_timeout)
 
     def test_config_minimum_template_path(self):
-        settings = StackSettings(config={'name': 'stack', 'template_path': 'foo'})
+        settings = StackSettings(**{'name': 'stack', 'template_path': 'foo'})
         self.assertEqual('stack', settings.name)
         self.assertIsNone(settings.template)
         self.assertEqual('foo', settings.template_path)
         self.assertIsNone(settings.env_values)
-        self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT, settings.stack_create_timeout)
+        self.assertEqual(snaps.config.stack.STACK_COMPLETE_TIMEOUT,
+                         settings.stack_create_timeout)
 
     def test_minimum_template(self):
         settings = StackSettings(name='stack', template='foo')
@@ -84,7 +94,8 @@ class StackSettingsUnitTests(unittest.TestCase):
         self.assertEqual('foo', settings.template)
         self.assertIsNone(settings.template_path)
         self.assertIsNone(settings.env_values)
-        self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT, settings.stack_create_timeout)
+        self.assertEqual(snaps.config.stack.STACK_COMPLETE_TIMEOUT,
+                         settings.stack_create_timeout)
 
     def test_minimum_template_path(self):
         settings = StackSettings(name='stack', template_path='foo')
@@ -92,11 +103,13 @@ class StackSettingsUnitTests(unittest.TestCase):
         self.assertEqual('foo', settings.template_path)
         self.assertIsNone(settings.template)
         self.assertIsNone(settings.env_values)
-        self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT, settings.stack_create_timeout)
+        self.assertEqual(snaps.config.stack.STACK_COMPLETE_TIMEOUT,
+                         settings.stack_create_timeout)
 
     def test_all(self):
         env_values = {'foo': 'bar'}
-        settings = StackSettings(name='stack', template='bar', template_path='foo', env_values=env_values,
+        settings = StackSettings(name='stack', template='bar',
+                                 template_path='foo', env_values=env_values,
                                  stack_create_timeout=999)
         self.assertEqual('stack', settings.name)
         self.assertEqual('bar', settings.template)
@@ -107,8 +120,8 @@ class StackSettingsUnitTests(unittest.TestCase):
     def test_config_all(self):
         env_values = {'foo': 'bar'}
         settings = StackSettings(
-            config={'name': 'stack', 'template': 'bar', 'template_path': 'foo',
-                    'env_values': env_values, 'stack_create_timeout': 999})
+            **{'name': 'stack', 'template': 'bar', 'template_path': 'foo',
+               'env_values': env_values, 'stack_create_timeout': 999})
         self.assertEqual('stack', settings.name)
         self.assertEqual('bar', settings.template)
         self.assertEqual('foo', settings.template_path)
@@ -118,33 +131,43 @@ class StackSettingsUnitTests(unittest.TestCase):
 
 class CreateStackSuccessTests(OSIntegrationTestCase):
     """
-    Test for the CreateStack class defined in create_stack.py
+    Tests for the OpenStackHeatStack class defined in create_stack.py
     """
 
     def setUp(self):
-        """
-        Instantiates the CreateStack object that is responsible for downloading and creating an OS stack file
-        within OpenStack
-        """
+        self.user_roles = ['heat_stack_owner']
+
         super(self.__class__, self).__start__()
 
-        self.guid = str(uuid.uuid4())
-        self.heat_cli = heat_utils.heat_client(self.os_creds)
+        self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+
+        self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
         self.stack_creator = None
 
         self.image_creator = OpenStackImage(
             self.os_creds, openstack_tests.cirros_image_settings(
-                name=self.__class__.__name__ + '-' + str(self.guid) + '-image', image_metadata=self.image_metadata))
+                name=self.guid + '-image',
+                image_metadata=self.image_metadata))
         self.image_creator.create()
 
         # Create Flavor
+        flavor_config = openstack_tests.get_flavor_config(
+            name=self.guid + '-flavor-name', ram=256, disk=10,
+            vcpus=1, metadata=self.flavor_metadata)
         self.flavor_creator = OpenStackFlavor(
-            self.admin_os_creds,
-            FlavorSettings(name=self.guid + '-flavor-name', ram=128, disk=10, vcpus=1))
+            self.admin_os_creds, flavor_config)
         self.flavor_creator.create()
 
-        self.env_values = {'image_name': self.image_creator.image_settings.name,
-                           'flavor_name': self.flavor_creator.flavor_settings.name}
+        self.network_name = self.guid + '-net'
+        self.subnet_name = self.guid + '-subnet'
+        self.vm_inst_name = self.guid + '-inst'
+
+        self.env_values = {
+            'image_name': self.image_creator.image_settings.name,
+            'flavor_name': self.flavor_creator.flavor_settings.name,
+            'net_name': self.network_name,
+            'subnet_name': self.subnet_name,
+            'inst_name': self.vm_inst_name}
 
         self.heat_tmplt_path = pkg_resources.resource_filename(
             'snaps.openstack.tests.heat', 'test_heat_template.yaml')
@@ -178,64 +201,97 @@ class CreateStackSuccessTests(OSIntegrationTestCase):
         Tests the creation of an OpenStack stack from Heat template file.
         """
         # Create Stack
-        # Set the default stack settings, then set any custom parameters sent from the app
-        stack_settings = StackSettings(name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
-                                       template_path=self.heat_tmplt_path,
-                                       env_values=self.env_values)
-        self.stack_creator = create_stack.OpenStackHeatStack(self.os_creds, stack_settings)
-        created_stack = self.stack_creator.create()
+        # Set the default stack settings, then set any custom parameters sent
+        # from the app
+        stack_settings = StackConfig(
+            name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
+            template_path=self.heat_tmplt_path,
+            env_values=self.env_values)
+        self.stack_creator = OpenStackHeatStack(
+            self.os_creds, stack_settings)
+        created_stack = self.stack_creator.create(block=True)
         self.assertIsNotNone(created_stack)
 
-        retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli, created_stack.id)
+        retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
+                                                     created_stack.id)
         self.assertIsNotNone(retrieved_stack)
         self.assertEqual(created_stack.name, retrieved_stack.name)
         self.assertEqual(created_stack.id, retrieved_stack.id)
-        self.assertIsNotNone(self.stack_creator.get_outputs())
-        self.assertEquals(0, len(self.stack_creator.get_outputs()))
+        self.assertEqual(0, len(self.stack_creator.get_outputs()))
+
+        derived_creator = create_stack.generate_creator(
+            self.os_creds, retrieved_stack,
+            [self.image_creator.image_settings])
+        derived_stack = derived_creator.get_stack()
+        self.assertEqual(retrieved_stack, derived_stack)
+
+    def test_create_stack_short_timeout(self):
+        """
+        Tests the creation of an OpenStack stack from Heat template file.
+        """
+        # Create Stack
+        # Set the default stack settings, then set any custom parameters sent
+        # from the app
+        stack_settings = StackConfig(
+            name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
+            template_path=self.heat_tmplt_path,
+            env_values=self.env_values, stack_create_timeout=0)
+
+        self.stack_creator = OpenStackHeatStack(
+            self.os_creds, stack_settings)
+        with self.assertRaises(StackCreationError):
+            self.stack_creator.create(block=True)
 
     def test_create_stack_template_dict(self):
         """
         Tests the creation of an OpenStack stack from a heat dict() object.
         """
         # Create Stack
-        # Set the default stack settings, then set any custom parameters sent from the app
+        # Set the default stack settings, then set any custom parameters sent
+        # from the app
         template_dict = heat_utils.parse_heat_template_str(
             file_utils.read_file(self.heat_tmplt_path))
-        stack_settings = StackSettings(name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
-                                       template=template_dict,
-                                       env_values=self.env_values)
-        self.stack_creator = create_stack.OpenStackHeatStack(self.os_creds, stack_settings)
-        created_stack = self.stack_creator.create()
+        stack_settings = StackConfig(
+            name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
+            template=template_dict,
+            env_values=self.env_values)
+        self.stack_creator = OpenStackHeatStack(
+            self.os_creds, stack_settings)
+        created_stack = self.stack_creator.create(block=True)
         self.assertIsNotNone(created_stack)
 
-        retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli, created_stack.id)
+        retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
+                                                     created_stack.id)
         self.assertIsNotNone(retrieved_stack)
         self.assertEqual(created_stack.name, retrieved_stack.name)
         self.assertEqual(created_stack.id, retrieved_stack.id)
-        self.assertIsNotNone(self.stack_creator.get_outputs())
-        self.assertEquals(0, len(self.stack_creator.get_outputs()))
+        self.assertEqual(0, len(self.stack_creator.get_outputs()))
 
     def test_create_delete_stack(self):
         """
-        Tests the creation then deletion of an OpenStack stack to ensure clean() does not raise an Exception.
+        Tests the creation then deletion of an OpenStack stack to ensure
+        clean() does not raise an Exception.
         """
         # Create Stack
         template_dict = heat_utils.parse_heat_template_str(
             file_utils.read_file(self.heat_tmplt_path))
-        stack_settings = StackSettings(name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
-                                       template=template_dict,
-                                       env_values=self.env_values)
-        self.stack_creator = create_stack.OpenStackHeatStack(self.os_creds, stack_settings)
-        created_stack = self.stack_creator.create()
+        stack_settings = StackConfig(
+            name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
+            template=template_dict,
+            env_values=self.env_values)
+        self.stack_creator = OpenStackHeatStack(
+            self.os_creds, stack_settings)
+        created_stack = self.stack_creator.create(block=True)
         self.assertIsNotNone(created_stack)
 
-        retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli, created_stack.id)
+        retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
+                                                     created_stack.id)
         self.assertIsNotNone(retrieved_stack)
         self.assertEqual(created_stack.name, retrieved_stack.name)
         self.assertEqual(created_stack.id, retrieved_stack.id)
-        self.assertIsNotNone(self.stack_creator.get_outputs())
-        self.assertEquals(0, len(self.stack_creator.get_outputs()))
-        self.assertEqual(create_stack.STATUS_CREATE_COMPLETE, self.stack_creator.get_status())
+        self.assertEqual(0, len(self.stack_creator.get_outputs()))
+        self.assertEqual(snaps.config.stack.STATUS_CREATE_COMPLETE,
+                         self.stack_creator.get_status())
 
         # Delete Stack manually
         heat_utils.delete_stack(self.heat_cli, created_stack)
@@ -243,14 +299,16 @@ class CreateStackSuccessTests(OSIntegrationTestCase):
         end_time = time.time() + 90
         deleted = False
         while time.time() < end_time:
-            status = heat_utils.get_stack_status(self.heat_cli, retrieved_stack.id)
-            if status == create_stack.STATUS_DELETE_COMPLETE:
+            status = heat_utils.get_stack_status(self.heat_cli,
+                                                 retrieved_stack.id)
+            if status == snaps.config.stack.STATUS_DELETE_COMPLETE:
                 deleted = True
                 break
 
         self.assertTrue(deleted)
 
-        # Must not throw an exception when attempting to cleanup non-existent stack
+        # Must not throw an exception when attempting to cleanup non-existent
+        # stack
         self.stack_creator.clean()
         self.assertIsNone(self.stack_creator.get_stack())
 
@@ -261,31 +319,888 @@ class CreateStackSuccessTests(OSIntegrationTestCase):
         # Create Stack
         template_dict = heat_utils.parse_heat_template_str(
             file_utils.read_file(self.heat_tmplt_path))
-        stack_settings = StackSettings(name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
-                                       template=template_dict,
-                                       env_values=self.env_values)
-        self.stack_creator = create_stack.OpenStackHeatStack(self.os_creds, stack_settings)
-        created_stack1 = self.stack_creator.create()
+        stack_settings = StackConfig(
+            name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
+            template=template_dict,
+            env_values=self.env_values)
+        self.stack_creator = OpenStackHeatStack(
+            self.os_creds, stack_settings)
+        created_stack1 = self.stack_creator.create(block=True)
 
-        retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli, created_stack1.id)
+        retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
+                                                     created_stack1.id)
         self.assertIsNotNone(retrieved_stack)
         self.assertEqual(created_stack1.name, retrieved_stack.name)
         self.assertEqual(created_stack1.id, retrieved_stack.id)
-        self.assertIsNotNone(self.stack_creator.get_outputs())
         self.assertEqual(0, len(self.stack_creator.get_outputs()))
 
         # Should be retrieving the instance data
-        stack_creator2 = create_stack.OpenStackHeatStack(self.os_creds, stack_settings)
-        stack2 = stack_creator2.create()
+        stack_creator2 = OpenStackHeatStack(self.os_creds, stack_settings)
+        stack2 = stack_creator2.create(block=True)
         self.assertEqual(created_stack1.id, stack2.id)
 
+    def test_retrieve_network_creators(self):
+        """
+        Tests the creation of an OpenStack stack from Heat template file and
+        the retrieval of the network creator.
+        """
+        stack_settings = StackConfig(
+            name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
+            template_path=self.heat_tmplt_path,
+            env_values=self.env_values)
+        self.stack_creator = OpenStackHeatStack(
+            self.os_creds, stack_settings)
+        created_stack = self.stack_creator.create(block=True)
+        self.assertIsNotNone(created_stack)
+
+        net_creators = self.stack_creator.get_network_creators()
+        self.assertIsNotNone(net_creators)
+        self.assertEqual(1, len(net_creators))
+        self.assertEqual(self.network_name, net_creators[0].get_network().name)
+
+        # Need to use 'admin' creds as heat creates objects under it's own
+        # project/tenant
+        neutron = neutron_utils.neutron_client(
+            self.os_creds, self.os_session)
+        keystone = keystone_utils.keystone_client(
+            self.os_creds, self.os_session)
+        net_by_name = neutron_utils.get_network(
+            neutron, keystone, network_name=net_creators[0].get_network().name)
+        self.assertEqual(net_creators[0].get_network(), net_by_name)
+        self.assertIsNotNone(neutron_utils.get_network_by_id(
+            neutron, net_creators[0].get_network().id))
+
+        self.assertEqual(1, len(net_creators[0].get_network().subnets))
+        subnet = net_creators[0].get_network().subnets[0]
+        subnet_by_name = neutron_utils.get_subnet(
+            neutron, net_creators[0].get_network(), subnet_name=subnet.name)
+        self.assertEqual(subnet, subnet_by_name)
+
+        subnet_by_id = neutron_utils.get_subnet_by_id(neutron, subnet.id)
+        self.assertIsNotNone(subnet_by_id)
+        self.assertEqual(subnet_by_name, subnet_by_id)
+
+    def test_retrieve_vm_inst_creators(self):
+        """
+        Tests the creation of an OpenStack stack from Heat template file and
+        the retrieval of the network creator.
+        """
+        stack_settings = StackConfig(
+            name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
+            template_path=self.heat_tmplt_path,
+            env_values=self.env_values)
+        self.stack_creator = OpenStackHeatStack(
+            self.os_creds, stack_settings)
+        created_stack = self.stack_creator.create(block=True)
+        self.assertIsNotNone(created_stack)
+
+        vm_inst_creators = self.stack_creator.get_vm_inst_creators()
+        self.assertIsNotNone(vm_inst_creators)
+        self.assertEqual(1, len(vm_inst_creators))
+        self.assertEqual(self.vm_inst_name,
+                         vm_inst_creators[0].get_vm_inst().name)
+
+        nova = nova_utils.nova_client(self.os_creds, self.os_session)
+        neutron = neutron_utils.neutron_client(self.os_creds, self.os_session)
+        keystone = keystone_utils.keystone_client(self.os_creds, self.os_session)
+        vm_inst_by_name = nova_utils.get_server(
+            nova, neutron, keystone,
+            server_name=vm_inst_creators[0].get_vm_inst().name)
+
+        self.assertEqual(vm_inst_creators[0].get_vm_inst(), vm_inst_by_name)
+        self.assertIsNotNone(nova_utils.get_server_object_by_id(
+            nova, neutron, keystone, vm_inst_creators[0].get_vm_inst().id))
+
+
+class CreateStackFloatingIpTests(OSIntegrationTestCase):
+    """
+    Tests to ensure that floating IPs can be accessed via an
+    OpenStackVmInstance object obtained from the OpenStackHeatStack instance
+    """
+
+    def setUp(self):
+        self.user_roles = ['heat_stack_owner', 'admin']
+
+        super(self.__class__, self).__start__()
+
+        self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+
+        self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
+        self.stack_creator = None
+
+        self.image_creator = OpenStackImage(
+            self.os_creds, openstack_tests.cirros_image_settings(
+                name=self.guid + '-image',
+                image_metadata=self.image_metadata))
+        self.image_creator.create()
+
+        self.network_name = self.guid + '-net'
+        self.subnet_name = self.guid + '-subnet'
+        self.flavor1_name = self.guid + '-flavor1'
+        self.flavor2_name = self.guid + '-flavor2'
+        self.sec_grp_name = self.guid + '-sec_grp'
+        self.vm_inst1_name = self.guid + '-inst1'
+        self.vm_inst2_name = self.guid + '-inst2'
+        self.keypair_name = self.guid + '-kp'
+
+        self.env_values = {
+            'image1_name': self.image_creator.image_settings.name,
+            'image2_name': self.image_creator.image_settings.name,
+            'flavor1_name': self.flavor1_name,
+            'flavor2_name': self.flavor2_name,
+            'flavor_extra_specs': self.flavor_metadata,
+            'net_name': self.network_name,
+            'subnet_name': self.subnet_name,
+            'inst1_name': self.vm_inst1_name,
+            'inst2_name': self.vm_inst2_name,
+            'keypair_name': self.keypair_name,
+            'external_net_name': self.ext_net_name,
+            'security_group_name': self.sec_grp_name}
+
+        self.heat_tmplt_path = pkg_resources.resource_filename(
+            'snaps.openstack.tests.heat', 'floating_ip_heat_template.yaml')
+
+        self.vm_inst_creators = list()
+
+    def tearDown(self):
+        """
+        Cleans the stack and downloaded stack file
+        """
+        if self.stack_creator:
+            try:
+                self.stack_creator.clean()
+            except:
+                pass
+
+        if self.image_creator:
+            try:
+                self.image_creator.clean()
+            except:
+                pass
+
+        for vm_inst_creator in self.vm_inst_creators:
+            try:
+                keypair_settings = vm_inst_creator.keypair_settings
+                if keypair_settings and keypair_settings.private_filepath:
+                    expanded_path = os.path.expanduser(
+                        keypair_settings.private_filepath)
+                    os.chmod(expanded_path, 0o755)
+                    os.remove(expanded_path)
+            except:
+                pass
+
+        super(self.__class__, self).__clean__()
+
+    def test_connect_via_ssh_heat_vm(self):
+        """
+        Tests the creation of an OpenStack stack from Heat template file and
+        the retrieval of two VM instance creators and attempt to connect via
+        SSH to the first one with a floating IP.
+        """
+        stack_settings = StackConfig(
+            name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
+            template_path=self.heat_tmplt_path,
+            env_values=self.env_values)
+        self.stack_creator = OpenStackHeatStack(
+            self.os_creds, stack_settings,
+            [self.image_creator.image_settings])
+        created_stack = self.stack_creator.create(block=True)
+        self.assertIsNotNone(created_stack)
+
+        self.vm_inst_creators = self.stack_creator.get_vm_inst_creators(
+            heat_keypair_option='private_key')
+        self.assertIsNotNone(self.vm_inst_creators)
+        self.assertEqual(2, len(self.vm_inst_creators))
+
+        for vm_inst_creator in self.vm_inst_creators:
+            if vm_inst_creator.get_vm_inst().name == self.vm_inst1_name:
+                self.assertTrue(
+                    create_instance_tests.validate_ssh_client(vm_inst_creator))
+            else:
+                vm_settings = vm_inst_creator.instance_settings
+                self.assertEqual(0, len(vm_settings.floating_ip_settings))
+
+    def test_connect_via_ssh_heat_vm_derived(self):
+        """
+        Tests the the retrieval of two VM instance creators from a derived
+        OpenStackHeatStack object and attempt to connect via
+        SSH to the first one with a floating IP.
+        """
+        stack_settings = StackConfig(
+            name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
+            template_path=self.heat_tmplt_path,
+            env_values=self.env_values)
+        self.stack_creator = OpenStackHeatStack(
+            self.os_creds, stack_settings,
+            [self.image_creator.image_settings])
+        created_stack = self.stack_creator.create(block=True)
+        self.assertIsNotNone(created_stack)
+
+        derived_stack = create_stack.generate_creator(
+            self.os_creds, created_stack,
+            [self.image_creator.image_settings])
+
+        self.vm_inst_creators = derived_stack.get_vm_inst_creators(
+            heat_keypair_option='private_key')
+        self.assertIsNotNone(self.vm_inst_creators)
+        self.assertEqual(2, len(self.vm_inst_creators))
+
+        for vm_inst_creator in self.vm_inst_creators:
+            if vm_inst_creator.get_vm_inst().name == self.vm_inst1_name:
+                self.assertTrue(
+                    create_instance_tests.validate_ssh_client(vm_inst_creator))
+            else:
+                vm_settings = vm_inst_creator.instance_settings
+                self.assertEqual(0, len(vm_settings.floating_ip_settings))
+
+
+class CreateStackNestedResourceTests(OSIntegrationTestCase):
+    """
+    Tests to ensure that nested heat templates work
+    """
+
+    def setUp(self):
+        self.user_roles = ['heat_stack_owner']
+
+        super(self.__class__, self).__start__()
+
+        self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+
+        self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
+        self.stack_creator = None
+
+        self.image_creator = OpenStackImage(
+            self.os_creds, openstack_tests.cirros_image_settings(
+                name="{}-{}".format(self.guid, 'image'),
+                image_metadata=self.image_metadata))
+        self.image_creator.create()
+
+        flavor_config = openstack_tests.get_flavor_config(
+            name="{}-{}".format(self.guid, 'flavor-name'), ram=256, disk=10,
+            vcpus=1, metadata=self.flavor_metadata)
+        self.flavor_creator = OpenStackFlavor(
+            self.admin_os_creds, flavor_config)
+        self.flavor_creator.create()
+
+        env_values = {
+            'network_name': self.guid + '-network',
+            'public_network': self.ext_net_name,
+            'agent_image': self.image_creator.image_settings.name,
+            'agent_flavor': self.flavor_creator.flavor_settings.name,
+            'key_name': self.guid + '-key',
+        }
+
+        heat_tmplt_path = pkg_resources.resource_filename(
+            'snaps.openstack.tests.heat', 'agent-group.yaml')
+        heat_resource_path = pkg_resources.resource_filename(
+            'snaps.openstack.tests.heat', 'agent.yaml')
+
+        stack_settings = StackConfig(
+            name="{}-{}".format(
+                self.__class__.__name__, str(self.guid) + '-stack'),
+            template_path=heat_tmplt_path,
+            resource_files=[heat_resource_path],
+            env_values=env_values)
+
+        self.stack_creator = OpenStackHeatStack(
+            self.os_creds, stack_settings,
+            [self.image_creator.image_settings])
+
+        self.vm_inst_creators = list()
+
+    def tearDown(self):
+        """
+        Cleans the stack and downloaded stack file
+        """
+        if self.stack_creator:
+            try:
+                self.stack_creator.clean()
+            except:
+                pass
+
+        if self.image_creator:
+            try:
+                self.image_creator.clean()
+            except:
+                pass
+
+        if self.flavor_creator:
+            try:
+                self.flavor_creator.clean()
+            except:
+                pass
+
+        for vm_inst_creator in self.vm_inst_creators:
+            try:
+                keypair_settings = vm_inst_creator.keypair_settings
+                if keypair_settings and keypair_settings.private_filepath:
+                    expanded_path = os.path.expanduser(
+                        keypair_settings.private_filepath)
+                    os.chmod(expanded_path, 0o755)
+                    os.remove(expanded_path)
+            except:
+                pass
+
+        super(self.__class__, self).__clean__()
+
+    def test_nested(self):
+        """
+        Tests the creation of an OpenStack stack from Heat template file and
+        the retrieval of two VM instance creators and attempt to connect via
+        SSH to the first one with a floating IP.
+        """
+        created_stack = self.stack_creator.create(block=True)
+        self.assertIsNotNone(created_stack)
+
+        self.vm_inst_creators = self.stack_creator.get_vm_inst_creators(
+            heat_keypair_option='private_key')
+        self.assertIsNotNone(self.vm_inst_creators)
+        self.assertEqual(1, len(self.vm_inst_creators))
+
+        for vm_inst_creator in self.vm_inst_creators:
+            self.assertTrue(
+                create_instance_tests.validate_ssh_client(vm_inst_creator))
+
+
+class CreateStackUpdateTests(OSIntegrationTestCase):
+    """
+    Tests to ensure that stack update commands work
+    """
+
+    def setUp(self):
+        self.user_roles = ['heat_stack_owner']
+
+        super(self.__class__, self).__start__()
+
+        self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+
+        self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
+        self.stack_creator = None
+
+        self.image_creator = OpenStackImage(
+            self.os_creds, openstack_tests.cirros_image_settings(
+                name=self.guid + '-image',
+                image_metadata=self.image_metadata))
+        self.image_creator.create()
+
+        self.flavor_creator = OpenStackFlavor(
+            self.admin_os_creds,
+            FlavorConfig(
+                name=self.guid + '-flavor-name', ram=256, disk=10, vcpus=1))
+        self.flavor_creator.create()
+
+        env_values = {
+            'network_name': self.guid + '-network',
+            'public_network': self.ext_net_name,
+            'agent_image': self.image_creator.image_settings.name,
+            'agent_flavor': self.flavor_creator.flavor_settings.name,
+            'key_name': self.guid + '-key',
+        }
+
+        heat_tmplt_path = pkg_resources.resource_filename(
+            'snaps.openstack.tests.heat', 'agent-group.yaml')
+        heat_resource_path = pkg_resources.resource_filename(
+            'snaps.openstack.tests.heat', 'agent.yaml')
+
+        stack_settings = StackConfig(
+            name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
+            template_path=heat_tmplt_path,
+            resource_files=[heat_resource_path],
+            env_values=env_values)
+
+        self.stack_creator = OpenStackHeatStack(
+            self.os_creds, stack_settings,
+            [self.image_creator.image_settings])
+
+        self.vm_inst_creators = list()
+
+    def tearDown(self):
+        """
+        Cleans the stack and downloaded stack file
+        """
+        if self.stack_creator:
+            try:
+                self.stack_creator.clean()
+            except:
+                pass
+
+        if self.image_creator:
+            try:
+                self.image_creator.clean()
+            except:
+                pass
+
+        if self.flavor_creator:
+            try:
+                self.flavor_creator.clean()
+            except:
+                pass
+
+        for vm_inst_creator in self.vm_inst_creators:
+            try:
+                keypair_settings = vm_inst_creator.keypair_settings
+                if keypair_settings and keypair_settings.private_filepath:
+                    expanded_path = os.path.expanduser(
+                        keypair_settings.private_filepath)
+                    os.chmod(expanded_path, 0o755)
+                    os.remove(expanded_path)
+            except:
+                pass
+
+        super(self.__class__, self).__clean__()
+
+    def test_update(self):
+        """
+        Tests the update of an OpenStack stack from Heat template file
+        by changing the number of VM instances from 1 to 2, and
+        the retrieval of two VM instance creators and attempt to connect via
+        SSH to the first one with a floating IP.
+        """
+        created_stack = self.stack_creator.create(block=True)
+        self.assertIsNotNone(created_stack)
+
+        self.vm_inst_creators = self.stack_creator.get_vm_inst_creators(
+            heat_keypair_option='private_key')
+        self.assertIsNotNone(self.vm_inst_creators)
+        self.assertEqual(1, len(self.vm_inst_creators))
+
+        for vm_inst_creator in self.vm_inst_creators:
+            self.assertTrue(
+                create_instance_tests.validate_ssh_client(vm_inst_creator))
+
+        env_values = {
+            'network_name': self.guid + '-network',
+            'public_network': self.ext_net_name,
+            'agent_count': 2,
+            'agent_image': self.image_creator.image_settings.name,
+            'agent_flavor': self.flavor_creator.flavor_settings.name,
+            'key_name': self.guid + '-key',
+        }
+
+        updated_stack = self.stack_creator.update(env_values, block=True)
+        self.assertIsNotNone(updated_stack)
+        self.assertEqual(STATUS_UPDATE_COMPLETE, updated_stack.status)
+
+        self.vm_inst_creators = self.stack_creator.get_vm_inst_creators(
+            heat_keypair_option='private_key')
+        self.assertIsNotNone(self.vm_inst_creators)
+        self.assertEqual(2, len(self.vm_inst_creators))
+
+        for vm_inst_creator in self.vm_inst_creators:
+            self.assertTrue(
+                create_instance_tests.validate_ssh_client(vm_inst_creator))
+
+
+class CreateStackRouterTests(OSIntegrationTestCase):
+    """
+    Tests for the CreateStack class defined in create_stack.py where the
+    target is a Network, Subnet, and Router
+    """
+
+    def setUp(self):
+        """
+        Instantiates the CreateStack object that is responsible for downloading
+        and creating an OS stack file within OpenStack
+        """
+        self.user_roles = ['heat_stack_owner']
+
+        super(self.__class__, self).__start__()
+
+        self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+
+        self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
+        self.neutron = neutron_utils.neutron_client(
+            self.os_creds, self.os_session)
+        self.stack_creator = None
+
+        self.net_name = self.guid + '-net'
+        self.subnet_name = self.guid + '-subnet'
+        self.router_name = self.guid + '-router'
+
+        self.env_values = {
+            'net_name': self.net_name,
+            'subnet_name': self.subnet_name,
+            'router_name': self.router_name,
+            'external_net_name': self.ext_net_name}
+
+        self.heat_tmplt_path = pkg_resources.resource_filename(
+            'snaps.openstack.tests.heat', 'router_heat_template.yaml')
+
+        stack_settings = StackConfig(
+            name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
+            template_path=self.heat_tmplt_path,
+            env_values=self.env_values)
+        self.stack_creator = OpenStackHeatStack(
+            self.os_creds, stack_settings)
+        self.created_stack = self.stack_creator.create(block=True)
+        self.assertIsNotNone(self.created_stack)
+
+    def tearDown(self):
+        """
+        Cleans the stack and downloaded stack file
+        """
+        if self.stack_creator:
+            try:
+                self.stack_creator.clean()
+            except:
+                pass
+
+        super(self.__class__, self).__clean__()
+
+    def test_retrieve_router_creator(self):
+        """
+        Tests the creation of an OpenStack stack from Heat template file and
+        the retrieval of an OpenStackRouter creator/state machine instance
+        """
+        router_creators = self.stack_creator.get_router_creators()
+        self.assertEqual(1, len(router_creators))
+
+        creator = router_creators[0]
+        self.assertEqual(self.router_name, creator.router_settings.name)
+
+        router = creator.get_router()
+
+        ext_net = neutron_utils.get_network(
+            self.neutron, self.keystone, network_name=self.ext_net_name)
+        self.assertEqual(ext_net.id, router.external_network_id)
+
+
+class CreateStackVolumeTests(OSIntegrationTestCase):
+    """
+    Tests to ensure that floating IPs can be accessed via an
+    OpenStackVolume object obtained from the OpenStackHeatStack instance
+    """
+
+    def setUp(self):
+
+        self.user_roles = ['heat_stack_owner', 'admin']
+
+        super(self.__class__, self).__start__()
+
+        self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+
+        self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
+        self.stack_creator = None
+
+        self.volume_name = self.guid + '-volume'
+        self.volume_type_name = self.guid + '-volume-type'
+
+        self.env_values = {
+            'volume_name': self.volume_name,
+            'volume_type_name': self.volume_type_name}
+
+        self.heat_tmplt_path = pkg_resources.resource_filename(
+            'snaps.openstack.tests.heat', 'volume_heat_template.yaml')
+
+        stack_settings = StackConfig(
+            name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
+            template_path=self.heat_tmplt_path,
+            env_values=self.env_values)
+        self.stack_creator = OpenStackHeatStack(
+            self.os_creds, stack_settings)
+        self.created_stack = self.stack_creator.create(block=True)
+        self.assertIsNotNone(self.created_stack)
+
+    def tearDown(self):
+        """
+        Cleans the stack and downloaded stack file
+        """
+        if self.stack_creator:
+            try:
+                self.stack_creator.clean()
+            except:
+                pass
+
+        super(self.__class__, self).__clean__()
+
+    def test_retrieve_volume_creator(self):
+        """
+        Tests the creation of an OpenStack stack from Heat template file and
+        the retrieval of an OpenStackVolume creator/state machine instance
+        """
+        volume_creators = self.stack_creator.get_volume_creators()
+        self.assertEqual(1, len(volume_creators))
+
+        creator = volume_creators[0]
+        self.assertEqual(self.volume_name, creator.volume_settings.name)
+        self.assertEqual(self.volume_name, creator.get_volume().name)
+        self.assertEqual(self.volume_type_name,
+                         creator.volume_settings.type_name)
+        self.assertEqual(self.volume_type_name, creator.get_volume().type)
+        self.assertEqual(1, creator.volume_settings.size)
+        self.assertEqual(1, creator.get_volume().size)
+
+    def test_retrieve_volume_type_creator(self):
+        """
+        Tests the creation of an OpenStack stack from Heat template file and
+        the retrieval of an OpenStackVolume creator/state machine instance
+        """
+        volume_type_creators = self.stack_creator.get_volume_type_creators()
+        self.assertEqual(1, len(volume_type_creators))
+
+        creator = volume_type_creators[0]
+        self.assertIsNotNone(creator)
+
+        volume_type = creator.get_volume_type()
+        self.assertIsNotNone(volume_type)
+
+        self.assertEqual(self.volume_type_name, volume_type.name)
+        self.assertTrue(volume_type.public)
+        self.assertIsNone(volume_type.qos_spec)
+
+        # TODO - Add encryption back and find out why it broke in Pike
+        # encryption = volume_type.encryption
+        # self.assertIsNotNone(encryption)
+        # self.assertIsNone(encryption.cipher)
+        # self.assertEqual('front-end', encryption.control_location)
+        # self.assertIsNone(encryption.key_size)
+        # self.assertEqual(u'nova.volume.encryptors.luks.LuksEncryptor',
+        #                  encryption.provider)
+        # self.assertEqual(volume_type.id, encryption.volume_type_id)
+
+
+class CreateStackFlavorTests(OSIntegrationTestCase):
+    """
+    Tests to ensure that floating IPs can be accessed via an
+    OpenStackFlavor object obtained from the OpenStackHeatStack instance
+    """
+
+    def setUp(self):
+
+        self.user_roles = ['heat_stack_owner', 'admin']
+
+        super(self.__class__, self).__start__()
+
+        self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+
+        self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
+        self.stack_creator = None
+
+        self.heat_tmplt_path = pkg_resources.resource_filename(
+            'snaps.openstack.tests.heat', 'flavor_heat_template.yaml')
+
+        stack_settings = StackConfig(
+            name=self.guid + '-stack',
+            template_path=self.heat_tmplt_path)
+        self.stack_creator = OpenStackHeatStack(
+            self.os_creds, stack_settings)
+        self.created_stack = self.stack_creator.create(block=True)
+        self.assertIsNotNone(self.created_stack)
+
+    def tearDown(self):
+        """
+        Cleans the stack and downloaded stack file
+        """
+        if self.stack_creator:
+            try:
+                self.stack_creator.clean()
+            except:
+                pass
+
+        super(self.__class__, self).__clean__()
+
+    def test_retrieve_flavor_creator(self):
+        """
+        Tests the creation of an OpenStack stack from Heat template file and
+        the retrieval of an OpenStackVolume creator/state machine instance
+        """
+        flavor_creators = self.stack_creator.get_flavor_creators()
+        self.assertEqual(1, len(flavor_creators))
+
+        creator = flavor_creators[0]
+        self.assertTrue(creator.get_flavor().name.startswith(self.guid))
+        self.assertEqual(1024, creator.get_flavor().ram)
+        self.assertEqual(200, creator.get_flavor().disk)
+        self.assertEqual(8, creator.get_flavor().vcpus)
+        self.assertEqual(0, creator.get_flavor().ephemeral)
+        self.assertIsNone(creator.get_flavor().swap)
+        self.assertEqual(1.0, creator.get_flavor().rxtx_factor)
+        self.assertTrue(creator.get_flavor().is_public)
+
+
+class CreateStackKeypairTests(OSIntegrationTestCase):
+    """
+    Tests to ensure that floating IPs can be accessed via an
+    OpenStackKeypair object obtained from the OpenStackHeatStack instance
+    """
+
+    def setUp(self):
+
+        self.user_roles = ['heat_stack_owner']
+
+        super(self.__class__, self).__start__()
+
+        self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+
+        self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
+        self.nova = nova_utils.nova_client(self.os_creds, self.os_session)
+        self.stack_creator = None
+
+        self.keypair_name = self.guid + '-kp'
+
+        self.env_values = {
+            'keypair_name': self.keypair_name}
+
+        self.heat_tmplt_path = pkg_resources.resource_filename(
+            'snaps.openstack.tests.heat', 'keypair_heat_template.yaml')
+
+        stack_settings = StackConfig(
+            name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
+            template_path=self.heat_tmplt_path,
+            env_values=self.env_values)
+        self.stack_creator = OpenStackHeatStack(
+            self.os_creds, stack_settings)
+        self.created_stack = self.stack_creator.create(block=True)
+        self.assertIsNotNone(self.created_stack)
+
+        self.keypair_creators = list()
+
+    def tearDown(self):
+        """
+        Cleans the stack and downloaded stack file
+        """
+        if self.stack_creator:
+            try:
+                self.stack_creator.clean()
+            except:
+                pass
+        for keypair_creator in self.keypair_creators:
+            try:
+                keypair_creator.clean()
+            except:
+                pass
+
+        super(self.__class__, self).__clean__()
+
+    def test_retrieve_keypair_creator(self):
+        """
+        Tests the creation of an OpenStack stack from Heat template file and
+        the retrieval of an OpenStackKeypair creator/state machine instance
+        """
+        self.kp_creators = self.stack_creator.get_keypair_creators(
+            'private_key')
+        self.assertEqual(1, len(self.kp_creators))
+
+        self.keypair_creator = self.kp_creators[0]
+
+        self.assertEqual(self.keypair_name,
+                         self.keypair_creator.get_keypair().name)
+        self.assertIsNotNone(
+            self.keypair_creator.keypair_settings.private_filepath)
+
+        private_file_contents = file_utils.read_file(
+            self.keypair_creator.keypair_settings.private_filepath)
+        self.assertTrue(private_file_contents.startswith(
+            '-----BEGIN RSA PRIVATE KEY-----'))
+
+        keypair = nova_utils.get_keypair_by_id(
+            self.nova, self.keypair_creator.get_keypair().id)
+        self.assertIsNotNone(keypair)
+        self.assertEqual(self.keypair_creator.get_keypair(), keypair)
+
+
+class CreateStackSecurityGroupTests(OSIntegrationTestCase):
+    """
+    Tests for the OpenStackHeatStack class to ensure it returns an
+    OpenStackSecurityGroup object
+    """
+
+    def setUp(self):
+        """
+        Instantiates the CreateStack object that is responsible for downloading
+        and creating an OS stack file within OpenStack
+        """
+        self.user_roles = ['heat_stack_owner']
+
+        super(self.__class__, self).__start__()
+
+        self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+
+        self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
+        self.nova = nova_utils.nova_client(self.os_creds, self.os_session)
+        self.stack_creator = None
+
+        self.security_group_name = self.guid + '-sec-grp'
+
+        self.env_values = {
+            'security_group_name': self.security_group_name}
+
+        self.heat_tmplt_path = pkg_resources.resource_filename(
+            'snaps.openstack.tests.heat', 'security_group_heat_template.yaml')
+
+        stack_settings = StackConfig(
+            name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
+            template_path=self.heat_tmplt_path,
+            env_values=self.env_values)
+        self.stack_creator = OpenStackHeatStack(
+            self.os_creds, stack_settings)
+        self.created_stack = self.stack_creator.create(block=True)
+        self.assertIsNotNone(self.created_stack)
+
+    def tearDown(self):
+        """
+        Cleans the stack and downloaded stack file
+        """
+        if self.stack_creator:
+            try:
+                self.stack_creator.clean()
+            except:
+                pass
+
+        super(self.__class__, self).__clean__()
+
+    def test_retrieve_security_group_creator(self):
+        """
+        Tests the creation of an OpenStack stack from Heat template file and
+        the retrieval of an OpenStackSecurityGroup creator/state machine
+        instance
+        """
+        sec_grp_creators = self.stack_creator.get_security_group_creators()
+        self.assertEqual(1, len(sec_grp_creators))
+
+        creator = sec_grp_creators[0]
+        sec_grp = creator.get_security_group()
+
+        self.assertEqual(self.security_group_name, sec_grp.name)
+        self.assertEqual('Test description', sec_grp.description)
+        self.assertEqual(2, len(sec_grp.rules))
+
+        has_ssh_rule = False
+        has_icmp_rule = False
+
+        for rule in sec_grp.rules:
+            if (rule.security_group_id == sec_grp.id
+                    and rule.direction == 'egress'
+                    and rule.ethertype == 'IPv4'
+                    and rule.port_range_min == 22
+                    and rule.port_range_max == 22
+                    and rule.protocol == 'tcp'
+                    and rule.remote_group_id is None
+                    and rule.remote_ip_prefix == '0.0.0.0/0'):
+                has_ssh_rule = True
+            if (rule.security_group_id == sec_grp.id
+                    and rule.direction == 'ingress'
+                    and rule.ethertype == 'IPv4'
+                    and rule.port_range_min is None
+                    and rule.port_range_max is None
+                    and rule.protocol == 'icmp'
+                    and rule.remote_group_id is None
+                    and rule.remote_ip_prefix == '0.0.0.0/0'):
+                has_icmp_rule = True
+
+        self.assertTrue(has_ssh_rule)
+        self.assertTrue(has_icmp_rule)
+
 
 class CreateStackNegativeTests(OSIntegrationTestCase):
     """
-    Negative test cases for the CreateStack class
+    Negative test cases for the OpenStackHeatStack class with poor
+    configuration
     """
 
     def setUp(self):
+        self.user_roles = ['heat_stack_owner']
+
         super(self.__class__, self).__start__()
 
         self.stack_name = self.__class__.__name__ + '-' + str(uuid.uuid4())
@@ -296,22 +1211,135 @@ class CreateStackNegativeTests(OSIntegrationTestCase):
     def tearDown(self):
         if self.stack_creator:
             self.stack_creator.clean()
+
         super(self.__class__, self).__clean__()
 
     def test_missing_dependencies(self):
         """
         Expect an StackCreationError when the stack file does not exist
         """
-        stack_settings = StackSettings(name=self.stack_name, template_path=self.heat_tmplt_path)
-        self.stack_creator = create_stack.OpenStackHeatStack(self.os_creds, stack_settings)
+        stack_settings = StackConfig(name=self.stack_name,
+                                     template_path=self.heat_tmplt_path)
+        self.stack_creator = OpenStackHeatStack(
+            self.os_creds, stack_settings)
         with self.assertRaises(HTTPBadRequest):
-            self.stack_creator.create()
+            self.stack_creator.create(block=True)
 
     def test_bad_stack_file(self):
         """
         Expect an StackCreationError when the stack file does not exist
         """
-        stack_settings = StackSettings(name=self.stack_name, template_path='foo')
-        self.stack_creator = create_stack.OpenStackHeatStack(self.os_creds, stack_settings)
+        stack_settings = StackConfig(
+            name=self.stack_name, template_path='foo')
+        self.stack_creator = OpenStackHeatStack(
+            self.os_creds, stack_settings)
         with self.assertRaises(IOError):
-            self.stack_creator.create()
+            self.stack_creator.create(block=True)
+
+
+class CreateStackFailureTests(OSIntegrationTestCase):
+    """
+    Tests for the OpenStackHeatStack class defined in create_stack.py for
+    when failures occur. Failures are being triggered by allocating 1 million
+    CPUs.
+    """
+
+    def setUp(self):
+        self.user_roles = ['heat_stack_owner']
+
+        super(self.__class__, self).__start__()
+
+        self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+
+        self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session)
+        self.stack_creator = None
+
+        self.tmp_file = file_utils.save_string_to_file(
+            ' ', str(uuid.uuid4()) + '-bad-image')
+        self.image_creator = OpenStackImage(
+            self.os_creds, ImageConfig(
+                name=self.guid + 'image', image_file=self.tmp_file.name,
+                image_user='foo', img_format='qcow2'))
+        self.image_creator.create()
+
+        # Create Flavor
+        self.flavor_creator = OpenStackFlavor(
+            self.admin_os_creds,
+            FlavorConfig(
+                name=self.guid + '-flavor-name', ram=256, disk=10,
+                vcpus=1000000))
+        self.flavor_creator.create()
+
+        self.network_name = self.guid + '-net'
+        self.subnet_name = self.guid + '-subnet'
+        self.vm_inst_name = self.guid + '-inst'
+
+        self.env_values = {
+            'image_name': self.image_creator.image_settings.name,
+            'flavor_name': self.flavor_creator.flavor_settings.name,
+            'net_name': self.network_name,
+            'subnet_name': self.subnet_name,
+            'inst_name': self.vm_inst_name}
+
+        self.heat_tmplt_path = pkg_resources.resource_filename(
+            'snaps.openstack.tests.heat', 'test_heat_template.yaml')
+
+    def tearDown(self):
+        """
+        Cleans the stack and downloaded stack file
+        """
+        if self.stack_creator:
+            try:
+                self.stack_creator.clean()
+            except:
+                pass
+
+        if self.image_creator:
+            try:
+                self.image_creator.clean()
+            except:
+                pass
+
+        if self.flavor_creator:
+            try:
+                self.flavor_creator.clean()
+            except:
+                pass
+
+        if self.tmp_file:
+            try:
+                os.remove(self.tmp_file.name)
+            except:
+                pass
+
+        super(self.__class__, self).__clean__()
+
+    def test_stack_failure(self):
+        """
+        Tests the creation of an OpenStack stack from Heat template file that
+        should always fail due to too many CPU cores
+        """
+        # Create Stack
+        # Set the default stack settings, then set any custom parameters sent
+        # from the app
+        stack_settings = StackConfig(
+            name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
+            template_path=self.heat_tmplt_path,
+            env_values=self.env_values)
+        self.stack_creator = OpenStackHeatStack(
+            self.os_creds, stack_settings)
+
+        with self.assertRaises(StackError):
+            try:
+                self.stack_creator.create(block=True)
+            except StackError:
+                resources = heat_utils.get_resources(
+                    self.heat_cli, self.stack_creator.get_stack().id)
+
+                found = False
+                for resource in resources:
+                    if (resource.status ==
+                            snaps.config.stack.STATUS_CREATE_COMPLETE):
+                        found = True
+                self.assertTrue(found)
+                raise