diff --git a/aodh/api/controllers/v2/alarms.py b/aodh/api/controllers/v2/alarms.py index 0174e59ed..c94666054 100644 --- a/aodh/api/controllers/v2/alarms.py +++ b/aodh/api/controllers/v2/alarms.py @@ -384,7 +384,7 @@ class Alarm(base.Base): @staticmethod def _is_trust_url(url): - return url.scheme in ('trust+http', 'trust+https') + return url.scheme.startswith('trust+') def update_actions(self, old_alarm=None): trustor_user_id = pecan.request.headers.get('X-User-Id') diff --git a/aodh/notifier/trust.py b/aodh/notifier/trust.py index c9bbdc37c..0cf24c34c 100644 --- a/aodh/notifier/trust.py +++ b/aodh/notifier/trust.py @@ -20,14 +20,12 @@ from aodh import keystone_client from aodh.notifier import rest -class TrustRestAlarmNotifier(rest.RestAlarmNotifier): - """Notifier supporting keystone trust authentication. +class TrustAlarmNotifierMixin(object): + """Mixin class to add Keystone trust support to an AlarmNotifier. - This alarm notifier is intended to be used to call an endpoint using - keystone authentication. It uses the aodh service user to - authenticate using the trust ID provided. - - The URL must be in the form trust+http://trust-id@host/action. + Provides a notify() method that interprets the trust ID and then calls + the parent class's notify(), passing the necessary authentication data in + the headers. """ def notify(self, action, alarm_id, alarm_name, severity, previous, current, @@ -45,6 +43,17 @@ class TrustRestAlarmNotifier(rest.RestAlarmNotifier): action.fragment) headers = {'X-Auth-Token': keystone_client.get_auth_token(client)} - super(TrustRestAlarmNotifier, self).notify( + super(TrustAlarmNotifierMixin, self).notify( action, alarm_id, alarm_name, severity, previous, current, reason, reason_data, headers) + + +class TrustRestAlarmNotifier(TrustAlarmNotifierMixin, rest.RestAlarmNotifier): + """Notifier supporting keystone trust authentication. + + This alarm notifier is intended to be used to call an endpoint using + keystone authentication. It uses the aodh service user to + authenticate using the trust ID provided. + + The URL must be in the form ``trust+http://trust-id@host/action``. + """ diff --git a/aodh/notifier/zaqar.py b/aodh/notifier/zaqar.py index d759f5590..92ef16225 100644 --- a/aodh/notifier/zaqar.py +++ b/aodh/notifier/zaqar.py @@ -22,6 +22,7 @@ import six.moves.urllib.parse as urlparse from aodh.i18n import _LE, _LI from aodh import keystone_client from aodh import notifier +from aodh.notifier import trust LOG = log.getLogger(__name__) @@ -144,7 +145,7 @@ class ZaqarAlarmNotifier(notifier.AlarmNotifier): 'current': current, 'reason': reason, 'reason_data': reason_data} message = dict(body=body) - self.notify_zaqar(action, message) + self.notify_zaqar(action, message, headers) @property def client(self): @@ -152,7 +153,7 @@ class ZaqarAlarmNotifier(notifier.AlarmNotifier): self._zclient = self.get_zaqar_client(self._get_client_conf()) return self._zclient - def notify_zaqar(self, action, message): + def notify_zaqar(self, action, message, headers=None): queue_info = urlparse.parse_qs(action.query) try: # NOTE(flwang): Try to get build a pre-signed client if user has @@ -187,3 +188,41 @@ class ZaqarAlarmNotifier(notifier.AlarmNotifier): LOG.error(_LE("Unknown error occurred; Failed to post message to" " Zaqar queue"), exc_info=True) + + +class TrustZaqarAlarmNotifier(trust.TrustAlarmNotifierMixin, + ZaqarAlarmNotifier): + """Zaqar notifier using a Keystone trust to post to user-defined queues. + + The URL must be in the form ``trust+zaqar://trust_id@?queue_name=example``. + """ + + def _get_client_conf(self, auth_token): + return { + 'auth_opts': { + 'backend': 'keystone', + 'options': { + 'os_auth_token': auth_token, + } + } + } + + def notify_zaqar(self, action, message, headers): + queue_info = urlparse.parse_qs(action.query) + try: + queue_name = queue_info.get('queue_name')[-1] + except IndexError: + LOG.error(_LE("Required 'queue_name' query option missing in" + " action %s"), + action) + return + + try: + conf = self._get_client_conf(headers['X-Auth-Token']) + client = self.get_zaqar_client(conf) + queue = client.queue(queue_name) + queue.post(message) + except Exception: + LOG.error(_LE("Unknown error occurred; Failed to post message to" + " Zaqar queue"), + exc_info=True) diff --git a/aodh/tests/unit/test_notifier.py b/aodh/tests/unit/test_notifier.py index 2b5682120..4df0592b8 100644 --- a/aodh/tests/unit/test_notifier.py +++ b/aodh/tests/unit/test_notifier.py @@ -405,6 +405,21 @@ class TestAlarmNotifier(tests_base.BaseTestCase): self.assertEqual(1, self.zaqar.subscriptions) self.assertEqual(1, self.zaqar.posts) + def test_trust_zaqar_notifier_action(self): + client = mock.MagicMock() + client.session.auth.get_access.return_value.auth_token = 'token_1234' + + self.useFixture( + mockpatch.Patch('aodh.keystone_client.get_trusted_client', + lambda *args: client)) + + action = 'trust+zaqar://trust-1234:delete@?queue_name=foobar-critical' + self._msg_notifier.sample({}, 'alarm.update', + self._notification(action)) + time.sleep(1) + self.assertEqual(0, self.zaqar.subscriptions) + self.assertEqual(1, self.zaqar.posts) + class FakeZaqarClient(object): diff --git a/setup.cfg b/setup.cfg index 9cc4160a1..321484408 100644 --- a/setup.cfg +++ b/setup.cfg @@ -102,6 +102,7 @@ aodh.notifier = trust+http = aodh.notifier.trust:TrustRestAlarmNotifier trust+https = aodh.notifier.trust:TrustRestAlarmNotifier zaqar = aodh.notifier.zaqar:ZaqarAlarmNotifier + trust+zaqar = aodh.notifier.zaqar:TrustZaqarAlarmNotifier wsgi_scripts = aodh-api = aodh.api.app:build_wsgi_app