def __init__(self): LOG.info("Ml2Plus initializing") registry._get_callback_manager()._notify_loop = ( patch_neutron._notify_loop) # First load drivers, then initialize DB, then initialize drivers self.type_manager = ml2_managers.TypeManager() self.extension_manager = managers.ExtensionManager() self.mechanism_manager = managers.MechanismManager() super(ml2_plugin.Ml2Plugin, self).__init__() self.type_manager.initialize() self.extension_manager.initialize() self.mechanism_manager.initialize() registry.subscribe(self._port_provisioned, resources.PORT, provisioning_blocks.PROVISIONING_COMPLETE) registry.subscribe(self._handle_segment_change, resources.SEGMENT, events.PRECOMMIT_CREATE) registry.subscribe(self._handle_segment_change, resources.SEGMENT, events.PRECOMMIT_DELETE) registry.subscribe(self._handle_segment_change, resources.SEGMENT, events.AFTER_CREATE) registry.subscribe(self._handle_segment_change, resources.SEGMENT, events.AFTER_DELETE) # REVISIT(kent): All the postcommit calls for SG and SG rules are not # currently implemented as they are not needed at this moment. registry.subscribe(self._handle_security_group_change, resources.SECURITY_GROUP, events.PRECOMMIT_CREATE) registry.subscribe(self._handle_security_group_change, resources.SECURITY_GROUP, events.PRECOMMIT_DELETE) registry.subscribe(self._handle_security_group_change, resources.SECURITY_GROUP, events.PRECOMMIT_UPDATE) # There is no update event to the security_group_rule registry.subscribe(self._handle_security_group_rule_change, resources.SECURITY_GROUP_RULE, events.PRECOMMIT_CREATE) registry.subscribe(self._handle_security_group_rule_change, resources.SECURITY_GROUP_RULE, events.PRECOMMIT_DELETE) try: registry.subscribe(self._subnet_delete_precommit_handler, resources.SUBNET, events.PRECOMMIT_DELETE) registry.subscribe(self._subnet_delete_after_delete_handler, resources.SUBNET, events.AFTER_DELETE) except AttributeError: LOG.info("Detected older version of Neutron, ML2Plus plugin " "is not subscribed to subnet_precommit_delete and " "subnet_after_delete events") self._setup_dhcp() self._start_rpc_notifiers() self.add_agent_status_check_worker(self.agent_health_check) self._verify_service_plugins_requirements() self.refresh_network_db_obj = cfg.CONF.ml2plus.refresh_network_db_obj self.refresh_port_db_obj = cfg.CONF.ml2plus.refresh_port_db_obj self.refresh_subnet_db_obj = cfg.CONF.ml2plus.refresh_subnet_db_obj self.refresh_subnetpool_db_obj = ( cfg.CONF.ml2plus.refresh_subnetpool_db_obj) self.refresh_address_scope_db_obj = ( cfg.CONF.ml2plus.refresh_address_scope_db_obj) LOG.info("Modular L2 Plugin (extended) initialization complete")
def _notify_loop(resource, event, trigger, **kwargs): """The notification loop.""" errors = [] callbacks = kwargs.pop('callbacks', None) if not callbacks: callbacks = list(registry._get_callback_manager()._callbacks[ resource].get(event, {}).items()) LOG.debug("Notify callbacks %s for %s, %s", callbacks, resource, event) for callback_id, callback in callbacks: try: callback(resource, event, trigger, **kwargs) except Exception as e: abortable_event = ( event.startswith(events.BEFORE) or event.startswith(events.PRECOMMIT) ) if not abortable_event: LOG.exception("Error during notification for " "%(callback)s %(resource)s, %(event)s", {'callback': callback_id, 'resource': resource, 'event': event}) else: LOG.error("Callback %(callback)s raised %(error)s", {'callback': callback_id, 'error': e}) errors.append(exceptions.NotificationError(callback_id, e)) return errors
def send_or_queue_notification(session, transaction_key, notifier_obj, notifier_method, args): rname = '' if notifier_method == NOVA_NOTIFIER_METHOD: # parse argument like "create_subnetpool" rname = args[0].split('_', 1)[1] event_name = 'after_' + args[0].split('_')[0] registry_method = getattr(notifier_obj, '_send_nova_notification') elif notifier_method == DHCP_NOTIFIER_METHOD: # parse argument like "subnetpool.create.end" rname = args[2].split('.')[0] event_name = 'after_' + args[2].split('.')[1] registry_method = getattr(notifier_obj, '_native_event_send_dhcp_notification') if rname: cbacks = registry._get_callback_manager()._callbacks.get(rname, None) if cbacks and event_name in cbacks.keys(): for entry in cbacks.values(): method = entry.values()[0] if registry_method == method: # This notification is already being sent by Neutron # soe we will avoid sending a duplicate notification return if not transaction_key or 'subnet.delete.end' in args or ( not QUEUE_OUT_OF_PROCESS_NOTIFICATIONS): # We make an exception for the dhcp agent notification # for port and subnet delete since the implementation # for sending that notification checks for the existence # of the associated network, which is not present in certain # cases if the delete notification is queued and sent after # the network delete. getattr(notifier_obj, notifier_method)(*args) return _queue_notification(session, transaction_key, notifier_obj, notifier_method, args)
event.startswith(events.BEFORE) or event.startswith(events.PRECOMMIT) ) if not abortable_event: LOG.exception("Error during notification for " "%(callback)s %(resource)s, %(event)s", {'callback': callback_id, 'resource': resource, 'event': event}) else: LOG.error("Callback %(callback)s raised %(error)s", {'callback': callback_id, 'error': e}) errors.append(exceptions.NotificationError(callback_id, e)) return errors original_notify_loop = registry._get_callback_manager()._notify_loop from inspect import isclass from inspect import isfunction from inspect import ismethod # The undecorated() and looks_like_a_decorator() functions have been # borrowed from the undecorated python library since RPM or Debian # packages are not readily available. def looks_like_a_decorator(a): return ( isfunction(a) or ismethod(a) or isclass(a) )
def test_neutron_registry_callback(mock_agent_rpc): """Test callback functions used for registering for RPC events in driver. Creates two drivers to simulate a differentiated environment deployment, then tests that each driver registers different functions to receive notification from Neutron after subscribing for events, and that the functions are called by the Neutron callback manager. Also, see related unit test, f5lbaasdriver/v2/bigip/test/test_bind_registry_callback.py :param mock_agent_rpc: Not used. Mock needed to create F5DriverV2. """ plugin = mock.MagicMock() # start with empty Neutron RPC callback registry registry.clear() # create default driver default_driver = F5DriverV2(plugin) default_driver.plugin_rpc = mock.MagicMock() # create a differentiated environment driver dmz_driver = F5DriverV2(plugin, 'dmz') dmz_driver.plugin_rpc = mock.MagicMock() default_callback_func = default_driver._bindRegistryCallback() dmz_callback_func = dmz_driver._bindRegistryCallback() # two different callback functions created assert default_callback_func != dmz_callback_func # registry holds two callbacks callback_mgr = registry._get_callback_manager() assert len( callback_mgr._callbacks[resources.PROCESS][events.AFTER_CREATE]) == 2 # both callbacks are in registry callbacks = callback_mgr._callbacks[resources.PROCESS][events.AFTER_CREATE] callback_iter = iter(callbacks) callback_name = next(callback_iter).split('.')[-1] assert callback_name == default_callback_func.__name__ callback_name = next(callback_iter).split('.')[-1] assert callback_name == dmz_callback_func.__name__ # callbacks can be called back with mock.patch('f5lbaasdriver.v2.bigip.driver_v2.LOG') as mock_log: # invoke callbacks from callback manager callback_mgr.notify(resources.PROCESS, events.AFTER_CREATE, mock.MagicMock()) # create_rpc_listener called assert default_driver.plugin_rpc.create_rpc_listener.called assert dmz_driver.plugin_rpc.create_rpc_listener.called # debug messages logged log_iter = iter(mock_log.debug.call_args_list) args, kwargs = log_iter.next() assert str(args[0]).startswith("F5DriverV2 with env None received") args, kwargs = log_iter.next() assert str(args[0]).startswith("F5DriverV2 with env dmz received")
def test_neutron_registry_callback(mock_agent_rpc): """Test callback functions used for registering for RPC events in driver. Creates two drivers to simulate a differentiated environment deployment, then tests that each driver registers different functions to receive notification from Neutron after subscribing for events, and that the functions are called by the Neutron callback manager. Also, see related unit test, f5lbaasdriver/v2/bigip/test/test_bind_registry_callback.py :param mock_agent_rpc: Not used. Mock needed to create F5DriverV2. """ plugin = mock.MagicMock() # start with empty Neutron RPC callback registry registry.clear() # create default driver default_driver = F5DriverV2(plugin) default_driver.plugin_rpc = mock.MagicMock() # create a differentiated environment driver dmz_driver = F5DriverV2(plugin, 'dmz') dmz_driver.plugin_rpc = mock.MagicMock() default_callback_func = default_driver._bindRegistryCallback() dmz_callback_func = dmz_driver._bindRegistryCallback() # two different callback functions created assert default_callback_func != dmz_callback_func # registry holds two callbacks callback_mgr = registry._get_callback_manager() assert len( callback_mgr._callbacks[resources.PROCESS][events.AFTER_CREATE]) == 2 # both callbacks are in registry callbacks = callback_mgr._callbacks[resources.PROCESS][events.AFTER_CREATE] callback_iter = iter(callbacks) callback_name = next(callback_iter).split('.')[-1] assert callback_name == default_callback_func.__name__ callback_name = next(callback_iter).split('.')[-1] assert callback_name == dmz_callback_func.__name__ # callbacks can be called back with mock.patch('f5lbaasdriver.v2.bigip.driver_v2.LOG') as mock_log: # invoke callbacks from callback manager callback_mgr.notify( resources.PROCESS, events.AFTER_CREATE, mock.MagicMock()) # create_rpc_listener called assert default_driver.plugin_rpc.create_rpc_listener.called assert dmz_driver.plugin_rpc.create_rpc_listener.called # debug messages logged log_iter = iter(mock_log.debug.call_args_list) args, kwargs = log_iter.next() assert str(args[0]).startswith("F5DriverV2 with env None received") args, kwargs = log_iter.next() assert str(args[0]).startswith("F5DriverV2 with env dmz received")
def _test_resource_with_errors(self, res_name, op, **kwargs): # Must call the internal notify_loop in order to get the errors return registry._get_callback_manager()._notify_loop( res_name, op, 'nsxadmin', **kwargs)
def _registry_notify(resource, event, trigger, **kwargs): # This invokes neutron's original (unpatched) registry notification # method. registry._get_callback_manager().notify( resource, event, trigger, **kwargs)
def _get_callbacks_for_resource_event(resource, event): return list(registry._get_callback_manager()._callbacks[ resource].get(event, {}).items())