Merge "Use exception_filter in RPC client"

This commit is contained in:
Jenkins 2017-03-29 13:46:53 +00:00 committed by Gerrit Code Review
commit 48ef79c5f7
14 changed files with 121 additions and 34 deletions

View File

@ -112,12 +112,9 @@ class MultipartMime(software_config.SoftwareConfig):
part = config part = config
if uuidutils.is_uuid_like(config): if uuidutils.is_uuid_like(config):
try: with self.rpc_client().ignore_error_by_name('NotFound'):
sc = self.rpc_client().show_software_config( sc = self.rpc_client().show_software_config(
self.context, config) self.context, config)
except Exception as ex:
self.rpc_client().ignore_error_named(ex, 'NotFound')
else:
part = sc[rpc_api.SOFTWARE_CONFIG_CONFIG] part = sc[rpc_api.SOFTWARE_CONFIG_CONFIG]
if part_type == self.MULTIPART: if part_type == self.MULTIPART:

View File

@ -130,14 +130,12 @@ class SoftwareComponent(sc.SoftwareConfig):
an empty list is returned. an empty list is returned.
""" """
if name == self.CONFIGS_ATTR and self.resource_id: if name == self.CONFIGS_ATTR and self.resource_id:
try: with self.rpc_client().ignore_error_by_name('NotFound'):
sc = self.rpc_client().show_software_config( sc = self.rpc_client().show_software_config(
self.context, self.resource_id) self.context, self.resource_id)
# configs list is stored in 'config' property of parent class # configs list is stored in 'config' property of parent class
# (see handle_create) # (see handle_create)
return sc[rpc_api.SOFTWARE_CONFIG_CONFIG].get(self.CONFIGS) return sc[rpc_api.SOFTWARE_CONFIG_CONFIG].get(self.CONFIGS)
except Exception as ex:
self.rpc_client().ignore_error_named(ex, 'NotFound')
def validate(self): def validate(self):
"""Validate SoftwareComponent properties consistency.""" """Validate SoftwareComponent properties consistency."""

View File

@ -113,11 +113,9 @@ class SoftwareConfig(resource.Resource):
if self.resource_id is None: if self.resource_id is None:
return return
try: with self.rpc_client().ignore_error_by_name('NotFound'):
self.rpc_client().delete_software_config( self.rpc_client().delete_software_config(
self.context, self.resource_id) self.context, self.resource_id)
except Exception as ex:
self.rpc_client().ignore_error_named(ex, 'NotFound')
def _resolve_attribute(self, name): def _resolve_attribute(self, name):
"""Retrieve attributes of the SoftwareConfig resource. """Retrieve attributes of the SoftwareConfig resource.
@ -126,12 +124,10 @@ class SoftwareConfig(resource.Resource):
software config does not exist, returns an empty string. software config does not exist, returns an empty string.
""" """
if name == self.CONFIG_ATTR and self.resource_id: if name == self.CONFIG_ATTR and self.resource_id:
try: with self.rpc_client().ignore_error_by_name('NotFound'):
sc = self.rpc_client().show_software_config( sc = self.rpc_client().show_software_config(
self.context, self.resource_id) self.context, self.resource_id)
return sc[rpc_api.SOFTWARE_CONFIG_CONFIG] return sc[rpc_api.SOFTWARE_CONFIG_CONFIG]
except Exception as ex:
self.rpc_client().ignore_error_named(ex, 'NotFound')
def resource_mapping(): def resource_mapping():

View File

@ -209,11 +209,9 @@ class SoftwareDeployment(signal_responder.SignalResponder):
return props return props
def _delete_derived_config(self, derived_config_id): def _delete_derived_config(self, derived_config_id):
try: with self.rpc_client().ignore_error_by_name('NotFound'):
self.rpc_client().delete_software_config( self.rpc_client().delete_software_config(
self.context, derived_config_id) self.context, derived_config_id)
except Exception as ex:
self.rpc_client().ignore_error_named(ex, 'NotFound')
def _create_derived_config(self, action, source_config): def _create_derived_config(self, action, source_config):
@ -466,10 +464,8 @@ class SoftwareDeployment(signal_responder.SignalResponder):
return self._check_complete() return self._check_complete()
def handle_delete(self): def handle_delete(self):
try: with self.rpc_client().ignore_error_by_name('NotFound'):
return self._handle_action(self.DELETE) return self._handle_action(self.DELETE)
except Exception as ex:
self.rpc_client().ignore_error_named(ex, 'NotFound')
def check_delete_complete(self, sd=None): def check_delete_complete(self, sd=None):
if not sd or not self._server_exists(sd) or self._check_complete(): if not sd or not self._server_exists(sd) or self._check_complete():
@ -479,12 +475,10 @@ class SoftwareDeployment(signal_responder.SignalResponder):
def _delete_resource(self): def _delete_resource(self):
derived_config_id = None derived_config_id = None
if self.resource_id is not None: if self.resource_id is not None:
try: with self.rpc_client().ignore_error_by_name('NotFound'):
derived_config_id = self._get_derived_config_id() derived_config_id = self._get_derived_config_id()
self.rpc_client().delete_software_deployment( self.rpc_client().delete_software_deployment(
self.context, self.resource_id) self.context, self.resource_id)
except Exception as ex:
self.rpc_client().ignore_error_named(ex, 'NotFound')
if derived_config_id: if derived_config_id:
self._delete_derived_config(derived_config_id) self._delete_derived_config(derived_config_id)

View File

@ -702,13 +702,11 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin,
self.USER_DATA_FORMAT] == self.SOFTWARE_CONFIG self.USER_DATA_FORMAT] == self.SOFTWARE_CONFIG
def get_software_config(self, ud_content): def get_software_config(self, ud_content):
try: with self.rpc_client().ignore_error_by_name('NotFound'):
sc = self.rpc_client().show_software_config( sc = self.rpc_client().show_software_config(
self.context, ud_content) self.context, ud_content)
return sc[rpc_api.SOFTWARE_CONFIG_CONFIG] return sc[rpc_api.SOFTWARE_CONFIG_CONFIG]
except Exception as ex: return ud_content
self.rpc_client().ignore_error_named(ex, 'NotFound')
return ud_content
def handle_create(self): def handle_create(self):
security_groups = self.properties[self.SECURITY_GROUPS] security_groups = self.properties[self.SECURITY_GROUPS]

View File

@ -521,14 +521,12 @@ class StackResource(resource.Resource):
if stack_identity is None: if stack_identity is None:
return return
try: with self.rpc_client().ignore_error_by_name('EntityNotFound'):
if self.abandon_in_progress: if self.abandon_in_progress:
self.rpc_client().abandon_stack(self.context, stack_identity) self.rpc_client().abandon_stack(self.context, stack_identity)
else: else:
self.rpc_client().delete_stack(self.context, stack_identity, self.rpc_client().delete_stack(self.context, stack_identity,
cast=False) cast=False)
except Exception as ex:
self.rpc_client().ignore_error_named(ex, 'EntityNotFound')
def handle_delete(self): def handle_delete(self):
return self.delete_nested() return self.delete_nested()

View File

@ -15,6 +15,9 @@
"""Client side of the heat engine RPC API.""" """Client side of the heat engine RPC API."""
import warnings
from oslo_utils import excutils
from oslo_utils import reflection from oslo_utils import reflection
from heat.common import messaging from heat.common import messaging
@ -100,14 +103,26 @@ class EngineClient(object):
error_name = reflection.get_class_name(error, fully_qualified=False) error_name = reflection.get_class_name(error, fully_qualified=False)
return error_name.split('_Remote')[0] return error_name.split('_Remote')[0]
def ignore_error_by_name(self, name):
"""Returns a context manager that filters exceptions with a given name.
:param name: Name to compare the local exception name to.
"""
def error_name_matches(err):
return self.local_error_name(err) == name
return excutils.exception_filter(error_name_matches)
def ignore_error_named(self, error, name): def ignore_error_named(self, error, name):
"""Raises the error unless its local name matches the supplied name. """Raises the error unless its local name matches the supplied name.
:param error: Remote raised error to derive the local name from. :param error: Remote raised error to derive the local name from.
:param name: Name to compare local name to. :param name: Name to compare local name to.
""" """
if self.local_error_name(error) != name: warnings.warn("Use ignore_error_by_name() to get a context manager "
raise error "instead.",
DeprecationWarning)
return self.ignore_error_by_name(name)(error)
def identify_stack(self, ctxt, stack_name): def identify_stack(self, ctxt, stack_name):
"""Returns the full stack identifier for a single, live stack. """Returns the full stack identifier for a single, live stack.

View File

@ -11,6 +11,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import contextlib
import email import email
import uuid import uuid
@ -99,7 +100,17 @@ class MultipartMimeTest(common.HeatTestCase):
'type': 'text' 'type': 'text'
}] }]
self.init_config(parts=parts) self.init_config(parts=parts)
@contextlib.contextmanager
def exc_filter():
try:
yield
except exc.NotFound:
pass
self.rpc_client.ignore_error_by_name.return_value = exc_filter()
self.rpc_client.show_software_config.side_effect = exc.NotFound() self.rpc_client.show_software_config.side_effect = exc.NotFound()
result = self.config.get_message() result = self.config.get_message()
self.assertEqual( self.assertEqual(

View File

@ -11,6 +11,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import contextlib
import mock import mock
import six import six
@ -59,6 +60,15 @@ class SoftwareComponentTest(common.HeatTestCase):
self.rpc_client = mock.MagicMock() self.rpc_client = mock.MagicMock()
self.component._rpc_client = self.rpc_client self.component._rpc_client = self.rpc_client
@contextlib.contextmanager
def exc_filter(*args):
try:
yield
except exc.NotFound:
pass
self.rpc_client.ignore_error_by_name.side_effect = exc_filter
def test_handle_create(self): def test_handle_create(self):
config_id = 'c8a19429-7fde-47ea-a42f-40045488226c' config_id = 'c8a19429-7fde-47ea-a42f-40045488226c'
value = {'id': config_id} value = {'id': config_id}

View File

@ -11,6 +11,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import contextlib
import mock import mock
from heat.common import exception as exc from heat.common import exception as exc
@ -45,6 +46,15 @@ class SoftwareConfigTest(common.HeatTestCase):
self.rpc_client = mock.MagicMock() self.rpc_client = mock.MagicMock()
self.config._rpc_client = self.rpc_client self.config._rpc_client = self.rpc_client
@contextlib.contextmanager
def exc_filter(*args):
try:
yield
except exc.NotFound:
pass
self.rpc_client.ignore_error_by_name.side_effect = exc_filter
def test_handle_create(self): def test_handle_create(self):
config_id = 'c8a19429-7fde-47ea-a42f-40045488226c' config_id = 'c8a19429-7fde-47ea-a42f-40045488226c'
value = {'id': config_id} value = {'id': config_id}

View File

@ -11,6 +11,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import contextlib
import copy import copy
import re import re
import uuid import uuid
@ -204,6 +205,15 @@ class SoftwareDeploymentTest(common.HeatTestCase):
self.rpc_client = mock.MagicMock() self.rpc_client = mock.MagicMock()
self.deployment._rpc_client = self.rpc_client self.deployment._rpc_client = self.rpc_client
@contextlib.contextmanager
def exc_filter(*args):
try:
yield
except exc.NotFound:
pass
self.rpc_client.ignore_error_by_name.side_effect = exc_filter
def test_validate(self): def test_validate(self):
template = dict(self.template_with_server) template = dict(self.template_with_server)
props = template['Resources']['server']['Properties'] props = template['Resources']['server']['Properties']
@ -1188,7 +1198,11 @@ class SoftwareDeploymentTest(common.HeatTestCase):
}], }],
'outputs': [], 'outputs': [],
} }
self.rpc_client.show_software_config.return_value = config
def show_sw_config(*args):
return config.copy()
self.rpc_client.show_software_config.side_effect = show_sw_config
mock_sd = self.mock_deployment() mock_sd = self.mock_deployment()
self.rpc_client.show_software_deployment.return_value = mock_sd self.rpc_client.show_software_deployment.return_value = mock_sd

View File

@ -12,6 +12,7 @@
# under the License. # under the License.
import collections import collections
import contextlib
import copy import copy
import mock import mock
@ -686,6 +687,14 @@ class ServersTest(common.HeatTestCase):
self.rpc_client = mock.MagicMock() self.rpc_client = mock.MagicMock()
server._rpc_client = self.rpc_client server._rpc_client = self.rpc_client
@contextlib.contextmanager
def exc_filter(*args):
try:
yield
except exception.NotFound:
pass
self.rpc_client.ignore_error_by_name.side_effect = exc_filter
self.rpc_client.show_software_config.side_effect = exception.NotFound self.rpc_client.show_software_config.side_effect = exception.NotFound
mock_create = self.patchobject(self.fc.servers, 'create', mock_create = self.patchobject(self.fc.servers, 'create',
return_value=return_server) return_value=return_server)

View File

@ -56,6 +56,24 @@ class EngineRpcAPITestCase(common.HeatTestCase):
reflection.get_class_name(exr, fully_qualified=False)) reflection.get_class_name(exr, fully_qualified=False))
self.assertEqual('NotFound', self.rpcapi.local_error_name(exr)) self.assertEqual('NotFound', self.rpcapi.local_error_name(exr))
def test_ignore_error_by_name(self):
ex = exception.NotFound()
exr = self._to_remote_error(ex)
filter_exc = self.rpcapi.ignore_error_by_name('NotFound')
with filter_exc:
raise ex
with filter_exc:
raise exr
def should_raise(exc):
with self.rpcapi.ignore_error_by_name('NotSupported'):
raise exc
self.assertRaises(exception.NotFound, should_raise, ex)
self.assertRaises(exception.NotFound, should_raise, exr)
def test_ignore_error_named(self): def test_ignore_error_named(self):
ex = exception.NotFound() ex = exception.NotFound()
exr = self._to_remote_error(ex) exr = self._to_remote_error(ex)

View File

@ -11,6 +11,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import contextlib
import json import json
import uuid import uuid
@ -204,7 +205,16 @@ class StackResourceTest(StackResourceBaseTest):
self.assertEqual({}, ret) self.assertEqual({}, ret)
def test_abandon_nested_sends_rpc_abandon(self): def test_abandon_nested_sends_rpc_abandon(self):
rpcc = mock.Mock() rpcc = mock.MagicMock()
@contextlib.contextmanager
def exc_filter(*args):
try:
yield
except exception.NotFound:
pass
rpcc.ignore_error_by_name.side_effect = exc_filter
self.parent_resource.rpc_client = rpcc self.parent_resource.rpc_client = rpcc
self.parent_resource.resource_id = 'fake_id' self.parent_resource.resource_id = 'fake_id'
@ -509,8 +519,17 @@ class StackResourceTest(StackResourceBaseTest):
def test_delete_nested_not_found_nested_stack(self): def test_delete_nested_not_found_nested_stack(self):
self.parent_resource.resource_id = 'fake_id' self.parent_resource.resource_id = 'fake_id'
rpcc = mock.Mock() rpcc = mock.MagicMock()
self.parent_resource.rpc_client = rpcc self.parent_resource.rpc_client = rpcc
@contextlib.contextmanager
def exc_filter(*args):
try:
yield
except exception.NotFound:
pass
rpcc.return_value.ignore_error_by_name.side_effect = exc_filter
rpcc.return_value.delete_stack = mock.Mock( rpcc.return_value.delete_stack = mock.Mock(
side_effect=exception.NotFound()) side_effect=exception.NotFound())
self.assertIsNone(self.parent_resource.delete_nested()) self.assertIsNone(self.parent_resource.delete_nested())