diff options
-rw-r--r-- | INFO | 1 | ||||
-rw-r--r-- | INFO.yaml | 4 | ||||
-rw-r--r-- | ansible/roles/infra_destroy_previous_configuration/tasks/delete_vm.yml | 29 | ||||
-rw-r--r-- | ansible/roles/infra_destroy_previous_configuration/tasks/main.yml | 15 | ||||
-rw-r--r-- | docker/Dockerfile.aarch64.patch | 2 | ||||
-rwxr-xr-x | nsb_setup.sh | 2 | ||||
-rw-r--r-- | samples/vnf_samples/nsut/vfw/tc_heat_rfc2544_ipv4_1rule_1flow_64B_trex.yaml | 7 | ||||
-rw-r--r-- | tests/unit/network_services/vnf_generic/vnf/test_sample_vnf.py | 53 | ||||
-rw-r--r-- | yardstick/benchmark/scenarios/lib/delete_network.py | 9 | ||||
-rw-r--r-- | yardstick/common/exceptions.py | 4 | ||||
-rw-r--r-- | yardstick/common/openstack_utils.py | 52 | ||||
-rw-r--r-- | yardstick/network_services/vnf_generic/vnf/sample_vnf.py | 18 | ||||
-rw-r--r-- | yardstick/orchestrator/heat.py | 9 | ||||
-rw-r--r-- | yardstick/tests/unit/benchmark/scenarios/lib/test_delete_network.py | 54 | ||||
-rw-r--r-- | yardstick/tests/unit/common/test_openstack_utils.py | 29 | ||||
-rw-r--r-- | yardstick/tests/unit/orchestrator/test_heat.py | 12 |
16 files changed, 195 insertions, 105 deletions
@@ -22,6 +22,7 @@ ross.b.brattain@intel.com chenjiankun1@huawei.com rodolfo.alonso.hernandez@intel.com emma.l.foley@intel.com +abhijit.sinha@intel.com Link to TSC approval: http://meetbot.opnfv.org/meetings/ Link to approval of additional submitters: @@ -74,6 +74,10 @@ committers: email: '14_ykl@tongji.edu.cn' company: 'tongji.edu.cn' id: 'tjuyinkanglin' + - name: 'Abhijit Sinha' + email: 'abhijit.sinha@intel.com' + company: 'intel.com' + id: 'abhijitsinha' tsc: # yamllint disable rule:line-length approval: 'http//meetbot.opnfv.org/meetings/' diff --git a/ansible/roles/infra_destroy_previous_configuration/tasks/delete_vm.yml b/ansible/roles/infra_destroy_previous_configuration/tasks/delete_vm.yml new file mode 100644 index 000000000..5e43ee81e --- /dev/null +++ b/ansible/roles/infra_destroy_previous_configuration/tasks/delete_vm.yml @@ -0,0 +1,29 @@ +# Copyright (c) 2017-2018 Intel Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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. +--- +# Ignore errors as VM can be destroyed without been undefined. +- name: Destroy old VMs + virt: + command: destroy + name: "{{ node_item.hostname }}" + when: node_item.hostname in virt_vms.list_vms + ignore_errors: yes + +# Ignore errors as VM can be running while undefined +- name: Undefine old VMs + virt: + command: undefine + name: "{{ node_item.hostname }}" + when: node_item.hostname in virt_vms.list_vms + ignore_errors: yes diff --git a/ansible/roles/infra_destroy_previous_configuration/tasks/main.yml b/ansible/roles/infra_destroy_previous_configuration/tasks/main.yml index 5595cd501..e6c2c0229 100644 --- a/ansible/roles/infra_destroy_previous_configuration/tasks/main.yml +++ b/ansible/roles/infra_destroy_previous_configuration/tasks/main.yml @@ -26,17 +26,10 @@ register: virt_vms - name: Destroy old VMs - virt: - command: destroy - name: "{{ item.hostname }}" - when: item.hostname in virt_vms.list_vms - with_items: "{{ infra_deploy_vars.nodes }}" - -- name: Undefine old VMs - virt: - command: undefine - name: "{{ item.hostname }}" - when: item.hostname in virt_vms.list_vms + include_tasks: delete_vm.yml + extra_vars: "{{ virt_vms }}" + loop_control: + loop_var: node_item with_items: "{{ infra_deploy_vars.nodes }}" - name: Delete old networks diff --git a/docker/Dockerfile.aarch64.patch b/docker/Dockerfile.aarch64.patch index ca933514a..24e3952fb 100644 --- a/docker/Dockerfile.aarch64.patch +++ b/docker/Dockerfile.aarch64.patch @@ -39,7 +39,7 @@ index 2ee5b4c..23e5ea5 100644 +RUN apt-get update && apt-get install -y git python-setuptools python-pip && apt-get -y autoremove && \ + apt-get install -y libssl-dev && apt-get -y install libffi-dev && apt-get clean RUN easy_install -U setuptools==30.0.0 - RUN pip install appdirs==1.4.0 pyopenssl==17.5.0 + RUN pip install appdirs==1.4.0 pyopenssl==17.5.0 python-openstackclient==3.11.0 @@ -43,8 +44,8 @@ RUN echo "daemon off;" >> /etc/nginx/nginx.conf diff --git a/nsb_setup.sh b/nsb_setup.sh index 4a8e4db93..50fc017d1 100755 --- a/nsb_setup.sh +++ b/nsb_setup.sh @@ -63,7 +63,7 @@ for i in "${pkg[@]}"; do fi done -pip install ansible==2.3.2 shade==1.17.0 docker-py==1.10.6 +pip install ansible==2.4.2 shade==1.22.2 docker-py==1.10.6 ANSIBLE_SCRIPTS="ansible" diff --git a/samples/vnf_samples/nsut/vfw/tc_heat_rfc2544_ipv4_1rule_1flow_64B_trex.yaml b/samples/vnf_samples/nsut/vfw/tc_heat_rfc2544_ipv4_1rule_1flow_64B_trex.yaml index 13fe5a5a5..2e096a126 100644 --- a/samples/vnf_samples/nsut/vfw/tc_heat_rfc2544_ipv4_1rule_1flow_64B_trex.yaml +++ b/samples/vnf_samples/nsut/vfw/tc_heat_rfc2544_ipv4_1rule_1flow_64B_trex.yaml @@ -15,6 +15,7 @@ --- {% set provider = provider or none %} {% set physical_networks = physical_networks or ['physnet1', 'physnet2'] %} +{% set segmentation_id = segmentation_id or none %} schema: yardstick:task:0.1 scenarios: @@ -74,6 +75,9 @@ context: {% if provider %} provider: {{ provider }} physical_network: {{ physical_networks[0] }} + {% if segmentation_id %} + segmentation_id: {{ segmentation_id }} + {% endif %} {% endif %} port_security_enabled: False enable_dhcp: 'false' @@ -83,6 +87,9 @@ context: {% if provider %} provider: {{ provider }} physical_network: {{ physical_networks[1] }} + {% if segmentation_id %} + segmentation_id: {{ segmentation_id }} + {% endif %} {% endif %} port_security_enabled: False enable_dhcp: 'false' diff --git a/tests/unit/network_services/vnf_generic/vnf/test_sample_vnf.py b/tests/unit/network_services/vnf_generic/vnf/test_sample_vnf.py index af941c04f..25633384e 100644 --- a/tests/unit/network_services/vnf_generic/vnf/test_sample_vnf.py +++ b/tests/unit/network_services/vnf_generic/vnf/test_sample_vnf.py @@ -624,37 +624,34 @@ class TestDpdkVnfSetupEnvHelper(unittest.TestCase): self.assertIsInstance(dpdk_vnf_setup_env_helper.setup_vnf_environment(), ResourceProfile) - def test__setup_dpdk_early_success(self): - vnfd_helper = VnfdHelper(self.VNFD_0) + def test__setup_dpdk(self): ssh_helper = mock.Mock() - ssh_helper.execute.return_value = 0, 'output', '' - ssh_helper.join_bin_path.return_value = 'joined_path' - ssh_helper.provision_tool.return_value = 'provision string' - scenario_helper = mock.Mock() - dpdk_setup_helper = DpdkVnfSetupEnvHelper(vnfd_helper, ssh_helper, scenario_helper) - dpdk_setup_helper._setup_hugepages = mock.Mock() - - self.assertIsNone(dpdk_setup_helper._setup_dpdk()) - self.assertEqual(dpdk_setup_helper.ssh_helper.execute.call_count, 2) - - @mock.patch('yardstick.ssh.SSH') - def test__setup_dpdk_short(self, _): - def execute_side(cmd): - if 'joined_path' in cmd: - return 0, 'output', '' - return 1, 'bad output', 'error output' + ssh_helper.execute = mock.Mock() + ssh_helper.execute.return_value = (0, 0, 0) + dpdk_setup_helper = DpdkVnfSetupEnvHelper(mock.ANY, ssh_helper, mock.ANY) + with mock.patch.object(dpdk_setup_helper, '_setup_hugepages') as \ + mock_setup_hp: + dpdk_setup_helper._setup_dpdk() + mock_setup_hp.assert_called_once() + ssh_helper.execute.assert_has_calls([ + mock.call('sudo modprobe uio && sudo modprobe igb_uio'), + mock.call('lsmod | grep -i igb_uio') + ]) - vnfd_helper = VnfdHelper(self.VNFD_0) + def test__setup_dpdk_igb_uio_not_loaded(self): ssh_helper = mock.Mock() - ssh_helper.execute.side_effect = execute_side - ssh_helper.join_bin_path.return_value = 'joined_path' - ssh_helper.provision_tool.return_value = 'provision string' - scenario_helper = mock.Mock() - dpdk_setup_helper = DpdkVnfSetupEnvHelper(vnfd_helper, ssh_helper, scenario_helper) - dpdk_setup_helper._setup_hugepages = mock.Mock() - - self.assertIsNone(dpdk_setup_helper._setup_dpdk()) - self.assertEqual(dpdk_setup_helper.ssh_helper.execute.call_count, 3) + ssh_helper.execute = mock.Mock() + ssh_helper.execute.side_effect = [(0, 0, 0), (1, 0, 0)] + dpdk_setup_helper = DpdkVnfSetupEnvHelper(mock.ANY, ssh_helper, mock.ANY) + with mock.patch.object(dpdk_setup_helper, '_setup_hugepages') as \ + mock_setup_hp: + with self.assertRaises(y_exceptions.DPDKSetupDriverError): + dpdk_setup_helper._setup_dpdk() + mock_setup_hp.assert_called_once() + ssh_helper.execute.assert_has_calls([ + mock.call('sudo modprobe uio && sudo modprobe igb_uio'), + mock.call('lsmod | grep -i igb_uio') + ]) @mock.patch('yardstick.ssh.SSH') def test__setup_resources(self, _): diff --git a/yardstick/benchmark/scenarios/lib/delete_network.py b/yardstick/benchmark/scenarios/lib/delete_network.py index e8796bf82..2e8b595f9 100644 --- a/yardstick/benchmark/scenarios/lib/delete_network.py +++ b/yardstick/benchmark/scenarios/lib/delete_network.py @@ -7,14 +7,12 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -from __future__ import print_function -from __future__ import absolute_import - import logging from yardstick.benchmark.scenarios import base import yardstick.common.openstack_utils as op_utils + LOG = logging.getLogger(__name__) @@ -30,7 +28,7 @@ class DeleteNetwork(base.Scenario): self.network_id = self.options.get("network_id", None) - self.neutron_client = op_utils.get_neutron_client() + self.shade_client = op_utils.get_shade_client() self.setup_done = False @@ -45,7 +43,7 @@ class DeleteNetwork(base.Scenario): if not self.setup_done: self.setup() - status = op_utils.delete_neutron_net(self.neutron_client, + status = op_utils.delete_neutron_net(self.shade_client, network_id=self.network_id) if status: result.update({"delete_network": 1}) @@ -53,3 +51,4 @@ class DeleteNetwork(base.Scenario): else: result.update({"delete_network": 0}) LOG.error("Delete network failed!") + return status diff --git a/yardstick/common/exceptions.py b/yardstick/common/exceptions.py index e38dd246c..a86d863db 100644 --- a/yardstick/common/exceptions.py +++ b/yardstick/common/exceptions.py @@ -63,3 +63,7 @@ class HeatTemplateError(YardstickException): """Error in Heat during the stack deployment""" message = ('Error in Heat during the creation of the OpenStack stack ' '"%(stack_name)"') + + +class DPDKSetupDriverError(YardstickException): + message = '"igb_uio" driver is not loaded' diff --git a/yardstick/common/openstack_utils.py b/yardstick/common/openstack_utils.py index c5b17c270..8f666e268 100644 --- a/yardstick/common/openstack_utils.py +++ b/yardstick/common/openstack_utils.py @@ -15,6 +15,7 @@ import logging from keystoneauth1 import loading from keystoneauth1 import session import shade +from shade import exc from cinderclient import client as cinderclient from novaclient import client as novaclient @@ -174,6 +175,7 @@ def get_glance_client(): # pragma: no cover def get_shade_client(): return shade.openstack_cloud() + # ********************************************* # NOVA # ********************************************* @@ -272,7 +274,8 @@ def create_aggregate_with_host(nova_client, aggregate_name, av_zone, def create_keypair(name, key_path=None): # pragma: no cover try: with open(key_path) as fpubkey: - keypair = get_nova_client().keypairs.create(name=name, public_key=fpubkey.read()) + keypair = get_nova_client().keypairs.create( + name=name, public_key=fpubkey.read()) return keypair except Exception: # pylint: disable=broad-except log.exception("Error [create_keypair(nova_client)]") @@ -304,9 +307,11 @@ def create_instance_and_wait_for_active(json_body): # pragma: no cover return None -def attach_server_volume(server_id, volume_id, device=None): # pragma: no cover +def attach_server_volume(server_id, volume_id, + device=None): # pragma: no cover try: - get_nova_client().volumes.create_server_volume(server_id, volume_id, device) + get_nova_client().volumes.create_server_volume(server_id, + volume_id, device) except Exception: # pylint: disable=broad-except log.exception("Error [attach_server_volume(nova_client, '%s', '%s')]", server_id, volume_id) @@ -370,7 +375,8 @@ def get_server_by_name(name): # pragma: no cover def create_flavor(name, ram, vcpus, disk, **kwargs): # pragma: no cover try: - return get_nova_client().flavors.create(name, ram, vcpus, disk, **kwargs) + return get_nova_client().flavors.create(name, ram, vcpus, + disk, **kwargs) except Exception: # pylint: disable=broad-except log.exception("Error [create_flavor(nova_client, %s, %s, %s, %s, %s)]", name, ram, disk, vcpus, kwargs['is_public']) @@ -455,13 +461,11 @@ def create_neutron_net(neutron_client, json_body): # pragma: no cover raise Exception("operation error") -def delete_neutron_net(neutron_client, network_id): # pragma: no cover +def delete_neutron_net(shade_client, network_id): try: - neutron_client.delete_network(network_id) - return True - except Exception: # pylint: disable=broad-except - log.error("Error [delete_neutron_net(neutron_client, '%s')]", - network_id) + return shade_client.delete_network(network_id) + except exc.OpenStackCloudException: + log.error("Error [delete_neutron_net(shade_client, '%s')]", network_id) return False @@ -558,7 +562,8 @@ def get_security_group_id(neutron_client, sg_name): # pragma: no cover return id -def create_security_group(neutron_client, sg_name, sg_description): # pragma: no cover +def create_security_group(neutron_client, sg_name, + sg_description): # pragma: no cover json_body = {'security_group': {'name': sg_name, 'description': sg_description}} try: @@ -611,8 +616,8 @@ def create_secgroup_rule(neutron_client, sg_id, direction, protocol, return False -def create_security_group_full(neutron_client, - sg_name, sg_description): # pragma: no cover +def create_security_group_full(neutron_client, sg_name, + sg_description): # pragma: no cover sg_id = get_security_group_id(neutron_client, sg_name) if sg_id != '': log.info("Using existing security group '%s'...", sg_name) @@ -670,22 +675,18 @@ def create_image(glance_client, image_name, file_path, disk_format, else: log.info("Creating image '%s' from '%s'...", image_name, file_path) - image = glance_client.images.create(name=image_name, - visibility=public, - disk_format=disk_format, - container_format=container_format, - min_disk=min_disk, - min_ram=min_ram, - tags=tag, - protected=protected, - **kwargs) + image = glance_client.images.create( + name=image_name, visibility=public, disk_format=disk_format, + container_format=container_format, min_disk=min_disk, + min_ram=min_ram, tags=tag, protected=protected, **kwargs) image_id = image.id with open(file_path) as image_data: glance_client.images.upload(image_id, image_data) return image_id except Exception: # pylint: disable=broad-except - log.error("Error [create_glance_image(glance_client, '%s', '%s', '%s')]", - image_name, file_path, public) + log.error( + "Error [create_glance_image(glance_client, '%s', '%s', '%s')]", + image_name, file_path, public) return None @@ -725,7 +726,8 @@ def create_volume(cinder_client, volume_name, volume_size, return None -def delete_volume(cinder_client, volume_id, forced=False): # pragma: no cover +def delete_volume(cinder_client, volume_id, + forced=False): # pragma: no cover try: if forced: try: diff --git a/yardstick/network_services/vnf_generic/vnf/sample_vnf.py b/yardstick/network_services/vnf_generic/vnf/sample_vnf.py index fbaaa0ca8..d57d7e601 100644 --- a/yardstick/network_services/vnf_generic/vnf/sample_vnf.py +++ b/yardstick/network_services/vnf_generic/vnf/sample_vnf.py @@ -250,20 +250,12 @@ class DpdkVnfSetupEnvHelper(SetupEnvHelper): self.ssh_helper.execute("sudo killall %s" % self.APP_NAME) def _setup_dpdk(self): - """ setup dpdk environment needed for vnf to run """ - + """Setup DPDK environment needed for VNF to run""" self._setup_hugepages() - self.ssh_helper.execute("sudo modprobe uio && sudo modprobe igb_uio") - - exit_status = self.ssh_helper.execute("lsmod | grep -i igb_uio")[0] - if exit_status == 0: - return - - dpdk = self.ssh_helper.join_bin_path(DPDK_VERSION) - dpdk_setup = self.ssh_helper.provision_tool(tool_file="nsb_setup.sh") - exit_status = self.ssh_helper.execute("which {} >/dev/null 2>&1".format(dpdk))[0] - if exit_status != 0: - self.ssh_helper.execute("bash %s dpdk >/dev/null 2>&1" % dpdk_setup) + self.ssh_helper.execute('sudo modprobe uio && sudo modprobe igb_uio') + exit_status = self.ssh_helper.execute('lsmod | grep -i igb_uio')[0] + if exit_status: + raise y_exceptions.DPDKSetupDriverError() def get_collectd_options(self): options = self.scenario_helper.all_options.get("collectd", {}) diff --git a/yardstick/orchestrator/heat.py b/yardstick/orchestrator/heat.py index 754482e4f..558b5d2a6 100644 --- a/yardstick/orchestrator/heat.py +++ b/yardstick/orchestrator/heat.py @@ -74,7 +74,14 @@ class HeatStack(object): if self.uuid is None: return - ret = self._cloud.delete_stack(self.uuid, wait=wait) + try: + ret = self._cloud.delete_stack(self.uuid, wait=wait) + except TypeError: + # NOTE(ralonsoh): this exception catch solves a bug in Shade, which + # tries to retrieve and read the stack status when it's already + # deleted. + ret = True + _DEPLOYED_STACKS.pop(self.uuid) self._stack = None return ret diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_network.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_network.py index 5f11713fa..aef99ee94 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_network.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_network.py @@ -6,30 +6,44 @@ # which accompanies this distribution, and is available at # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## + +from oslo_utils import uuidutils import unittest import mock -from yardstick.benchmark.scenarios.lib.delete_network import DeleteNetwork +import yardstick.common.openstack_utils as op_utils +from yardstick.benchmark.scenarios.lib import delete_network class DeleteNetworkTestCase(unittest.TestCase): - @mock.patch('yardstick.common.openstack_utils.get_neutron_client') - @mock.patch('yardstick.common.openstack_utils.delete_neutron_net') - def test_delete_network(self, mock_get_neutron_client, mock_delete_neutron_net): - options = { - 'network_id': '123-123-123' - } - args = {"options": options} - obj = DeleteNetwork(args, {}) - obj.run({}) - self.assertTrue(mock_get_neutron_client.called) - self.assertTrue(mock_delete_neutron_net.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + def setUp(self): + self._mock_delete_neutron_net = mock.patch.object( + op_utils, 'delete_neutron_net') + self.mock_delete_neutron_net = self._mock_delete_neutron_net.start() + self._mock_get_shade_client = mock.patch.object( + op_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(delete_network, 'LOG') + self.mock_log = self._mock_log.start() + _uuid = uuidutils.generate_uuid() + self.args = {'options': {'network_id': _uuid}} + self._del_obj = delete_network.DeleteNetwork(self.args, mock.ANY) + + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_delete_neutron_net.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + self.mock_delete_neutron_net.return_value = True + self.assertTrue(self._del_obj.run({})) + self.mock_log.info.assert_called_once_with( + "Delete network successful!") + + def test_run_fail(self): + self.mock_delete_neutron_net.return_value = False + self.assertFalse(self._del_obj.run({})) + self.mock_log.error.assert_called_once_with("Delete network failed!") diff --git a/yardstick/tests/unit/common/test_openstack_utils.py b/yardstick/tests/unit/common/test_openstack_utils.py index b685e63be..8a2f5f95b 100644 --- a/yardstick/tests/unit/common/test_openstack_utils.py +++ b/yardstick/tests/unit/common/test_openstack_utils.py @@ -11,6 +11,7 @@ from oslo_utils import uuidutils import unittest import mock +from shade import exc from yardstick.common import openstack_utils @@ -54,3 +55,31 @@ class GetNetworkIdTestCase(unittest.TestCase): output = openstack_utils.get_network_id(mock_shade_client, 'network_name') self.assertEqual(None, output) + + +class DeleteNeutronNetTestCase(unittest.TestCase): + + def setUp(self): + self.mock_shade_client = mock.Mock() + self.mock_shade_client.delete_network = mock.Mock() + + def test_delete_neutron_net(self): + self.mock_shade_client.delete_network.return_value = True + output = openstack_utils.delete_neutron_net(self.mock_shade_client, + 'network_id') + self.assertTrue(output) + + def test_delete_neutron_net_fail(self): + self.mock_shade_client.delete_network.return_value = False + output = openstack_utils.delete_neutron_net(self.mock_shade_client, + 'network_id') + self.assertFalse(output) + + @mock.patch.object(openstack_utils, 'log') + def test_delete_neutron_net_exception(self, mock_logger): + self.mock_shade_client.delete_network.side_effect = ( + exc.OpenStackCloudException('error message')) + output = openstack_utils.delete_neutron_net(self.mock_shade_client, + 'network_id') + self.assertFalse(output) + mock_logger.error.assert_called_once() diff --git a/yardstick/tests/unit/orchestrator/test_heat.py b/yardstick/tests/unit/orchestrator/test_heat.py index f53c9b78c..e0a353812 100644 --- a/yardstick/tests/unit/orchestrator/test_heat.py +++ b/yardstick/tests/unit/orchestrator/test_heat.py @@ -89,6 +89,18 @@ class HeatStackTestCase(unittest.TestCase): 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) + class HeatTemplateTestCase(unittest.TestCase): |