159 lines
6.8 KiB
Python
159 lines
6.8 KiB
Python
# Copyright (c) 2015 Mirantis, Inc.
|
|
# 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 neutron_lib import constants
|
|
from oslo_config import cfg
|
|
from oslo_log import log
|
|
from oslo_serialization import jsonutils
|
|
from pecan import hooks
|
|
|
|
from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
|
|
from neutron.common import rpc as n_rpc
|
|
from neutron import manager
|
|
from neutron.pecan_wsgi import constants as pecan_constants
|
|
from neutron.pecan_wsgi.hooks import utils
|
|
|
|
LOG = log.getLogger(__name__)
|
|
|
|
|
|
class NotifierHook(hooks.PecanHook):
|
|
priority = 135
|
|
|
|
@property
|
|
def _notifier(self):
|
|
if not hasattr(self, '_notifier_inst'):
|
|
self._notifier_inst = n_rpc.get_notifier('network')
|
|
return self._notifier_inst
|
|
|
|
def _nova_notify(self, action, resource, *args):
|
|
action_resource = '%s_%s' % (action, resource)
|
|
if not hasattr(self, '_nova_notifier'):
|
|
# this is scoped to avoid a dependency on nova client when nova
|
|
# notifications aren't enabled
|
|
from neutron.notifiers import nova
|
|
self._nova_notifier = nova.Notifier()
|
|
self._nova_notifier.send_network_change(action_resource, *args)
|
|
|
|
def _notify_dhcp_agent(self, context, resource_name, action, resources):
|
|
plugin = manager.NeutronManager.get_plugin_for_resource(resource_name)
|
|
notifier_method = '%s.%s.end' % (resource_name, action)
|
|
# use plugin's dhcp notifier, if this is already instantiated
|
|
agent_notifiers = getattr(plugin, 'agent_notifiers', {})
|
|
dhcp_agent_notifier = (
|
|
agent_notifiers.get(constants.AGENT_TYPE_DHCP) or
|
|
dhcp_rpc_agent_api.DhcpAgentNotifyAPI()
|
|
)
|
|
# The DHCP Agent does not accept bulk notifications
|
|
for resource in resources:
|
|
item = {resource_name: resource}
|
|
LOG.debug("Sending DHCP agent notification for: %s", item)
|
|
dhcp_agent_notifier.notify(context, item, notifier_method)
|
|
|
|
def before(self, state):
|
|
if state.request.method not in ('POST', 'PUT', 'DELETE'):
|
|
return
|
|
resource = state.request.context.get('resource')
|
|
if not resource:
|
|
return
|
|
if utils.is_member_action(utils.get_controller(state)):
|
|
return
|
|
action = pecan_constants.ACTION_MAP.get(state.request.method)
|
|
event = '%s.%s.start' % (resource, action)
|
|
if action in ('create', 'update'):
|
|
# notifier just gets plain old body without any treatment other
|
|
# than the population of the object ID being operated on
|
|
payload = state.request.json.copy()
|
|
if action == 'update':
|
|
payload['id'] = state.request.context.get('resource_id')
|
|
elif action == 'delete':
|
|
resource_id = state.request.context.get('resource_id')
|
|
payload = {resource + '_id': resource_id}
|
|
self._notifier.info(state.request.context.get('neutron_context'),
|
|
event, payload)
|
|
|
|
def after(self, state):
|
|
resource_name = state.request.context.get('resource')
|
|
collection_name = state.request.context.get('collection')
|
|
neutron_context = state.request.context.get('neutron_context')
|
|
if not resource_name:
|
|
LOG.debug("Skipping NotifierHook processing as there was no "
|
|
"resource associated with the request")
|
|
return
|
|
action = pecan_constants.ACTION_MAP.get(state.request.method)
|
|
if not action or action == 'get':
|
|
LOG.debug("No notification will be sent for action: %s", action)
|
|
return
|
|
if utils.is_member_action(utils.get_controller(state)):
|
|
return
|
|
if state.response.status_int > 300:
|
|
LOG.debug("No notification will be sent due to unsuccessful "
|
|
"status code: %s", state.response.status_int)
|
|
return
|
|
|
|
if action == 'delete':
|
|
# The object has been deleted, so we must notify the agent with the
|
|
# data of the original object
|
|
data = {collection_name:
|
|
state.request.context.get('original_resources', [])}
|
|
else:
|
|
try:
|
|
data = jsonutils.loads(state.response.body)
|
|
except ValueError:
|
|
if not state.response.body:
|
|
data = {}
|
|
resources = []
|
|
if data:
|
|
if resource_name in data:
|
|
resources = [data[resource_name]]
|
|
elif collection_name in data:
|
|
# This was a bulk request
|
|
resources = data[collection_name]
|
|
# Send a notification only if a resource can be identified in the
|
|
# response. This means that for operations such as add_router_interface
|
|
# no notification will be sent
|
|
if cfg.CONF.dhcp_agent_notification and data:
|
|
self._notify_dhcp_agent(
|
|
neutron_context, resource_name,
|
|
action, resources)
|
|
if cfg.CONF.notify_nova_on_port_data_changes:
|
|
orig = {}
|
|
if action == 'update':
|
|
orig = state.request.context.get('original_resources')[0]
|
|
elif action == 'delete':
|
|
# NOTE(kevinbenton): the nova notifier is a bit strange because
|
|
# it expects the original to be in the last argument on a
|
|
# delete rather than in the 'original_obj' position
|
|
resources = (
|
|
state.request.context.get('original_resources') or [])
|
|
for resource in resources:
|
|
self._nova_notify(action, resource_name, orig,
|
|
{resource_name: resource})
|
|
|
|
event = '%s.%s.end' % (resource_name, action)
|
|
if action == 'delete':
|
|
resource_id = state.request.context.get('resource_id')
|
|
payload = {resource_name + '_id': resource_id}
|
|
elif action in ('create', 'update'):
|
|
if not resources:
|
|
# create/update did not complete so no notification
|
|
return
|
|
if len(resources) > 1:
|
|
payload = {collection_name: resources}
|
|
else:
|
|
payload = {resource_name: resources[0]}
|
|
else:
|
|
return
|
|
self._notifier.info(neutron_context, event, payload)
|