Merge "Setup IPSEC tunnel mode for VPP Crypto testing"
[yardstick.git] / yardstick / tests / unit / orchestrator / test_heat.py
index faf70cd..2e60a72 100644 (file)
@@ -1,5 +1,3 @@
-#!/usr/bin/env python
-
 ##############################################################################
 # Copyright (c) 2017 Intel Corporation
 #
 # http://www.apache.org/licenses/LICENSE-2.0
 ##############################################################################
 
-# Unittest for yardstick.benchmark.orchestrator.heat
-from contextlib import contextmanager
-from itertools import count
-from tempfile import NamedTemporaryFile
-import time
-import uuid
+import tempfile
 
+import munch
 import mock
+from oslo_serialization import jsonutils
+from oslo_utils import uuidutils
+import shade
 import unittest
 
 from yardstick.benchmark.contexts import node
+from yardstick.common import constants
+from yardstick.common import exceptions
 from yardstick.orchestrator import heat
 
 
-TARGET_MODULE = 'yardstick.orchestrator.heat'
-
-
-def mock_patch_target_module(inner_import):
-    return mock.patch('.'.join([TARGET_MODULE, inner_import]))
-
-
-@contextmanager
-def timer():
-    start = time.time()
-    data = {'start': start}
-    try:
-        yield data
-    finally:
-        data['end'] = end = time.time()
-        data['delta'] = end - start
+class FakeStack(object):
 
+    def __init__(self, outputs=None, status=None, id=None):
+        self.outputs = outputs
+        self.status = status
+        self.id = id
 
-def index_value_iter(index, index_value, base_value=None):
-    for current_index in count():
-        if current_index == index:
-            yield index_value
-        else:
-            yield base_value
 
+class HeatStackTestCase(unittest.TestCase):
 
-def get_error_message(error):
-    try:
-        # py2
-        return error.message
-    except AttributeError:
-        # py3
-        return next((arg for arg in error.args if isinstance(arg, str)), None)
+    def setUp(self):
+        self.stack_name = 'STACK NAME'
+        with mock.patch.object(shade, 'openstack_cloud'):
+            self.heatstack = heat.HeatStack(self.stack_name)
+        self._mock_stack_create = mock.patch.object(self.heatstack._cloud,
+                                                    'create_stack')
+        self.mock_stack_create = self._mock_stack_create.start()
+        self._mock_stack_delete = mock.patch.object(self.heatstack._cloud,
+                                                    'delete_stack')
+        self.mock_stack_delete = self._mock_stack_delete.start()
+        self._mock_stack_get = mock.patch.object(self.heatstack._cloud,
+                                                 'get_stack')
+        self.mock_stack_get = self._mock_stack_get.start()
+
+        self.addCleanup(self._cleanup)
+
+    def _cleanup(self):
+        self._mock_stack_create.stop()
+        self._mock_stack_delete.stop()
+        self._mock_stack_get.stop()
+        heat._DEPLOYED_STACKS = {}
+
+    @mock.patch.object(shade, 'openstack_cloud')
+    def test__init(self, mock_openstack_cloud):
+        os_cloud_config = {'key': 'value'}
+        heatstack = heat.HeatStack('name', os_cloud_config=os_cloud_config)
+        self.assertEqual('name', heatstack.name)
+        os_cloud_config.update(constants.OS_CLOUD_DEFAULT_CONFIG)
+        mock_openstack_cloud.assert_called_once_with(**os_cloud_config)
+
+    def test_create(self):
+        template = {'tkey': 'tval'}
+        heat_parameters = {'pkey': 'pval'}
+        outputs = [{'output_key': 'okey', 'output_value': 'oval'}]
+        id = uuidutils.generate_uuid()
+        self.mock_stack_create.return_value = FakeStack(
+            outputs=outputs, status=mock.Mock(), id=id)
+        mock_tfile = mock.Mock()
+        with mock.patch.object(tempfile._TemporaryFileWrapper, '__enter__',
+                               return_value=mock_tfile):
+            self.heatstack.create(template, heat_parameters, True, 100)
+            mock_tfile.write.assert_called_once_with(jsonutils.dump_as_bytes(template))
+            mock_tfile.close.assert_called_once()
+
+        self.mock_stack_create.assert_called_once_with(
+            self.stack_name, template_file=mock_tfile.name, wait=True,
+            timeout=100, pkey='pval')
+        self.assertEqual({'okey': 'oval'}, self.heatstack.outputs)
+        self.assertEqual(heat._DEPLOYED_STACKS[id], self.heatstack._stack)
+
+    def test_stacks_exist(self):
+        self.assertEqual(0, self.heatstack.stacks_exist())
+        heat._DEPLOYED_STACKS['id'] = 'stack'
+        self.assertEqual(1, self.heatstack.stacks_exist())
+
+    def test_delete_not_uuid(self):
+        self.assertIsNone(self.heatstack.delete())
+
+    def test_delete_existing_uuid(self):
+        id = uuidutils.generate_uuid()
+        self.heatstack._stack = FakeStack(
+            outputs=mock.Mock(), status=mock.Mock(), id=id)
+        heat._DEPLOYED_STACKS[id] = self.heatstack._stack
+        delete_return = mock.Mock()
+        self.mock_stack_delete.return_value = delete_return
+
+        ret = self.heatstack.delete(wait=True)
+        self.assertEqual(delete_return, ret)
+        self.assertFalse(heat._DEPLOYED_STACKS)
+        self.mock_stack_delete.assert_called_once_with(id, wait=True)
+
+    def test_delete_bug_in_shade(self):
+        id = uuidutils.generate_uuid()
+        self.heatstack._stack = FakeStack(
+            outputs=mock.Mock(), status=mock.Mock(), id=id)
+        heat._DEPLOYED_STACKS[id] = self.heatstack._stack
+        self.mock_stack_delete.side_effect = TypeError()
+
+        ret = self.heatstack.delete(wait=True)
+        self.assertTrue(ret)
+        self.assertFalse(heat._DEPLOYED_STACKS)
+        self.mock_stack_delete.assert_called_once_with(id, wait=True)
+
+    def test_get(self):
+        # make sure shade/get_stack is called with the appropriate vars
+        self.mock_stack_get.return_value = munch.Munch(
+            id="my-existing-stack-id",
+            outputs=[
+                {
+                 u'output_value': u'b734d06a-dec7-...',
+                 u'output_key': u'ares.demo-test-port-network_id',
+                 u'description': u''
+                },
+                {u'output_value': u'b08da78c-2218-...',
+                 u'output_key': u'ares.demo-test-port-subnet_id',
+                 u'description': u''
+                },
+                {u'output_value': u'10.0.1.0/24',
+                 u'output_key': u'demo-test-subnet-cidr',
+                 u'description': u''
+                },
+                {u'output_value': u'b08da78c-2218-...',
+                 u'output_key': u'demo-test-subnet',
+                 u'description': u''
+                },
+                {u'output_value': u'b1a03624-aefc-...',
+                 u'output_key': u'ares.demo',
+                 u'description': u''
+                },
+                {u'output_value': u'266a8088-c630-...',
+                 u'output_key': u'demo-secgroup',
+                 u'description': u''
+                },
+                {u'output_value': u'10.0.1.5',
+                 u'output_key': u'ares.demo-test-port',
+                 u'description': u''
+                },
+                {u'output_value': u'10.0.1.1',
+                 u'output_key': u'demo-test-subnet-gateway_ip',
+                 u'description': u''
+                },
+                {u'output_value': u'',
+                 u'output_key': u'ares.demo-test-port-device_id',
+                 u'description': u''
+                },
+                {u'output_value': u'172.24.4.7',
+                 u'output_key': u'ares.demo-fip',
+                 u'description': u''
+                },
+                {u'output_value': u'fa:16:3e:6c:c3:0f',
+                 u'output_key': u'ares.demo-test-port-mac_address',
+                 u'description': u''}
+            ]
+        )
+        expected_outputs = {
+            'ares.demo-test-port-network_id': 'b734d06a-dec7-...',
+            'ares.demo-test-port-subnet_id': 'b08da78c-2218-...',
+            'demo-test-subnet-cidr': '10.0.1.0/24',
+            'demo-test-subnet': 'b08da78c-2218-...',
+            'ares.demo': 'b1a03624-aefc-...',
+            'demo-secgroup': '266a8088-c630-...',
+            'ares.demo-test-port': '10.0.1.5',
+            'demo-test-subnet-gateway_ip': '10.0.1.1',
+            'ares.demo-test-port-device_id': '',
+            'ares.demo-fip': '172.24.4.7',
+            'ares.demo-test-port-mac_address': 'fa:16:3e:6c:c3:0f',
+        }
 
+        stack_id = "my-existing-stack-id"
+        self.heatstack.name = "my-existing-stack"
+        self.heatstack.get()
 
-class HeatContextTestCase(unittest.TestCase):
+        self.mock_stack_get.assert_called_once_with(self.heatstack.name)
+        self.assertEqual(expected_outputs, self.heatstack.outputs)
+        self.assertEqual(1, len(heat._DEPLOYED_STACKS))
+        self.assertEqual(self.heatstack._stack,
+                         heat._DEPLOYED_STACKS[stack_id])
 
-    def test_get_short_key_uuid(self):
-        u = uuid.uuid4()
-        k = heat.get_short_key_uuid(u)
-        self.assertEqual(heat.HEAT_KEY_UUID_LENGTH, len(k))
-        self.assertIn(k, str(u))
+    def test_get_invalid_name(self):
+        # No context matching this name exists
+        self.mock_stack_get.return_value = []
+        self.heatstack.name = 'not-a-stack'
+        self.heatstack.get()
+        self.assertEqual(0, len(heat._DEPLOYED_STACKS))
 
 
 class HeatTemplateTestCase(unittest.TestCase):
 
     def setUp(self):
-        self.template = heat.HeatTemplate('test')
+        self._os_cloud_config = {'key1': 'value1'}
+        self.template = heat.HeatTemplate(
+            'test', os_cloud_config=self._os_cloud_config)
 
     def test_add_tenant_network(self):
         self.template.add_network('some-network')
 
-        self.assertEqual(
-            self.template.resources['some-network']['type'],
-            'OS::Neutron::Net')
+        self.assertEqual('OS::Neutron::Net',
+                         self.template.resources['some-network']['type'])
 
     def test_add_provider_network(self):
         self.template.add_network('some-network', 'physnet2', 'sriov')
 
-        self.assertEqual(
-            self.template.resources['some-network']['type'],
-            'OS::Neutron::ProviderNet')
-        self.assertEqual(
-            self.template.resources['some-network']['properties']['physical_network'],
-            'physnet2')
+        self.assertEqual(self.template.resources['some-network']['type'],
+                         'OS::Neutron::ProviderNet')
+        self.assertEqual(self.template.resources['some-network'][
+                             'properties']['physical_network'], 'physnet2')
 
     def test_add_subnet(self):
         netattrs = {'cidr': '10.0.0.0/24',
-                    'provider': None, 'external_network': 'ext_net'}
-        self.template.add_subnet(
-            'some-subnet', "some-network", netattrs['cidr'])
+                    'provider': None,
+                    'external_network': 'ext_net'}
+        self.template.add_subnet('some-subnet', "some-network",
+                                 netattrs['cidr'])
 
-        self.assertEqual(
-            self.template.resources['some-subnet']['type'],
-            'OS::Neutron::Subnet')
-        self.assertEqual(
-            self.template.resources['some-subnet']['properties']['cidr'],
-            '10.0.0.0/24')
+        self.assertEqual(self.template.resources['some-subnet']['type'],
+                         'OS::Neutron::Subnet')
+        self.assertEqual(self.template.resources['some-subnet']['properties'][
+                             'cidr'], '10.0.0.0/24')
 
     def test_add_router(self):
         self.template.add_router('some-router', 'ext-net', 'some-subnet')
 
-        self.assertEqual(
-            self.template.resources['some-router']['type'],
-            'OS::Neutron::Router')
-        self.assertIn(
-            'some-subnet',
-            self.template.resources['some-router']['depends_on'])
+        self.assertEqual(self.template.resources['some-router']['type'],
+                         'OS::Neutron::Router')
+        self.assertIn('some-subnet',
+                      self.template.resources['some-router']['depends_on'])
 
     def test_add_router_interface(self):
-        self.template.add_router_interface(
-            'some-router-if', 'some-router', 'some-subnet')
+        self.template.add_router_interface('some-router-if', 'some-router',
+                                           'some-subnet')
 
-        self.assertEqual(
-            self.template.resources['some-router-if']['type'],
-            'OS::Neutron::RouterInterface')
-        self.assertIn(
-            'some-subnet',
-            self.template.resources['some-router-if']['depends_on'])
+        self.assertEqual(self.template.resources['some-router-if']['type'],
+                         'OS::Neutron::RouterInterface')
+        self.assertIn('some-subnet',
+                      self.template.resources['some-router-if']['depends_on'])
 
     def test_add_servergroup(self):
         self.template.add_servergroup('some-server-group', 'anti-affinity')
 
-        self.assertEqual(
-            self.template.resources['some-server-group']['type'],
-            'OS::Nova::ServerGroup')
-        self.assertEqual(
-            self.template.resources['some-server-group']['properties']['policies'],
-            ['anti-affinity'])
+        self.assertEqual(self.template.resources['some-server-group']['type'],
+                         'OS::Nova::ServerGroup')
+        self.assertEqual(self.template.resources['some-server-group'][
+                             'properties']['policies'], ['anti-affinity'])
+
+    def test_add_security_group(self):
+        security_group = {
+            'rules': [
+                {'remote_ip_prefix': '0.0.0.0/0',
+                 'port_range_max': 65535,
+                 'port_range_min': 1,
+                 'protocol': 'custom'},
+            ]
+        }
+        self.template.add_security_group('some-security-group', security_group)
+
+        secgroup_rsc = self.template.resources['some-security-group']
+
+        self.assertEqual(secgroup_rsc['type'], "OS::Neutron::SecurityGroup")
+        self.assertEqual(secgroup_rsc['properties']['description'],
+                         "Custom security group rules defined by the user")
+        self.assertEqual(secgroup_rsc['properties']['rules'][0]['protocol'],
+                         'custom')
 
     def test__add_resources_to_template_raw(self):
         test_context = node.NodeContext()
-        test_context.name = 'foo'
+        self.addCleanup(test_context._delete_context)
+        test_context._name = 'foo'
         test_context.template_file = '/tmp/some-heat-file'
         test_context.heat_parameters = {'image': 'cirros'}
         test_context.key_filename = "/tmp/1234"
         test_context.keypair_name = "foo-key"
         test_context.secgroup_name = "foo-secgroup"
         test_context.key_uuid = "2f2e4997-0a8e-4eb7-9fa4-f3f8fbbc393b"
-        heat_object = heat.HeatObject()
-
-        heat_stack = heat.HeatStack("tmpStack")
-        self.assertTrue(heat_stack.stacks_exist())
 
-        test_context.tmpfile = NamedTemporaryFile(delete=True, mode='w+t')
+        test_context.tmpfile = tempfile.NamedTemporaryFile(
+            delete=True, mode='w+t')
         test_context.tmpfile.write("heat_template_version: 2015-04-30")
         test_context.tmpfile.flush()
         test_context.tmpfile.seek(0)
-        heat_template = heat.HeatTemplate(heat_object)
+        heat_template = heat.HeatTemplate('template name')
         heat_template.resources = {}
 
         heat_template.add_network("network1")
@@ -162,325 +302,116 @@ class HeatTemplateTestCase(unittest.TestCase):
         heat_template.add_subnet("subnet2", "network2", "cidr2")
         heat_template.add_router("router1", "gw1", "subnet1")
         heat_template.add_router_interface("router_if1", "router1", "subnet1")
-        heat_template.add_port("port1", "network1", "subnet1", "normal")
-        heat_template.add_port(
-            "port2",
-            "network2",
-            "subnet2",
-            "normal",
-            sec_group_id="sec_group1",
-            provider="not-sriov")
-        heat_template.add_port(
-            "port3",
-            "network2",
-            "subnet2",
-            "normal",
-            sec_group_id="sec_group1",
-            provider="sriov")
-        heat_template.add_floating_ip(
-            "floating_ip1", "network1", "port1", "router_if1")
-        heat_template.add_floating_ip(
-            "floating_ip2", "network2", "port2", "router_if2", "foo-secgroup")
-        heat_template.add_floating_ip_association(
-            "floating_ip1_association", "floating_ip1", "port1")
+        network1 = mock.MagicMock()
+        network1.stack_name = "network1"
+        network1.subnet_stack_name = "subnet1"
+        network1.vnic_type = "normal"
+        network2 = mock.MagicMock()
+        network2.stack_name = "network2"
+        network2.subnet_stack_name = "subnet2"
+        network2.vnic_type = "normal"
+        heat_template.add_port("port1", network1)
+        heat_template.add_port("port2", network2,
+                               sec_group_id="sec_group1", provider="not-sriov")
+        heat_template.add_port("port3", network2,
+                               sec_group_id="sec_group1", provider="sriov")
+        heat_template.add_floating_ip("floating_ip1", "network1", "port1",
+                                      "router_if1")
+        heat_template.add_floating_ip("floating_ip2", "network2", "port2",
+                                      "router_if2", "foo-secgroup")
+        heat_template.add_floating_ip_association("floating_ip1_association",
+                                                  "floating_ip1", "port1")
         heat_template.add_servergroup("server_grp2", "affinity")
         heat_template.add_servergroup("server_grp3", "anti-affinity")
         heat_template.add_security_group("security_group")
+        heat_template.add_server(name="server1", image="image1",
+                                 flavor="flavor1", flavors=[])
+        heat_template.add_server_group(name="servergroup",
+                                       policies=["policy1", "policy2"])
+        heat_template.add_server_group(name="servergroup",
+                                       policies="policy1")
         heat_template.add_server(
-            name="server1", image="image1", flavor="flavor1", flavors=[])
-        heat_template.add_server_group(
-            name="servergroup", policies=["policy1", "policy2"])
-        heat_template.add_server_group(name="servergroup", policies="policy1")
-        heat_template.add_server(
-            name="server2",
-            image="image1",
-            flavor="flavor1",
-            flavors=[],
-            ports=[
-                "port1",
-                "port2"],
-            networks=[
-                "network1",
-                "network2"],
-            scheduler_hints="hints1",
-            user="user1",
-            key_name="foo-key",
-            user_data="user",
-            metadata={
-                "cat": 1,
-                "doc": 2},
-            additional_properties={
-                "prop1": 1,
-                "prop2": 2})
+            name="server2", image="image1", flavor="flavor1", flavors=[],
+            ports=["port1", "port2"], networks=["network1", "network2"],
+            scheduler_hints="hints1", user="user1", key_name="foo-key",
+            user_data="user", metadata={"cat": 1, "doc": 2},
+            additional_properties={"prop1": 1, "prop2": 2})
         heat_template.add_server(
-            name="server2",
-            image="image1",
-            flavor="flavor1",
-            flavors=[
-                "flavor1",
-                "flavor2"],
-            ports=[
-                "port1",
-                "port2"],
-            networks=[
-                "network1",
-                "network2"],
-            scheduler_hints="hints1",
-            user="user1",
-            key_name="foo-key",
-            user_data="user",
-            metadata={
-                "cat": 1,
-                "doc": 2},
-            additional_properties={
-                "prop1": 1,
-                "prop2": 2})
+            name="server2", image="image1", flavor="flavor1",
+            flavors=["flavor1", "flavor2"], ports=["port1", "port2"],
+            networks=["network1", "network2"], scheduler_hints="hints1",
+            user="user1", key_name="foo-key", user_data="user",
+            metadata={"cat": 1, "doc": 2},
+            additional_properties={"prop1": 1, "prop2": 2})
         heat_template.add_server(
-            name="server2",
-            image="image1",
-            flavor="flavor1",
-            flavors=[
-                "flavor3",
-                "flavor4"],
-            ports=[
-                "port1",
-                "port2"],
-            networks=[
-                "network1",
-                "network2"],
-            scheduler_hints="hints1",
-            user="user1",
-            key_name="foo-key",
-            user_data="user",
-            metadata={
-                "cat": 1,
-                "doc": 2},
-            additional_properties={
-                "prop1": 1,
-                "prop2": 2})
-        heat_template.add_flavor(
-            name="flavor1",
-            vcpus=1,
-            ram=2048,
-            disk=1,
-            extra_specs={
-                "cat": 1,
-                "dog": 2})
+            name="server2", image="image1", flavor="flavor1",
+            flavors=["flavor3", "flavor4"], ports=["port1", "port2"],
+            networks=["network1", "network2"], scheduler_hints="hints1",
+            user="user1", key_name="foo-key", user_data="user",
+            metadata={"cat": 1, "doc": 2},
+            additional_properties={"prop1": 1, "prop2": 2})
+        heat_template.add_flavor(name="flavor1", vcpus=1, ram=2048, disk=1,
+                                 extra_specs={"cat": 1, "dog": 2})
         heat_template.add_flavor(name=None, vcpus=1, ram=2048)
         heat_template.add_server(
-            name="server1",
-            image="image1",
-            flavor="flavor1",
-            flavors=[],
-            ports=[
-                "port1",
-                "port2"],
-            networks=[
-                "network1",
-                "network2"],
-            scheduler_hints="hints1",
-            user="user1",
-            key_name="foo-key",
-            user_data="user",
-            metadata={
-                "cat": 1,
-                "doc": 2},
-            additional_properties={
-                "prop1": 1,
-                "prop2": 2})
+            name="server1", image="image1", flavor="flavor1", flavors=[],
+            ports=["port1", "port2"], networks=["network1", "network2"],
+            scheduler_hints="hints1", user="user1", key_name="foo-key",
+            user_data="user", metadata={"cat": 1, "doc": 2},
+            additional_properties={"prop1": 1, "prop2": 2})
         heat_template.add_network("network1")
 
         heat_template.add_flavor("test")
-        self.assertEqual(
-            heat_template.resources['test']['type'], 'OS::Nova::Flavor')
-
-    @mock_patch_target_module('op_utils')
-    @mock_patch_target_module('heatclient')
-    def test_create_negative(self, mock_heat_client_class, mock_op_utils):
-        self.template.HEAT_WAIT_LOOP_INTERVAL = 0
-        mock_heat_client = mock_heat_client_class()  # get the constructed mock
-
-        # populate attributes of the constructed mock
-        mock_heat_client.stacks.get().stack_status_reason = 'the reason'
-
-        expected_status_calls = 0
-        expected_constructor_calls = 1  # above, to get the instance
-        expected_create_calls = 0
-        expected_op_utils_usage = 0
-
-        with mock.patch.object(self.template, 'status', return_value=None) as mock_status:
-            # block with timeout hit
-            timeout = 0
-            with self.assertRaises(RuntimeError) as raised, timer():
-                self.template.create(block=True, timeout=timeout)
-
-            # ensure op_utils was used
-            expected_op_utils_usage += 1
-            self.assertEqual(
-                mock_op_utils.get_session.call_count, expected_op_utils_usage)
-            self.assertEqual(
-                mock_op_utils.get_endpoint.call_count, expected_op_utils_usage)
-            self.assertEqual(
-                mock_op_utils.get_heat_api_version.call_count,
-                expected_op_utils_usage)
-
-            # ensure the constructor and instance were used
-            self.assertEqual(mock_heat_client_class.call_count,
-                             expected_constructor_calls)
-            self.assertEqual(
-                mock_heat_client.stacks.create.call_count,
-                expected_create_calls)
-
-            # ensure that the status was used
-            self.assertGreater(mock_status.call_count, expected_status_calls)
-            expected_status_calls = mock_status.call_count  # synchronize the value
-
-            # ensure the expected exception was raised
-            error_message = get_error_message(raised.exception)
-            self.assertIn('timeout', error_message)
-            self.assertNotIn('the reason', error_message)
-
-            # block with create failed
-            timeout = 10
-            mock_status.side_effect = iter([None, None, u'CREATE_FAILED'])
-            with self.assertRaises(RuntimeError) as raised, timer():
-                self.template.create(block=True, timeout=timeout)
-
-            # ensure the existing heat_client was used and op_utils was used
-            # again
-            self.assertEqual(
-                mock_op_utils.get_session.call_count, expected_op_utils_usage)
-            self.assertEqual(
-                mock_op_utils.get_endpoint.call_count, expected_op_utils_usage)
-            self.assertEqual(
-                mock_op_utils.get_heat_api_version.call_count,
-                expected_op_utils_usage)
-
-            # ensure the constructor was not used but the instance was used
-            self.assertEqual(mock_heat_client_class.call_count,
-                             expected_constructor_calls)
-            self.assertEqual(
-                mock_heat_client.stacks.create.call_count,
-                expected_create_calls)
-
-            # ensure that the status was used three times
-            expected_status_calls += 3
-            self.assertEqual(mock_status.call_count, expected_status_calls)
-
-    # NOTE(elfoley): This needs to be split into multiple tests.
-    # The lines where the template is reset should serve as a guide for where
-    # to split.
-    @mock_patch_target_module('op_utils')
-    @mock_patch_target_module('heatclient')
-    def test_create(self, mock_heat_client_class, mock_op_utils):
-        self.template.HEAT_WAIT_LOOP_INTERVAL = 0.2
-        mock_heat_client = mock_heat_client_class()
-
-        # populate attributes of the constructed mock
-        mock_heat_client.stacks.get().outputs = [
-            {'output_key': 'key1', 'output_value': 'value1'},
-            {'output_key': 'key2', 'output_value': 'value2'},
-            {'output_key': 'key3', 'output_value': 'value3'},
+        self.assertEqual(heat_template.resources['test']['type'],
+                         'OS::Nova::Flavor')
+
+    def test_create_not_block(self):
+        heat_stack = mock.Mock()
+        with mock.patch.object(heat, 'HeatStack', return_value=heat_stack) \
+                as mock_heatstack:
+            ret = self.template.create(block=False)
+
+        mock_heatstack.assert_called_once_with(
+            self.template.name, os_cloud_config=self.template._os_cloud_config)
+        heat_stack.create.assert_called_once_with(
+            self.template._template, self.template.heat_parameters, False,
+            3600)
+        self.assertEqual(heat_stack, ret)
+
+    def test_create_block(self):
+        heat_stack = mock.Mock()
+        heat_stack.status = self.template.HEAT_STATUS_COMPLETE
+        with mock.patch.object(heat, 'HeatStack', return_value=heat_stack):
+            ret = self.template.create(block=False)
+        heat_stack.create.assert_called_once_with(
+            self.template._template, self.template.heat_parameters, False,
+            3600)
+        self.assertEqual(heat_stack, ret)
+
+    def test_create_block_status_no_complete(self):
+        heat_stack = mock.Mock()
+        heat_stack.status = 'other status'
+        heat_stack.get_failures.return_value = []
+        with mock.patch.object(heat, 'HeatStack', return_value=heat_stack):
+            self.assertRaises(exceptions.HeatTemplateError,
+                              self.template.create, block=True)
+        heat_stack.create.assert_called_once_with(
+            self.template._template, self.template.heat_parameters, True,
+            3600)
+
+    def test_create_block_status_no_complete_with_reasons(self):
+        heat_stack = mock.Mock()
+        heat_stack.status = 'other status'
+        heat_stack.get_failures.return_value = [
+            mock.Mock(resource_status_reason="A reason"),
+            mock.Mock(resource_status_reason="Something else")
         ]
-        expected_outputs = {  # pylint: disable=unused-variable
-            'key1': 'value1',
-            'key2': 'value2',
-            'key3': 'value3',
-        }
-
-        expected_status_calls = 0
-        expected_constructor_calls = 1  # above, to get the instance
-        expected_create_calls = 0
-        expected_op_utils_usage = 0
-
-        with mock.patch.object(self.template, 'status') as mock_status:
-            self.template.name = 'no block test'
-            mock_status.return_value = None
-
-            # no block
-            self.assertIsInstance(self.template.create(
-                block=False, timeout=2), heat.HeatStack)
-
-            # ensure op_utils was used
-            expected_op_utils_usage += 1
-            self.assertEqual(
-                mock_op_utils.get_session.call_count, expected_op_utils_usage)
-            self.assertEqual(
-                mock_op_utils.get_endpoint.call_count, expected_op_utils_usage)
-            self.assertEqual(
-                mock_op_utils.get_heat_api_version.call_count,
-                expected_op_utils_usage)
-
-            # ensure the constructor and instance were used
-            self.assertEqual(mock_heat_client_class.call_count,
-                             expected_constructor_calls)
-            self.assertEqual(
-                mock_heat_client.stacks.create.call_count,
-                expected_create_calls)
-
-            # ensure that the status was not used
-            self.assertEqual(mock_status.call_count, expected_status_calls)
-
-            # ensure no outputs because this requires blocking
-            self.assertEqual(self.template.outputs, {})
-
-            # block with immediate complete
-            self.template.name = 'block, immediate complete test'
-
-            mock_status.return_value = self.template.HEAT_CREATE_COMPLETE_STATUS
-            self.assertIsInstance(self.template.create(
-                block=True, timeout=2), heat.HeatStack)
-
-            # ensure existing instance was re-used and op_utils was not used
-            self.assertEqual(mock_heat_client_class.call_count,
-                             expected_constructor_calls)
-            self.assertEqual(
-                mock_heat_client.stacks.create.call_count,
-                expected_create_calls)
-
-            # ensure status was checked once
-            expected_status_calls += 1
-            self.assertEqual(mock_status.call_count, expected_status_calls)
-
-            # reset template outputs
-            self.template.outputs = None
-
-            # block with delayed complete
-            self.template.name = 'block, delayed complete test'
-
-            success_index = 2
-            mock_status.side_effect = index_value_iter(
-                success_index, self.template.HEAT_CREATE_COMPLETE_STATUS)
-            self.assertIsInstance(self.template.create(
-                block=True, timeout=2), heat.HeatStack)
-
-            # ensure existing instance was re-used and op_utils was not used
-            self.assertEqual(mock_heat_client_class.call_count,
-                             expected_constructor_calls)
-            self.assertEqual(
-                mock_heat_client.stacks.create.call_count,
-                expected_create_calls)
-
-            # ensure status was checked three more times
-            expected_status_calls += 1 + success_index
-            self.assertEqual(mock_status.call_count, expected_status_calls)
-
-
-class HeatStackTestCase(unittest.TestCase):
-
-    def test_delete_calls__delete_multiple_times(self):
-        stack = heat.HeatStack('test')
-        stack.uuid = 1
-        with mock.patch.object(stack, "_delete") as delete_mock:
-            stack.delete()
-        # call once and then call again if uuid is not none
-        self.assertGreater(delete_mock.call_count, 1)
-
-    def test_delete_all_calls_delete(self):
-        # we must patch the object before we create an instance
-        # so we can override delete() in all the instances
-        with mock.patch.object(heat.HeatStack, "delete") as delete_mock:
-            stack = heat.HeatStack('test')
-            stack.uuid = 1
-            stack.delete_all()
-            self.assertGreater(delete_mock.call_count, 0)
+        with mock.patch.object(heat, 'HeatStack', return_value=heat_stack):
+            with mock.patch.object(heat, 'log') as mock_log:
+                self.assertRaises(exceptions.HeatTemplateError,
+                                  self.template.create, block=True)
+                mock_log.error.assert_any_call("%s", "A reason")
+                mock_log.error.assert_any_call("%s", "Something else")
+        heat_stack.create.assert_called_once_with(
+            self.template._template, self.template.heat_parameters, True,
+            3600)