# Copyright 2013 Hewlett-Packard Development Company, L.P. # # Author: Kiall Mac Innes # # 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. # # Copied: nova.notifications import abc from oslo_config import cfg from oslo_log import log as logging import six from designate.plugin import DriverPlugin from designate import objects from designate import rpc LOG = logging.getLogger(__name__) notify_opts = [ cfg.BoolOpt('notify_api_faults', default=False, help='Send notifications if there\'s a failure in the API.'), cfg.StrOpt('notification-plugin', default='default', help='The notification plugin to use'), ] CONF = cfg.CONF CONF.register_opts(notify_opts) NOTIFICATION_PLUGIN = None def send_api_fault(context, url, status, exception): """Send an api.fault notification.""" if not CONF.notify_api_faults: return payload = {'url': url, 'exception': str(exception), 'status': status} rpc.get_notifier('api').error(context, 'dns.api.fault', payload) def init_notification_plugin(): LOG.debug("Loading notification plugin: %s", cfg.CONF.notification_plugin) cls = NotificationPlugin.get_driver(cfg.CONF.notification_plugin) global NOTIFICATION_PLUGIN NOTIFICATION_PLUGIN = cls() def get_plugin(): if NOTIFICATION_PLUGIN is None: init_notification_plugin() return NOTIFICATION_PLUGIN class NotificationPlugin(DriverPlugin): """Base class for Notification Driver implementations""" __plugin_type__ = 'notification' __plugin_ns__ = 'designate.notification.plugin' def __init__(self): super(NotificationPlugin, self).__init__() @abc.abstractmethod def emit(self, notification_type, context, result, *args, **kwargs): """Return a payload to emit as part of the notification""" class Default(NotificationPlugin): """Returns the result, as implemented in the base class""" __plugin_name__ = 'default' def emit(self, notification_type, context, result, *args, **kwargs): """Return the result of the function called""" return [result] class Audit(NotificationPlugin): """Grabs Zone/Recordset names and RRData changes""" __plugin_name__ = 'audit' def zone_name(self, arglist, result): for arg in arglist + [result]: if isinstance(arg, objects.Zone): if arg.name is not None: return arg.name if hasattr(arg, 'zone_name'): if arg.zone_name is not None: return arg.zone_name return None def zone_id(self, arglist, result): for arg in arglist + [result]: if isinstance(arg, objects.Zone): if arg.id is not None: return arg.id if hasattr(arg, 'zone_id'): if arg.zone_id is not None: return arg.zone_id return None def recordset_name(self, arglist, result): for arg in arglist + [result]: if isinstance(arg, objects.RecordSet): if arg.name is not None: return arg.name return None def recordset_data(self, arglist, result): if not isinstance(result, objects.RecordSet): return [] for arg in arglist: if isinstance(arg, objects.RecordSet): if 'records' not in arg.obj_what_changed(): return [] original_rrs = arg.obj_get_original_value('records') old_value = ' '.join( [obj['data'] for obj in original_rrs]) new_value = ' '.join( [rr.data for rr in result.records]) if old_value == new_value: return [] return [{ 'change': 'records', 'old_value': old_value, 'new_value': new_value, }] return [] def other_data(self, arglist, result): changes = [] for arg in arglist: if isinstance(arg, objects.DesignateObject): for change in arg.obj_what_changed(): if change != 'records': old_value = arg.obj_get_original_value(change) new_value = getattr(arg, change) # Just in case something odd makes it here if any(not isinstance(val, (int, float, bool, six.string_types, type(None))) for val in (old_value, new_value)): LOG.warning("Nulling notification values after " "unexpected values (%s, %s)", old_value, new_value) old_value, new_value = None, None if old_value == new_value: continue changes.append({ 'change': change, 'old_value': str(old_value), 'new_value': str(new_value), }) return changes def blank_event(self): return [{ 'change': None, 'old_value': None, 'new_value': None, }] def gather_changes(self, arglist, result, notification_type): changes = [] if 'update' in notification_type: changes.extend(self.other_data(arglist, result)) if notification_type == 'dns.recordset.update': changes.extend(self.recordset_data(arglist, result)) elif 'create' in notification_type: if notification_type == 'dns.recordset.create': changes.extend(self.recordset_data(arglist, result)) else: changes.extend(self.blank_event()) else: changes.extend(self.blank_event()) return changes def emit(self, notification_type, context, result, *args, **kwargs): arglist = [] for item in args: if isinstance(item, tuple) or isinstance(item, list): arglist.extend(item) if isinstance(item, dict): arglist.extend(list(item.values())) payloads = [] for change in self.gather_changes(arglist, result, notification_type): payloads.append({ 'zone_name': self.zone_name(arglist, result), 'zone_id': self.zone_id(arglist, result), 'recordset_name': self.recordset_name(arglist, result), 'old_data': change['old_value'], 'new_data': change['new_value'], 'changed_field': change['change'], }) return payloads