Another short one. As mostly anyone that has had to maintain an OpenStack cluster will know, many of the OpenStack
services has the ability to report notifications via the message bus. These notifications will be enabled in most
production deployments but DevStack doesn’t enable them by default. Fortunately enabling them is pretty painless. If we
wanted to enable the notifications in, say, Keystone, we can do so by configuring the following in
/etc/keystone/keystone.conf
:
[oslo_messaging_notifications]
transport_url = rabbit://stackrabbit:***@***:5672/
driver = messagingv2
These options are provided by the oslo.messaging project, which virtually every OpenStack service harnesses.
Once you’ve enabled them though, you what can you do with them? Using the messagingv2
means they’re published over the
AMQP message bus (and the message is enveloped, which is the difference to messaging
), however, they’re not exposed
via HTTP APIs or similar. We could whip out a telemetry tool like Ceilometer but that seems overkill for a
quick bit of debugging. Well it turns out oslo.messaging provides utilities for both creating notifications and
listening to them. Writing a listener is relatively simple:
import json
import logging
from oslo_config import cfg
import oslo_messaging
cfg.CONF()
logging.basicConfig(level=logging.INFO)
LOG = logging.getLogger()
URL = 'rabbit://stackrabbit:***@***:5672/'
class NotificationEndpoint:
def _notify(self, ctxt, publisher_id, event_type, payload, metadata):
LOG.info('notification received %s:%s' % (publisher_id, event_type))
output = json.dumps(
{
"payload": payload,
"publisher_id": publisher_id,
"event_type": event_type,
},
indent=4,
)
print(output)
audit = _notify
debug = _notify
info = _notify
warn = _notify
error = _notify
critical = _notify
sample = _notify
def main():
transport = oslo_messaging.get_notification_transport(cfg.CONF, url=URL)
targets = [oslo_messaging.Target(topic='notifications')]
endpoints = [NotificationEndpoint()]
server = oslo_messaging.get_notification_listener(
transport,
targets,
endpoints,
executor='threading',
)
LOG.info("messaging starting")
server.start()
LOG.info("messaging started")
server.wait()
LOG.info("exit")
if __name__ == '__main__':
main()
If you run this and then, say, attempt to authenticate with invalid credentials, you’ll see notifications arriving:
{
"payload": {
"typeURI": "http://schemas.dmtf.org/cloud/audit/1.0/event",
"eventType": "activity",
"id": "68c5b24b-89f0-5c93-abe4-5c3d6651fb00",
"eventTime": "2023-07-07T15:52:00.496156+0000",
"action": "authenticate",
"outcome": "failure",
"observer": {
"id": "08d95fde312344debbcc00106b139995",
"typeURI": "service/security"
},
"initiator": {
"id": "78fab588d9d34900a6c59fc22f3a2049",
"typeURI": "service/security/account/user",
"host": {
"address": "***",
"agent": "openstacksdk/1.3.0 keystoneauth1/5.2.1 python-requests/2.31.0 CPython/3.11.4"
},
"request_id": "req-0126be50-db3a-4975-a814-d717a5fdfdf8",
"user_id": "***",
"username": "***"
},
"target": {
"id": "fc220ce7-2787-5152-bf81-9943cb8ee24b",
"typeURI": "service/security/account/user"
}
},
"publisher_id": "identity.stephenfin-devstack",
"event_type": "identity.authenticate"
}
This can be extremely valuable for debugging since notifications will expose things that aren’t necessarily obvious from logs.