Merge "Notifications upon disable"
This commit is contained in:
commit
c7b2b11150
|
@ -80,15 +80,19 @@ class Manager(manager.Manager):
|
|||
ret['domain_id'])
|
||||
return ret
|
||||
|
||||
@notifications.disabled('project', public=False)
|
||||
def _disable_project(self, tenant_id):
|
||||
return self.token_api.delete_tokens_for_users(
|
||||
self.list_user_ids_for_project(tenant_id),
|
||||
project_id=tenant_id)
|
||||
|
||||
@notifications.updated('project')
|
||||
def update_project(self, tenant_id, tenant):
|
||||
tenant = tenant.copy()
|
||||
if 'enabled' in tenant:
|
||||
tenant['enabled'] = clean.project_enabled(tenant['enabled'])
|
||||
if not tenant.get('enabled', True):
|
||||
self.token_api.delete_tokens_for_users(
|
||||
self.list_user_ids_for_project(tenant_id),
|
||||
project_id=tenant_id)
|
||||
self._disable_project(tenant_id)
|
||||
ret = self.driver.update_project(tenant_id, tenant)
|
||||
self.get_project.invalidate(self, tenant_id)
|
||||
self.get_project_by_name.invalidate(self, ret['name'],
|
||||
|
@ -300,12 +304,17 @@ class Manager(manager.Manager):
|
|||
def list_domains(self, hints=None):
|
||||
return self.driver.list_domains(hints or driver_hints.Hints())
|
||||
|
||||
@notifications.disabled('domain', public=False)
|
||||
def _disable_domain(self, domain_id):
|
||||
self.token_api.delete_tokens_for_domain(domain_id)
|
||||
|
||||
@notifications.updated('domain')
|
||||
def update_domain(self, domain_id, domain):
|
||||
ret = self.driver.update_domain(domain_id, domain)
|
||||
# disable owned users & projects when the API user specifically set
|
||||
# enabled=False
|
||||
if not domain.get('enabled', True):
|
||||
self.token_api.delete_tokens_for_domain(domain_id)
|
||||
self._disable_domain(domain_id)
|
||||
self.get_domain.invalidate(self, domain_id)
|
||||
self.get_domain_by_name.invalidate(self, ret['name'])
|
||||
return ret
|
||||
|
|
|
@ -23,7 +23,7 @@ from keystone.openstack.common.notifier import api as notifier_api
|
|||
LOG = log.getLogger(__name__)
|
||||
# NOTE(gyee): actions that can be notified. One must update this list whenever
|
||||
# a new action is supported.
|
||||
ACTIONS = frozenset(['created', 'deleted', 'updated'])
|
||||
ACTIONS = frozenset(['created', 'deleted', 'disabled', 'updated'])
|
||||
# resource types that can be notified
|
||||
SUBSCRIBERS = {}
|
||||
|
||||
|
@ -34,11 +34,17 @@ class ManagerNotificationWrapper(object):
|
|||
Sends a notification if the wrapped Manager method does not raise an
|
||||
``Exception`` (such as ``keystone.exception.NotFound``).
|
||||
|
||||
:param operation: one of the values from ACTIONS
|
||||
:param resource_type: type of resource being affected
|
||||
:param public: If True (default), the event will be sent to the notifier
|
||||
API. If False, the event will only be sent via
|
||||
notify_event_callbacks to in process listeners
|
||||
|
||||
"""
|
||||
def __init__(self, operation, resource_type):
|
||||
def __init__(self, operation, resource_type, public=True):
|
||||
self.operation = operation
|
||||
self.resource_type = resource_type
|
||||
self.public = public
|
||||
|
||||
def __call__(self, f):
|
||||
def wrapper(*args, **kwargs):
|
||||
|
@ -48,10 +54,11 @@ class ManagerNotificationWrapper(object):
|
|||
except Exception:
|
||||
raise
|
||||
else:
|
||||
_send_notification(
|
||||
send_notification(
|
||||
self.operation,
|
||||
self.resource_type,
|
||||
args[1]) # f(self, resource_id, ...)
|
||||
args[1], # f(self, resource_id, ...)
|
||||
public=self.public)
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
@ -67,6 +74,11 @@ def updated(*args, **kwargs):
|
|||
return ManagerNotificationWrapper('updated', *args, **kwargs)
|
||||
|
||||
|
||||
def disabled(*args, **kwargs):
|
||||
"""Decorator to send notifications when an object is disabled."""
|
||||
return ManagerNotificationWrapper('disabled', *args, **kwargs)
|
||||
|
||||
|
||||
def deleted(*args, **kwargs):
|
||||
"""Decorator to send notifications for ``Manager.delete_*`` methods."""
|
||||
return ManagerNotificationWrapper('deleted', *args, **kwargs)
|
||||
|
@ -126,7 +138,8 @@ def notify_event_callbacks(service, resource_type, operation, payload):
|
|||
cb(service, resource_type, operation, payload)
|
||||
|
||||
|
||||
def _send_notification(operation, resource_type, resource_id):
|
||||
def send_notification(operation, resource_type, resource_id,
|
||||
public=True):
|
||||
"""Send notification to inform observers about the affected resource.
|
||||
|
||||
This method doesn't raise an exception when sending the notification fails.
|
||||
|
@ -134,6 +147,10 @@ def _send_notification(operation, resource_type, resource_id):
|
|||
:param operation: operation being performed (created, updated, or deleted)
|
||||
:param resource_type: type of resource being operated on
|
||||
:param resource_id: ID of resource being operated on
|
||||
:param public: if True (default), the event will be sent
|
||||
to the notifier API.
|
||||
if False, the event will only be sent via
|
||||
notify_event_callbacks to in process listeners.
|
||||
"""
|
||||
context = {}
|
||||
payload = {'resource_info': resource_id}
|
||||
|
@ -146,10 +163,11 @@ def _send_notification(operation, resource_type, resource_id):
|
|||
|
||||
notify_event_callbacks(service, resource_type, operation, payload)
|
||||
|
||||
try:
|
||||
notifier_api.notify(
|
||||
context, publisher_id, event_type, notifier_api.INFO, payload)
|
||||
except Exception:
|
||||
LOG.exception(
|
||||
_('Failed to send %(res_id)s %(event_type)s notification'),
|
||||
{'res_id': resource_id, 'event_type': event_type})
|
||||
if public:
|
||||
try:
|
||||
notifier_api.notify(
|
||||
context, publisher_id, event_type, notifier_api.INFO, payload)
|
||||
except Exception:
|
||||
LOG.exception(
|
||||
_('Failed to send %(res_id)s %(event_type)s notification'),
|
||||
{'res_id': resource_id, 'event_type': event_type})
|
||||
|
|
|
@ -38,7 +38,8 @@ class NotificationsWrapperTestCase(tests.TestCase):
|
|||
self.exp_operation = None
|
||||
self.send_notification_called = False
|
||||
|
||||
def fake_notify(operation, resource_type, resource_id):
|
||||
def fake_notify(operation, resource_type, resource_id,
|
||||
public=True):
|
||||
self.assertEqual(self.exp_operation, operation)
|
||||
self.assertEqual(EXP_RESOURCE_TYPE, resource_type)
|
||||
self.assertEqual(self.exp_resource_id, resource_id)
|
||||
|
@ -47,7 +48,7 @@ class NotificationsWrapperTestCase(tests.TestCase):
|
|||
fixture = self.useFixture(moxstubout.MoxStubout())
|
||||
self.stubs = fixture.stubs
|
||||
|
||||
self.stubs.Set(notifications, '_send_notification', fake_notify)
|
||||
self.stubs.Set(notifications, 'send_notification', fake_notify)
|
||||
|
||||
@notifications.created(EXP_RESOURCE_TYPE)
|
||||
def create_resource(self, resource_id, data):
|
||||
|
@ -148,36 +149,62 @@ class NotificationsTestCase(tests.TestCase):
|
|||
|
||||
mod_path = 'keystone.notifications.notifier_api.notify'
|
||||
with mock.patch(mod_path) as mocked:
|
||||
notifications._send_notification(operation, resource_type,
|
||||
resource)
|
||||
notifications.send_notification(operation, resource_type,
|
||||
resource)
|
||||
mocked.assert_called_once_with(*expected_args)
|
||||
|
||||
|
||||
class NotificationsForEntities(test_v3.RestfulTestCase):
|
||||
def setUp(self):
|
||||
super(NotificationsForEntities, self).setUp()
|
||||
self._notifications = []
|
||||
|
||||
self.exp_resource_id = None
|
||||
self.exp_operation = None
|
||||
self.exp_resource_type = None
|
||||
self.send_notification_called = False
|
||||
|
||||
def fake_notify(operation, resource_type, resource_id):
|
||||
self.exp_resource_id = resource_id
|
||||
self.exp_operation = operation
|
||||
self.exp_resource_type = resource_type
|
||||
self.send_notification_called = True
|
||||
def fake_notify(operation, resource_type, resource_id,
|
||||
public=True):
|
||||
note = {
|
||||
'resource_id': resource_id,
|
||||
'operation': operation,
|
||||
'resource_type': resource_type,
|
||||
'send_notification_called': True,
|
||||
'public': public}
|
||||
self._notifications.append(note)
|
||||
|
||||
fixture = self.useFixture(moxstubout.MoxStubout())
|
||||
self.stubs = fixture.stubs
|
||||
|
||||
self.stubs.Set(notifications, '_send_notification', fake_notify)
|
||||
self.stubs.Set(notifications, 'send_notification', fake_notify)
|
||||
|
||||
def _assertLastNotify(self, resource_id, operation, resource_type):
|
||||
self.assertEqual(self.exp_operation, operation)
|
||||
self.assertEqual(self.exp_resource_id, resource_id)
|
||||
self.assertEqual(self.exp_resource_type, resource_type)
|
||||
self.assertTrue(self.send_notification_called)
|
||||
self.assertTrue(len(self._notifications) > 0)
|
||||
note = self._notifications[-1]
|
||||
self.assertEqual(note['operation'], operation)
|
||||
self.assertEqual(note['resource_id'], resource_id)
|
||||
self.assertEqual(note['resource_type'], resource_type)
|
||||
self.assertTrue(note['send_notification_called'])
|
||||
|
||||
def _assertNotifyNotSent(self, resource_id, operation, resource_type,
|
||||
public=True):
|
||||
unexpected = {
|
||||
'resource_id': resource_id,
|
||||
'operation': operation,
|
||||
'resource_type': resource_type,
|
||||
'send_notification_called': True,
|
||||
'public': public}
|
||||
for note in self._notifications:
|
||||
self.assertNotEqual(unexpected, note)
|
||||
|
||||
def _assertNotifySent(self, resource_id, operation, resource_type, public):
|
||||
expected = {
|
||||
'resource_id': resource_id,
|
||||
'operation': operation,
|
||||
'resource_type': resource_type,
|
||||
'send_notification_called': True,
|
||||
'public': public}
|
||||
for note in self._notifications:
|
||||
if expected == note:
|
||||
break
|
||||
else:
|
||||
self.fail("Notification not sent.")
|
||||
|
||||
def test_create_group(self):
|
||||
group_ref = self.new_group_ref(domain_id=self.domain_id)
|
||||
|
@ -237,6 +264,12 @@ class NotificationsForEntities(test_v3.RestfulTestCase):
|
|||
self.identity_api.delete_user(user_ref['id'])
|
||||
self._assertLastNotify(user_ref['id'], 'deleted', 'user')
|
||||
|
||||
def test_update_domain(self):
|
||||
domain_ref = self.new_domain_ref()
|
||||
self.assignment_api.create_domain(domain_ref['id'], domain_ref)
|
||||
self.assignment_api.update_domain(domain_ref['id'], domain_ref)
|
||||
self._assertLastNotify(domain_ref['id'], 'updated', 'domain')
|
||||
|
||||
def test_delete_trust(self):
|
||||
trustor = self.new_user_ref(domain_id=self.domain_id)
|
||||
self.identity_api.create_user(trustor['id'], trustor)
|
||||
|
@ -250,6 +283,14 @@ class NotificationsForEntities(test_v3.RestfulTestCase):
|
|||
self.trust_api.delete_trust(trust_ref['id'])
|
||||
self._assertLastNotify(trust_ref['id'], 'deleted', 'OS-TRUST:trust')
|
||||
|
||||
def test_disable_domain(self):
|
||||
domain_ref = self.new_domain_ref()
|
||||
self.identity_api.create_domain(domain_ref['id'], domain_ref)
|
||||
domain_ref['enabled'] = False
|
||||
self.identity_api.update_domain(domain_ref['id'], domain_ref)
|
||||
self._assertNotifySent(domain_ref['id'], 'disabled', 'domain',
|
||||
public=False)
|
||||
|
||||
def test_update_group(self):
|
||||
group_ref = self.new_group_ref(domain_id=self.domain_id)
|
||||
self.identity_api.create_group(group_ref['id'], group_ref)
|
||||
|
@ -260,7 +301,24 @@ class NotificationsForEntities(test_v3.RestfulTestCase):
|
|||
project_ref = self.new_project_ref(domain_id=self.domain_id)
|
||||
self.assignment_api.create_project(project_ref['id'], project_ref)
|
||||
self.assignment_api.update_project(project_ref['id'], project_ref)
|
||||
self._assertNotifySent(project_ref['id'], 'updated', 'project',
|
||||
public=True)
|
||||
|
||||
def test_disable_project(self):
|
||||
project_ref = self.new_project_ref(domain_id=self.domain_id)
|
||||
self.assignment_api.create_project(project_ref['id'], project_ref)
|
||||
project_ref['enabled'] = False
|
||||
self.assignment_api.update_project(project_ref['id'], project_ref)
|
||||
self._assertNotifySent(project_ref['id'], 'disabled', 'project',
|
||||
public=False)
|
||||
|
||||
def test_update_project_does_not_send_disable(self):
|
||||
project_ref = self.new_project_ref(domain_id=self.domain_id)
|
||||
self.assignment_api.create_project(project_ref['id'], project_ref)
|
||||
project_ref['enabled'] = True
|
||||
self.assignment_api.update_project(project_ref['id'], project_ref)
|
||||
self._assertLastNotify(project_ref['id'], 'updated', 'project')
|
||||
self._assertNotifyNotSent(project_ref['id'], 'disabled', 'project')
|
||||
|
||||
def test_update_role(self):
|
||||
role_ref = self.new_role_ref()
|
||||
|
|
Loading…
Reference in New Issue