Updated OpenStack Common
(Fixed issue with notifier package)
This commit is contained in:
@@ -26,7 +26,7 @@ Usual usage in an openstack.common module:
|
|||||||
import gettext
|
import gettext
|
||||||
|
|
||||||
|
|
||||||
t = gettext.translation('openstack-common', 'locale', fallback=True)
|
t = gettext.translation('conductor', 'locale', fallback=True)
|
||||||
|
|
||||||
|
|
||||||
def _(msg):
|
def _(msg):
|
||||||
|
@@ -29,6 +29,7 @@ It also allows setting of formatting information through conf.
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import ConfigParser
|
||||||
import cStringIO
|
import cStringIO
|
||||||
import inspect
|
import inspect
|
||||||
import itertools
|
import itertools
|
||||||
@@ -87,11 +88,11 @@ logging_cli_opts = [
|
|||||||
metavar='PATH',
|
metavar='PATH',
|
||||||
deprecated_name='logfile',
|
deprecated_name='logfile',
|
||||||
help='(Optional) Name of log file to output to. '
|
help='(Optional) Name of log file to output to. '
|
||||||
'If not set, logging will go to stdout.'),
|
'If no default is set, logging will go to stdout.'),
|
||||||
cfg.StrOpt('log-dir',
|
cfg.StrOpt('log-dir',
|
||||||
deprecated_name='logdir',
|
deprecated_name='logdir',
|
||||||
help='(Optional) The directory to keep log files in '
|
help='(Optional) The base directory used for relative '
|
||||||
'(will be prepended to --log-file)'),
|
'--log-file paths'),
|
||||||
cfg.BoolOpt('use-syslog',
|
cfg.BoolOpt('use-syslog',
|
||||||
default=False,
|
default=False,
|
||||||
help='Use syslog for logging.'),
|
help='Use syslog for logging.'),
|
||||||
@@ -323,10 +324,30 @@ def _create_logging_excepthook(product_name):
|
|||||||
return logging_excepthook
|
return logging_excepthook
|
||||||
|
|
||||||
|
|
||||||
|
class LogConfigError(Exception):
|
||||||
|
|
||||||
|
message = _('Error loading logging config %(log_config)s: %(err_msg)s')
|
||||||
|
|
||||||
|
def __init__(self, log_config, err_msg):
|
||||||
|
self.log_config = log_config
|
||||||
|
self.err_msg = err_msg
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.message % dict(log_config=self.log_config,
|
||||||
|
err_msg=self.err_msg)
|
||||||
|
|
||||||
|
|
||||||
|
def _load_log_config(log_config):
|
||||||
|
try:
|
||||||
|
logging.config.fileConfig(log_config)
|
||||||
|
except ConfigParser.Error, exc:
|
||||||
|
raise LogConfigError(log_config, str(exc))
|
||||||
|
|
||||||
|
|
||||||
def setup(product_name):
|
def setup(product_name):
|
||||||
"""Setup logging."""
|
"""Setup logging."""
|
||||||
if CONF.log_config:
|
if CONF.log_config:
|
||||||
logging.config.fileConfig(CONF.log_config)
|
_load_log_config(CONF.log_config)
|
||||||
else:
|
else:
|
||||||
_setup_logging_from_conf()
|
_setup_logging_from_conf()
|
||||||
sys.excepthook = _create_logging_excepthook(product_name)
|
sys.excepthook = _create_logging_excepthook(product_name)
|
||||||
|
14
conductor/conductor/openstack/common/notifier/__init__.py
Normal file
14
conductor/conductor/openstack/common/notifier/__init__.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Copyright 2011 OpenStack Foundation.
|
||||||
|
# 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.
|
182
conductor/conductor/openstack/common/notifier/api.py
Normal file
182
conductor/conductor/openstack/common/notifier/api.py
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
# Copyright 2011 OpenStack Foundation.
|
||||||
|
# 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 uuid
|
||||||
|
|
||||||
|
from oslo.config import cfg
|
||||||
|
|
||||||
|
from conductor.openstack.common import context
|
||||||
|
from conductor.openstack.common.gettextutils import _
|
||||||
|
from conductor.openstack.common import importutils
|
||||||
|
from conductor.openstack.common import jsonutils
|
||||||
|
from conductor.openstack.common import log as logging
|
||||||
|
from conductor.openstack.common import timeutils
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
notifier_opts = [
|
||||||
|
cfg.MultiStrOpt('notification_driver',
|
||||||
|
default=[],
|
||||||
|
help='Driver or drivers to handle 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)
|
||||||
|
|
||||||
|
msg = dict(message_id=str(uuid.uuid4()),
|
||||||
|
publisher_id=publisher_id,
|
||||||
|
event_type=event_type,
|
||||||
|
priority=priority,
|
||||||
|
payload=payload,
|
||||||
|
timestamp=str(timeutils.utcnow()))
|
||||||
|
|
||||||
|
for driver in _get_drivers():
|
||||||
|
try:
|
||||||
|
driver.notify(context, msg)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.exception(_("Problem '%(e)s' attempting to "
|
||||||
|
"send to notification system. "
|
||||||
|
"Payload=%(payload)s")
|
||||||
|
% dict(e=e, payload=payload))
|
||||||
|
|
||||||
|
|
||||||
|
_drivers = None
|
||||||
|
|
||||||
|
|
||||||
|
def _get_drivers():
|
||||||
|
"""Instantiate, cache, and return drivers based on the CONF."""
|
||||||
|
global _drivers
|
||||||
|
if _drivers is None:
|
||||||
|
_drivers = {}
|
||||||
|
for notification_driver in CONF.notification_driver:
|
||||||
|
add_driver(notification_driver)
|
||||||
|
|
||||||
|
return _drivers.values()
|
||||||
|
|
||||||
|
|
||||||
|
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:
|
||||||
|
driver = importutils.import_module(notification_driver)
|
||||||
|
_drivers[notification_driver] = driver
|
||||||
|
except ImportError:
|
||||||
|
LOG.exception(_("Failed to load notifier %s. "
|
||||||
|
"These notifications will not be sent.") %
|
||||||
|
notification_driver)
|
||||||
|
else:
|
||||||
|
# Driver is already loaded; just add the object.
|
||||||
|
_drivers[notification_driver] = notification_driver
|
||||||
|
|
||||||
|
|
||||||
|
def _reset_drivers():
|
||||||
|
"""Used by unit tests to reset the drivers."""
|
||||||
|
global _drivers
|
||||||
|
_drivers = None
|
@@ -0,0 +1,35 @@
|
|||||||
|
# Copyright 2011 OpenStack Foundation.
|
||||||
|
# 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 oslo.config import cfg
|
||||||
|
|
||||||
|
from conductor.openstack.common import jsonutils
|
||||||
|
from conductor.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(
|
||||||
|
'conductor.openstack.common.notification.%s' %
|
||||||
|
message['event_type'])
|
||||||
|
getattr(logger, priority)(jsonutils.dumps(message))
|
@@ -0,0 +1,19 @@
|
|||||||
|
# Copyright 2011 OpenStack Foundation.
|
||||||
|
# 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
|
@@ -0,0 +1,46 @@
|
|||||||
|
# Copyright 2011 OpenStack Foundation.
|
||||||
|
# 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 oslo.config import cfg
|
||||||
|
|
||||||
|
from conductor.openstack.common import context as req_context
|
||||||
|
from conductor.openstack.common.gettextutils import _
|
||||||
|
from conductor.openstack.common import log as logging
|
||||||
|
from conductor.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 via RPC"""
|
||||||
|
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:
|
||||||
|
LOG.exception(_("Could not send notification to %(topic)s. "
|
||||||
|
"Payload=%(message)s"), locals())
|
@@ -0,0 +1,52 @@
|
|||||||
|
# Copyright 2011 OpenStack Foundation.
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
'''messaging based notification driver, with message envelopes'''
|
||||||
|
|
||||||
|
from oslo.config import cfg
|
||||||
|
|
||||||
|
from conductor.openstack.common import context as req_context
|
||||||
|
from conductor.openstack.common.gettextutils import _
|
||||||
|
from conductor.openstack.common import log as logging
|
||||||
|
from conductor.openstack.common import rpc
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
notification_topic_opt = cfg.ListOpt(
|
||||||
|
'topics', default=['notifications', ],
|
||||||
|
help='AMQP topic(s) used for openstack notifications')
|
||||||
|
|
||||||
|
opt_group = cfg.OptGroup(name='rpc_notifier2',
|
||||||
|
title='Options for rpc_notifier2')
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
CONF.register_group(opt_group)
|
||||||
|
CONF.register_opt(notification_topic_opt, opt_group)
|
||||||
|
|
||||||
|
|
||||||
|
def notify(context, message):
|
||||||
|
"""Sends a notification via RPC"""
|
||||||
|
if not context:
|
||||||
|
context = req_context.get_admin_context()
|
||||||
|
priority = message.get('priority',
|
||||||
|
CONF.default_notification_level)
|
||||||
|
priority = priority.lower()
|
||||||
|
for topic in CONF.rpc_notifier2.topics:
|
||||||
|
topic = '%s.%s' % (topic, priority)
|
||||||
|
try:
|
||||||
|
rpc.notify(context, topic, message, envelope=True)
|
||||||
|
except Exception:
|
||||||
|
LOG.exception(_("Could not send notification to %(topic)s. "
|
||||||
|
"Payload=%(message)s"), locals())
|
@@ -0,0 +1,22 @@
|
|||||||
|
# Copyright 2011 OpenStack Foundation.
|
||||||
|
# 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)
|
@@ -171,6 +171,14 @@ def generate_authors():
|
|||||||
" log --format='%aN <%aE>' | sort -u | "
|
" log --format='%aN <%aE>' | sort -u | "
|
||||||
"egrep -v '" + jenkins_email + "'")
|
"egrep -v '" + jenkins_email + "'")
|
||||||
changelog = _run_shell_command(git_log_cmd)
|
changelog = _run_shell_command(git_log_cmd)
|
||||||
|
signed_cmd = ("git log --git-dir=" + git_dir +
|
||||||
|
" | grep -i Co-authored-by: | sort -u")
|
||||||
|
signed_entries = _run_shell_command(signed_cmd)
|
||||||
|
if signed_entries:
|
||||||
|
new_entries = "\n".join(
|
||||||
|
[signed.split(":", 1)[1].strip()
|
||||||
|
for signed in signed_entries.split("\n") if signed])
|
||||||
|
changelog = "\n".join((changelog, new_entries))
|
||||||
mailmap = _parse_git_mailmap(git_dir)
|
mailmap = _parse_git_mailmap(git_dir)
|
||||||
with open(new_authors, 'w') as new_authors_fh:
|
with open(new_authors, 'w') as new_authors_fh:
|
||||||
new_authors_fh.write(canonicalize_emails(changelog, mailmap))
|
new_authors_fh.write(canonicalize_emails(changelog, mailmap))
|
||||||
|
@@ -59,10 +59,10 @@ CONF.register_opts(socket_opts)
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def run_server(application, port):
|
def run_server(application, port, **kwargs):
|
||||||
"""Run a WSGI server with the given application."""
|
"""Run a WSGI server with the given application."""
|
||||||
sock = eventlet.listen(('0.0.0.0', port))
|
sock = eventlet.listen(('0.0.0.0', port))
|
||||||
eventlet.wsgi.server(sock, application)
|
eventlet.wsgi.server(sock, application, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class Service(service.Service):
|
class Service(service.Service):
|
||||||
|
Reference in New Issue
Block a user