From c17c78df7c96e1ccb08c99a274d3e086804e3833 Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Sun, 31 Jul 2016 03:10:35 -0700 Subject: [PATCH] Port device events for common agent Some of the agent code may want to receive events whenever a device is added/updated/deleted. Currently the only way to do that is to modify the loop directly or create an extension. Neither of these options are good if it's code we don't want users to disable (the extensions) and it's specific to one agent (so we don't want to modify the loop). Partially-Implements: blueprint vlan-aware-vms Change-Id: I3468c7f46cc1b4000cdd537e8f216d207a14727a --- neutron/callbacks/resources.py | 1 + .../ml2/drivers/agent/_common_agent.py | 10 ++++++ .../ml2/drivers/agent/test__common_agent.py | 33 +++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/neutron/callbacks/resources.py b/neutron/callbacks/resources.py index 028cc715376..719675ec6bb 100644 --- a/neutron/callbacks/resources.py +++ b/neutron/callbacks/resources.py @@ -18,6 +18,7 @@ NETWORK = 'network' NETWORKS = 'networks' PORT = 'port' PORTS = 'ports' +PORT_DEVICE = 'port_device' PROCESS = 'process' ROUTER = 'router' ROUTER_GATEWAY = 'router_gateway' diff --git a/neutron/plugins/ml2/drivers/agent/_common_agent.py b/neutron/plugins/ml2/drivers/agent/_common_agent.py index ead63bc6f97..d68c4f957c2 100644 --- a/neutron/plugins/ml2/drivers/agent/_common_agent.py +++ b/neutron/plugins/ml2/drivers/agent/_common_agent.py @@ -32,6 +32,9 @@ from neutron.agent.l2 import l2_agent_extensions_manager as ext_manager from neutron.agent import rpc as agent_rpc from neutron.agent import securitygroups_rpc as sg_rpc from neutron.api.rpc.callbacks import resources +from neutron.callbacks import events +from neutron.callbacks import registry +from neutron.callbacks import resources as local_resources from neutron.common import config as common_config from neutron.common import constants as n_const from neutron.common import topics @@ -303,6 +306,10 @@ class CommonAgentLoop(service.Service): device_details['port_id'], device_details['device']) self.ext_manager.handle_port(self.context, device_details) + registry.notify(local_resources.PORT_DEVICE, + events.AFTER_UPDATE, self, + context=self.context, + device_details=device_details) else: LOG.info(_LI("Device %s not defined on plugin"), device) @@ -339,6 +346,9 @@ class CommonAgentLoop(service.Service): self.ext_manager.delete_port(self.context, {'device': device, 'port_id': port_id}) + registry.notify(local_resources.PORT_DEVICE, events.AFTER_DELETE, + self, context=self.context, device=device, + port_id=port_id) if self.prevent_arp_spoofing: self.mgr.delete_arp_spoofing_protection(devices) return resync diff --git a/neutron/tests/unit/plugins/ml2/drivers/agent/test__common_agent.py b/neutron/tests/unit/plugins/ml2/drivers/agent/test__common_agent.py index f55d81f16ee..8db229098d5 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/agent/test__common_agent.py +++ b/neutron/tests/unit/plugins/ml2/drivers/agent/test__common_agent.py @@ -20,6 +20,9 @@ from oslo_config import cfg import testtools from neutron.agent.linux import bridge_lib +from neutron.callbacks import events +from neutron.callbacks import registry +from neutron.callbacks import resources from neutron.common import constants as n_const from neutron.plugins.ml2.drivers.agent import _agent_manager_base as amb from neutron.plugins.ml2.drivers.agent import _common_agent as ca @@ -68,6 +71,36 @@ class TestCommonAgentLoop(base.BaseTestCase): with mock.patch.object(self.agent, "daemon_loop"): self.agent.start() + def test_treat_devices_removed_notify(self): + handler = mock.Mock() + registry.subscribe(handler, resources.PORT_DEVICE, events.AFTER_DELETE) + devices = [DEVICE_1] + self.agent.treat_devices_removed(devices) + handler.assert_called_once_with(mock.ANY, mock.ANY, self.agent, + context=mock.ANY, device=DEVICE_1, + port_id=mock.ANY) + + def test_treat_devices_added_updated_notify(self): + handler = mock.Mock() + registry.subscribe(handler, resources.PORT_DEVICE, events.AFTER_UPDATE) + agent = self.agent + mock_details = {'device': 'dev123', + 'port_id': 'port123', + 'network_id': 'net123', + 'admin_state_up': True, + 'network_type': 'vlan', + 'segmentation_id': 100, + 'physical_network': 'physnet1', + 'device_owner': 'horse'} + agent.plugin_rpc = mock.Mock() + agent.plugin_rpc.get_devices_details_list.return_value = [mock_details] + agent.mgr = mock.Mock() + agent.mgr.plug_interface.return_value = True + agent.treat_devices_added_updated(set(['dev123'])) + handler.assert_called_once_with(mock.ANY, mock.ANY, self.agent, + context=mock.ANY, + device_details=mock_details) + def test_treat_devices_removed_with_existed_device(self): agent = self.agent agent.mgr.ensure_port_admin_state = mock.Mock()