summaryrefslogtreecommitdiffstats
path: root/nova/openstack
diff options
context:
space:
mode:
authorAndrew Bogott <abogott@wikimedia.org>2012-06-28 19:18:20 -0500
committerAndrew Bogott <abogott@wikimedia.org>2012-07-06 22:08:42 -0500
commitaa87cc10d6a02d74f1466dc1a4e0e39c1a0878da (patch)
treefcc71796e895df9d2dfcd239b7ea208c4717fae8 /nova/openstack
parent642ec634c3beeb69175d0df12bcec8833e7efae5 (diff)
downloadnova-aa87cc10d6a02d74f1466dc1a4e0e39c1a0878da.tar.gz
nova-aa87cc10d6a02d74f1466dc1a4e0e39c1a0878da.tar.xz
nova-aa87cc10d6a02d74f1466dc1a4e0e39c1a0878da.zip
Switch to common notifiers.
- Added notifier files to openstack-common.conf, merged from common. - Import notifiers from common rather than nova. - Removed nova-specific notifier code. - Update some driver paths to refer to the new location within nova. Should be a no-op, as the common notification code is freshly copied from Nova. Change-Id: Icdf892bc3826b683fc74f22ead00939beda2859f
Diffstat (limited to 'nova/openstack')
-rw-r--r--nova/openstack/common/context.py81
-rw-r--r--nova/openstack/common/log.py2
-rw-r--r--nova/openstack/common/notifier/__init__.py14
-rw-r--r--nova/openstack/common/notifier/api.py141
-rw-r--r--nova/openstack/common/notifier/list_notifier.py116
-rw-r--r--nova/openstack/common/notifier/log_notifier.py34
-rw-r--r--nova/openstack/common/notifier/no_op_notifier.py19
-rw-r--r--nova/openstack/common/notifier/rabbit_notifier.py45
-rw-r--r--nova/openstack/common/notifier/test_notifier.py22
9 files changed, 473 insertions, 1 deletions
diff --git a/nova/openstack/common/context.py b/nova/openstack/common/context.py
new file mode 100644
index 000000000..dd7dd04c3
--- /dev/null
+++ b/nova/openstack/common/context.py
@@ -0,0 +1,81 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""
+Simple class that stores security context information in the web request.
+
+Projects should subclass this class if they wish to enhance the request
+context or provide additional information in their specific WSGI pipeline.
+"""
+
+import itertools
+import uuid
+
+
+def generate_request_id():
+ return 'req-' + str(uuid.uuid4())
+
+
+class RequestContext(object):
+
+ """
+ Stores information about the security context under which the user
+ accesses the system, as well as additional request information.
+ """
+
+ def __init__(self, auth_tok=None, user=None, tenant=None, is_admin=False,
+ read_only=False, show_deleted=False, request_id=None):
+ self.auth_tok = auth_tok
+ self.user = user
+ self.tenant = tenant
+ self.is_admin = is_admin
+ self.read_only = read_only
+ self.show_deleted = show_deleted
+ if not request_id:
+ request_id = generate_request_id()
+ self.request_id = request_id
+
+ def to_dict(self):
+ return {'user': self.user,
+ 'tenant': self.tenant,
+ 'is_admin': self.is_admin,
+ 'read_only': self.read_only,
+ 'show_deleted': self.show_deleted,
+ 'auth_token': self.auth_tok,
+ 'request_id': self.request_id}
+
+
+def get_admin_context(show_deleted="no"):
+ context = RequestContext(None,
+ tenant=None,
+ is_admin=True,
+ show_deleted=show_deleted)
+ return context
+
+
+def get_context_from_function_and_args(function, args, kwargs):
+ """Find an arg of type RequestContext and return it.
+
+ This is useful in a couple of decorators where we don't
+ know much about the function we're wrapping.
+ """
+
+ for arg in itertools.chain(kwargs.values(), args):
+ if isinstance(arg, RequestContext):
+ return arg
+
+ return None
diff --git a/nova/openstack/common/log.py b/nova/openstack/common/log.py
index 42491b717..b25a4eefc 100644
--- a/nova/openstack/common/log.py
+++ b/nova/openstack/common/log.py
@@ -40,10 +40,10 @@ import stat
import sys
import traceback
-from nova import notifier
from nova.openstack.common import cfg
from nova.openstack.common import jsonutils
from nova.openstack.common import local
+from nova.openstack.common import notifier
log_opts = [
diff --git a/nova/openstack/common/notifier/__init__.py b/nova/openstack/common/notifier/__init__.py
new file mode 100644
index 000000000..482d54e4f
--- /dev/null
+++ b/nova/openstack/common/notifier/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2011 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
diff --git a/nova/openstack/common/notifier/api.py b/nova/openstack/common/notifier/api.py
new file mode 100644
index 000000000..4a09d0ac2
--- /dev/null
+++ b/nova/openstack/common/notifier/api.py
@@ -0,0 +1,141 @@
+# Copyright 2011 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import inspect
+import uuid
+
+from nova.openstack.common import cfg
+from nova.openstack.common import context
+from nova.openstack.common import importutils
+from nova.openstack.common import jsonutils
+from nova.openstack.common import log as logging
+from nova.openstack.common import timeutils
+
+
+LOG = logging.getLogger(__name__)
+
+notifier_opts = [
+ cfg.StrOpt('notification_driver',
+ default='nova.openstack.common.notifier.no_op_notifier',
+ help='Default driver for sending notifications'),
+ cfg.StrOpt('default_notification_level',
+ default='INFO',
+ help='Default notification level for outgoing notifications'),
+ cfg.StrOpt('default_publisher_id',
+ default='$host',
+ help='Default publisher_id for outgoing notifications'),
+ ]
+
+CONF = cfg.CONF
+CONF.register_opts(notifier_opts)
+
+WARN = 'WARN'
+INFO = 'INFO'
+ERROR = 'ERROR'
+CRITICAL = 'CRITICAL'
+DEBUG = 'DEBUG'
+
+log_levels = (DEBUG, WARN, INFO, ERROR, CRITICAL)
+
+
+class BadPriorityException(Exception):
+ pass
+
+
+def notify_decorator(name, fn):
+ """ decorator for notify which is used from utils.monkey_patch()
+
+ :param name: name of the function
+ :param function: - object of the function
+ :returns: function -- decorated function
+
+ """
+ def wrapped_func(*args, **kwarg):
+ body = {}
+ body['args'] = []
+ body['kwarg'] = {}
+ for arg in args:
+ body['args'].append(arg)
+ for key in kwarg:
+ body['kwarg'][key] = kwarg[key]
+
+ ctxt = context.get_context_from_function_and_args(fn, args, kwarg)
+ notify(ctxt,
+ CONF.default_publisher_id,
+ name,
+ CONF.default_notification_level,
+ body)
+ return fn(*args, **kwarg)
+ return wrapped_func
+
+
+def publisher_id(service, host=None):
+ if not host:
+ host = CONF.host
+ return "%s.%s" % (service, host)
+
+
+def notify(context, publisher_id, event_type, priority, payload):
+ """Sends a notification using the specified driver
+
+ :param publisher_id: the source worker_type.host of the message
+ :param event_type: the literal type of event (ex. Instance Creation)
+ :param priority: patterned after the enumeration of Python logging
+ levels in the set (DEBUG, WARN, INFO, ERROR, CRITICAL)
+ :param payload: A python dictionary of attributes
+
+ Outgoing message format includes the above parameters, and appends the
+ following:
+
+ message_id
+ a UUID representing the id for this notification
+
+ timestamp
+ the GMT timestamp the notification was sent at
+
+ The composite message will be constructed as a dictionary of the above
+ attributes, which will then be sent via the transport mechanism defined
+ by the driver.
+
+ Message example::
+
+ {'message_id': str(uuid.uuid4()),
+ 'publisher_id': 'compute.host1',
+ 'timestamp': timeutils.utcnow(),
+ 'priority': 'WARN',
+ 'event_type': 'compute.create_instance',
+ 'payload': {'instance_id': 12, ... }}
+
+ """
+ if priority not in log_levels:
+ raise BadPriorityException(
+ _('%s not in valid priorities') % priority)
+
+ # Ensure everything is JSON serializable.
+ payload = jsonutils.to_primitive(payload, convert_instances=True)
+
+ driver = importutils.import_module(CONF.notification_driver)
+ msg = dict(message_id=str(uuid.uuid4()),
+ publisher_id=publisher_id,
+ event_type=event_type,
+ priority=priority,
+ payload=payload,
+ timestamp=str(timeutils.utcnow()))
+ try:
+ driver.notify(context, msg)
+ except Exception, e:
+ LOG.exception(_("Problem '%(e)s' attempting to "
+ "send to notification system. Payload=%(payload)s") %
+ locals())
diff --git a/nova/openstack/common/notifier/list_notifier.py b/nova/openstack/common/notifier/list_notifier.py
new file mode 100644
index 000000000..7c2ec6e6d
--- /dev/null
+++ b/nova/openstack/common/notifier/list_notifier.py
@@ -0,0 +1,116 @@
+# Copyright 2011 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from nova.openstack.common import cfg
+from nova.openstack.common import importutils
+from nova.openstack.common import log as logging
+
+
+list_notifier_drivers_opt = cfg.MultiStrOpt('list_notifier_drivers',
+ default=['nova.openstack.common.notifier.no_op_notifier'],
+ help='List of drivers to send notifications')
+
+CONF = cfg.CONF
+CONF.register_opt(list_notifier_drivers_opt)
+
+LOG = logging.getLogger(__name__)
+
+drivers = None
+
+
+class ImportFailureNotifier(object):
+ """Noisily re-raises some exception over-and-over when notify is called."""
+
+ def __init__(self, exception):
+ self.exception = exception
+
+ def notify(self, context, message):
+ raise self.exception
+
+
+def _get_drivers():
+ """Instantiates and returns drivers based on the flag values."""
+ global drivers
+ if drivers is None:
+ drivers = []
+ for notification_driver in CONF.list_notifier_drivers:
+ try:
+ drivers.append(importutils.import_module(notification_driver))
+ except ImportError as e:
+ drivers.append(ImportFailureNotifier(e))
+ return drivers
+
+
+def add_driver(notification_driver):
+ """Add a notification driver at runtime."""
+ # Make sure the driver list is initialized.
+ _get_drivers()
+ if isinstance(notification_driver, basestring):
+ # Load and add
+ try:
+ drivers.append(importutils.import_module(notification_driver))
+ except ImportError as e:
+ drivers.append(ImportFailureNotifier(e))
+ else:
+ # Driver is already loaded; just add the object.
+ drivers.append(notification_driver)
+
+
+def _object_name(obj):
+ name = []
+ if hasattr(obj, '__module__'):
+ name.append(obj.__module__)
+ if hasattr(obj, '__name__'):
+ name.append(obj.__name__)
+ else:
+ name.append(obj.__class__.__name__)
+ return '.'.join(name)
+
+
+def remove_driver(notification_driver):
+ """Remove a notification driver at runtime."""
+ # Make sure the driver list is initialized.
+ _get_drivers()
+ removed = False
+ if notification_driver in drivers:
+ # We're removing an object. Easy.
+ drivers.remove(notification_driver)
+ removed = True
+ else:
+ # We're removing a driver by name. Search for it.
+ for driver in drivers:
+ if _object_name(driver) == notification_driver:
+ drivers.remove(driver)
+ removed = True
+
+ if not removed:
+ raise ValueError("Cannot remove; %s is not in list" %
+ notification_driver)
+
+
+def notify(context, message):
+ """Passes notification to multiple notifiers in a list."""
+ for driver in _get_drivers():
+ try:
+ driver.notify(context, message)
+ except Exception as e:
+ LOG.exception(_("Problem '%(e)s' attempting to send to "
+ "notification driver %(driver)s."), locals())
+
+
+def _reset_drivers():
+ """Used by unit tests to reset the drivers."""
+ global drivers
+ drivers = None
diff --git a/nova/openstack/common/notifier/log_notifier.py b/nova/openstack/common/notifier/log_notifier.py
new file mode 100644
index 000000000..5bf0ce3b8
--- /dev/null
+++ b/nova/openstack/common/notifier/log_notifier.py
@@ -0,0 +1,34 @@
+# Copyright 2011 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import json
+
+from nova.openstack.common import cfg
+from nova.openstack.common import log as logging
+
+
+CONF = cfg.CONF
+
+
+def notify(_context, message):
+ """Notifies the recipient of the desired event given the model.
+ Log notifications using openstack's default logging system"""
+
+ priority = message.get('priority',
+ CONF.default_notification_level)
+ priority = priority.lower()
+ logger = logging.getLogger(
+ 'nova.openstack.common.notification.%s' % message['event_type'])
+ getattr(logger, priority)(json.dumps(message))
diff --git a/nova/openstack/common/notifier/no_op_notifier.py b/nova/openstack/common/notifier/no_op_notifier.py
new file mode 100644
index 000000000..ee1ddbdca
--- /dev/null
+++ b/nova/openstack/common/notifier/no_op_notifier.py
@@ -0,0 +1,19 @@
+# Copyright 2011 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+def notify(_context, message):
+ """Notifies the recipient of the desired event given the model"""
+ pass
diff --git a/nova/openstack/common/notifier/rabbit_notifier.py b/nova/openstack/common/notifier/rabbit_notifier.py
new file mode 100644
index 000000000..a74e29480
--- /dev/null
+++ b/nova/openstack/common/notifier/rabbit_notifier.py
@@ -0,0 +1,45 @@
+# Copyright 2011 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+from nova.openstack.common import cfg
+from nova.openstack.common import context as req_context
+from nova.openstack.common import log as logging
+from nova.openstack.common import rpc
+
+LOG = logging.getLogger(__name__)
+
+notification_topic_opt = cfg.ListOpt('notification_topics',
+ default=['notifications', ],
+ help='AMQP topic used for openstack notifications')
+
+CONF = cfg.CONF
+CONF.register_opt(notification_topic_opt)
+
+
+def notify(context, message):
+ """Sends a notification to the RabbitMQ"""
+ if not context:
+ context = req_context.get_admin_context()
+ priority = message.get('priority',
+ CONF.default_notification_level)
+ priority = priority.lower()
+ for topic in CONF.notification_topics:
+ topic = '%s.%s' % (topic, priority)
+ try:
+ rpc.notify(context, topic, message)
+ except Exception, e:
+ LOG.exception(_("Could not send notification to %(topic)s. "
+ "Payload=%(message)s"), locals())
diff --git a/nova/openstack/common/notifier/test_notifier.py b/nova/openstack/common/notifier/test_notifier.py
new file mode 100644
index 000000000..5e348803d
--- /dev/null
+++ b/nova/openstack/common/notifier/test_notifier.py
@@ -0,0 +1,22 @@
+# Copyright 2011 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+NOTIFICATIONS = []
+
+
+def notify(_context, message):
+ """Test notifier, stores notifications in memory for unittests."""
+ NOTIFICATIONS.append(message)