Add role assignment notifications for identity

Keystone added CADF notifications for role assignments in Juno.
We should be able to meter these events from Ceilometer.

Spec is here:
http://specs.openstack.org/openstack/keystone-specs/specs/juno/role-assignment-notifications.html

Impl is here: https://review.openstack.org/#/c/112204/ (merged)

Change-Id: I64e9b66081e3d453347541fd231da7d8e2598eb3
This commit is contained in:
Steve Martinelli 2014-09-05 17:48:09 -04:00
parent d7065ff54f
commit 5f8cb7acc6
4 changed files with 122 additions and 11 deletions

View File

@ -136,3 +136,35 @@ class Authenticate(_Base):
@property
def event_types(self):
return [self.event_name]
class RoleAssignment(_Base):
"""Convert role assignment notifications into Samples."""
resource_type = 'role_assignment'
resource_name = '%s.%s' % (SERVICE, resource_type)
def process_notification(self, message):
# NOTE(stevemar): action is created.role_assignment
action = message['payload']['action']
event, resource_type = action.split(".")
# NOTE(stevemar): meter_name is identity.role_assignment.created
meter_name = '%s.%s.%s' % (SERVICE, resource_type, event)
yield sample.Sample.from_notification(
name=meter_name,
type=sample.TYPE_DELTA,
unit=self.resource_type,
volume=1,
resource_id=message['payload']['role'],
user_id=message['payload']['initiator']['id'],
project_id=None,
message=message)
@property
def event_types(self):
return [
'%s.created' % self.resource_name,
'%s.deleted' % self.resource_name,
]

View File

@ -22,11 +22,13 @@ from ceilometer import sample
NOW = datetime.datetime.isoformat(datetime.datetime.utcnow())
PROJECT_ID = u'project_id'
DOMAIN_ID = u'domain_id'
USER_ID = u'user_id'
ROLE_ID = u'role_id'
GROUP_ID = u'group_id'
TRUST_ID = u'trust_id'
PUBLISHER_ID = u'identity.node-n5x66lxdy67d'
ROLE_ASSIGNMENT = 'role_assignment'
def notification_for(resource_type, operation, resource_id):
@ -44,10 +46,9 @@ def notification_for(resource_type, operation, resource_id):
}
def authn_notification_for(outcome):
def cadf_format_notification():
return {
u'event_type': u'identity.authenticate',
u'event_type': u'some_identity_event',
u'message_id': u'1371a590-d5fd-448f-b3bb-a14dead6f4cb',
u'payload': {
u'typeURI': u'http://schemas.dmtf.org/cloud/audit/1.0/event',
@ -70,8 +71,8 @@ def authn_notification_for(outcome):
},
u'eventType': u'activity',
u'eventTime': u'2014-08-04T05:38:59.978898+0000',
u'action': u'authenticate',
u'outcome': outcome,
u'action': u'action_name',
u'outcome': 'success',
u'id': u'openstack:eca02fef-9394-4008-8fb3-c434133ca4b2'
},
u'priority': u'INFO',
@ -80,6 +81,48 @@ def authn_notification_for(outcome):
}
def authn_notification_for(outcome):
base = cadf_format_notification()
base['event_type'] = 'identity.authenticate'
base['payload']['action'] = 'authenticate'
base['payload']['outcome'] = outcome
return base
def notification_for_role_change(action, project, user):
"""Create a notifications for a role_assignment
In this case, action is either 'created' or 'deleted'. Also
in a role_assignment notifications, in the payload portion,
there may be a 'domain' key or a 'project' key, never both.
The same holds for the 'user' key and 'group' key.
There must always be a 'role'.
"""
base = cadf_format_notification()
# NOTE(stevemar): i.e. created.role_assignment
action_name = '%s.%s' % (action, ROLE_ASSIGNMENT)
event, resource_type = action_name.split(".")
# NOTE(stevemar): i.e. identity.role_assignment.created
event_name = '%s.%s.%s' % (notifications.SERVICE, resource_type, event)
base['event_type'] = event_name
base['payload']['action'] = action_name
base['payload']['role'] = ROLE_ID
base['payload']['inherited_to_projects'] = False
if project:
base['payload']['project'] = PROJECT_ID
else:
base['payload']['domain'] = DOMAIN_ID
if user:
base['payload']['user'] = USER_ID
else:
base['payload']['group'] = GROUP_ID
return base
class TestCRUDNotification(base.BaseTestCase):
def _verify_common_sample(self, s):
@ -156,25 +199,58 @@ class TestAuthenticationNotification(base.BaseTestCase):
self.assertEqual(sample.TYPE_DELTA, s.type)
self.assertIsNone(s.project_id)
self.assertEqual(USER_ID, s.user_id)
self.assertEqual(USER_ID, s.resource_id)
self.assertEqual('user', s.unit)
self.assertEqual(1, s.volume)
metadata = s.resource_metadata
self.assertEqual(PUBLISHER_ID, metadata.get('host'))
def _test_operation(self, outcome):
def _test_authn_operation(self, outcome):
notif = authn_notification_for(outcome)
handler = notifications.Authenticate(mock.Mock())
data = list(handler.process_notification(notif))
self.assertEqual(1, len(data))
name = '%s.%s.%s' % (notifications.SERVICE, 'authenticate', outcome)
self.assertEqual(name, data[0].name)
self.assertEqual(USER_ID, data[0].resource_id)
self.assertEqual('user', data[0].unit)
self._verify_common_sample(data[0])
def _test_role_assignment_operation(self, action, project, user):
notif = notification_for_role_change(action, project, user)
handler = notifications.RoleAssignment(mock.Mock())
data = list(handler.process_notification(notif))
self.assertEqual(1, len(data))
name = '%s.%s.%s' % (notifications.SERVICE, ROLE_ASSIGNMENT, action)
self.assertEqual(name, data[0].name)
self.assertEqual(ROLE_ID, data[0].resource_id)
self.assertEqual(ROLE_ASSIGNMENT, data[0].unit)
metadata = data[0].resource_metadata
if project:
self.assertEqual(PROJECT_ID, metadata.get('project'))
else:
self.assertEqual(DOMAIN_ID, metadata.get('domain'))
if user:
self.assertEqual(USER_ID, metadata.get('user'))
else:
self.assertEqual(GROUP_ID, metadata.get('group'))
self._verify_common_sample(data[0])
def test_authn_success(self):
self._test_operation('success')
self._test_authn_operation('success')
def test_authn_failure(self):
self._test_operation('failure')
self._test_authn_operation('failure')
def test_authn_pending(self):
self._test_operation('pending')
self._test_authn_operation('pending')
def test_create_role_assignment_group_domain(self):
self._test_role_assignment_operation('created', False, False)
def test_delete_role_assignment_group_domain(self):
self._test_role_assignment_operation('deleted', False, False)
def test_create_role_assignment_user_project(self):
self._test_role_assignment_operation('created', True, True)
def test_delete_role_assignment_user_project(self):
self._test_role_assignment_operation('deleted', True, True)

View File

@ -203,6 +203,8 @@ Name Type Unit Resource Origi
identity.authenticate.success Delta user user ID notification User successfully authenticates
identity.authenticate.pending Delta user user ID notification User pending authentication
identity.authenticate.failure Delta user user ID notification User failed authentication
identity.role_assignment.created Delta role_assignment role ID notification A role is added to an actor on a target
identity.role_assignment.deleted Delta role_assignment role ID notification A role is removed from an actor on a target
identity.user.created Delta user user ID notification A user is created
identity.user.deleted Delta user user ID notification A user is deleted
identity.user.updated Delta user user ID notification A user is updated

View File

@ -57,6 +57,7 @@ ceilometer.notification =
role = ceilometer.identity.notifications:Role
project = ceilometer.identity.notifications:Project
trust = ceilometer.identity.notifications:Trust
role_assignment = ceilometer.identity.notifications:RoleAssignment
image_crud = ceilometer.image.notifications:ImageCRUD
image = ceilometer.image.notifications:Image
image_size = ceilometer.image.notifications:ImageSize