From 7c8d2ad3a9c9ed3e35d852cd76ca0869972a1eed Mon Sep 17 00:00:00 2001 From: gongysh Date: Fri, 1 Mar 2013 16:23:32 +0800 Subject: [PATCH] Move network schedule to first port creation. blueprint quantum-scheduler we remove scheduling network to dhcp agent when creating network so that we can give admin the chance to allocate the given network to a given dhcp agent after network creation. Change-Id: I228870669825405277de0505a3f2ece074918524 --- .../rpc/agentnotifiers/dhcp_rpc_agent_api.py | 13 ++ quantum/db/agentschedulers_db.py | 9 +- .../nicira/nicira_nvp_plugin/QuantumPlugin.py | 4 +- .../plugins/openvswitch/ovs_quantum_plugin.py | 3 - quantum/scheduler/dhcp_agent_scheduler.py | 10 +- quantum/scheduler/l3_agent_scheduler.py | 8 +- .../unit/openvswitch/test_agent_scheduler.py | 181 ++++++++++++++---- 7 files changed, 169 insertions(+), 59 deletions(-) diff --git a/quantum/api/rpc/agentnotifiers/dhcp_rpc_agent_api.py b/quantum/api/rpc/agentnotifiers/dhcp_rpc_agent_api.py index 2a01ad18e8..c89e6199a4 100644 --- a/quantum/api/rpc/agentnotifiers/dhcp_rpc_agent_api.py +++ b/quantum/api/rpc/agentnotifiers/dhcp_rpc_agent_api.py @@ -62,6 +62,19 @@ class DhcpAgentNotifyAPI(proxy.RpcProxy): plugin = manager.QuantumManager.get_plugin() if (method != 'network_delete_end' and utils.is_extension_supported( plugin, constants.AGENT_SCHEDULER_EXT_ALIAS)): + if method == 'port_create_end': + # we don't schedule when we create network + # because we want to give admin a chance to + # schedule network manually by API + adminContext = (context if context.is_admin else + context.elevated()) + network = plugin.get_network(adminContext, network_id) + chosen_agent = plugin.schedule_network(adminContext, network) + if chosen_agent: + self._notification_host( + context, 'network_create_end', + {'network': {'id': network_id}}, + chosen_agent['host']) for (host, topic) in self._get_dhcp_agents(context, network_id): self.cast( context, self.make_msg(method, diff --git a/quantum/db/agentschedulers_db.py b/quantum/db/agentschedulers_db.py index 5137820f85..bf04be20e2 100644 --- a/quantum/db/agentschedulers_db.py +++ b/quantum/db/agentschedulers_db.py @@ -292,12 +292,13 @@ class AgentSchedulerDbMixin(agentscheduler.AgentSchedulerPluginBase): else: return {'agents': []} - def schedule_network(self, context, request_network, created_network): + def schedule_network(self, context, created_network): if self.network_scheduler: - result = self.network_scheduler.schedule( - self, context, request_network, created_network) - if not result: + chosen_agent = self.network_scheduler.schedule( + self, context, created_network) + if not chosen_agent: LOG.warn(_('Fail scheduling network %s'), created_network) + return chosen_agent def auto_schedule_networks(self, context, host): if self.network_scheduler: diff --git a/quantum/plugins/nicira/nicira_nvp_plugin/QuantumPlugin.py b/quantum/plugins/nicira/nicira_nvp_plugin/QuantumPlugin.py index 8fb8aee193..9ca9d4b72c 100644 --- a/quantum/plugins/nicira/nicira_nvp_plugin/QuantumPlugin.py +++ b/quantum/plugins/nicira/nicira_nvp_plugin/QuantumPlugin.py @@ -956,7 +956,7 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2, net_binding) self._extend_network_port_security_dict(context, new_net) self._extend_network_dict_l3(context, new_net) - self.schedule_network(context, network['network'], new_net) + self.schedule_network(context, new_net) return new_net def delete_network(self, context, id): @@ -1351,7 +1351,7 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2, self._extend_port_dict_security_group(context, port_data) self._extend_port_qos_queue(context, port_data) net = self.get_network(context, port_data['network_id']) - self.schedule_network(context, None, net) + self.schedule_network(context, net) return port_data def update_port(self, context, id, port): diff --git a/quantum/plugins/openvswitch/ovs_quantum_plugin.py b/quantum/plugins/openvswitch/ovs_quantum_plugin.py index 9c2b72cda2..54acdb8ca3 100644 --- a/quantum/plugins/openvswitch/ovs_quantum_plugin.py +++ b/quantum/plugins/openvswitch/ovs_quantum_plugin.py @@ -497,7 +497,6 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2, self._extend_network_dict_l3(context, net) # note - exception will rollback entire transaction LOG.debug(_("Created network: %s"), net['id']) - self.schedule_network(context, network['network'], net) return net def update_network(self, context, id, network): @@ -578,8 +577,6 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2, else: self.notifier.security_groups_member_updated( context, port.get(ext_sg.SECURITYGROUPS)) - net = self.get_network(context, port['network_id']) - self.schedule_network(context, None, net) return self._extend_port_dict_binding(context, port) def get_port(self, context, id, fields=None): diff --git a/quantum/scheduler/dhcp_agent_scheduler.py b/quantum/scheduler/dhcp_agent_scheduler.py index 62929889cb..e8ae31950d 100644 --- a/quantum/scheduler/dhcp_agent_scheduler.py +++ b/quantum/scheduler/dhcp_agent_scheduler.py @@ -35,7 +35,7 @@ class ChanceScheduler(object): More sophisticated scheduler (similar to filter scheduler in nova?) can be introduced later.""" - def schedule(self, plugin, context, request_network, network): + def schedule(self, plugin, context, network): """Schedule the network to an active DHCP agent if there is no active DHCP agent hosting it. """ @@ -47,21 +47,21 @@ class ChanceScheduler(object): if dhcp_agents: LOG.debug(_('Network %s is hosted already'), network['id']) - return False + return enabled_dhcp_agents = plugin.get_agents_db( context, filters={ 'agent_type': [constants.AGENT_TYPE_DHCP], 'admin_state_up': [True]}) if not enabled_dhcp_agents: LOG.warn(_('No enabled DHCP agents')) - return False + return active_dhcp_agents = [enabled_dhcp_agent for enabled_dhcp_agent in enabled_dhcp_agents if not agents_db.AgentDbMixin.is_agent_down( enabled_dhcp_agent['heartbeat_timestamp'])] if not active_dhcp_agents: LOG.warn(_('No active DHCP agents')) - return False + return chosen_agent = random.choice(active_dhcp_agents) binding = agentschedulers_db.NetworkDhcpAgentBinding() binding.dhcp_agent = chosen_agent @@ -71,7 +71,7 @@ class ChanceScheduler(object): 'DHCP agent %(agent_id)s'), {'network_id': network['id'], 'agent_id': chosen_agent['id']}) - return True + return chosen_agent def auto_schedule_networks(self, plugin, context, host): """Schedule non-hosted networks to the DHCP agent on diff --git a/quantum/scheduler/l3_agent_scheduler.py b/quantum/scheduler/l3_agent_scheduler.py index 0d3b1efbb5..fda363f325 100644 --- a/quantum/scheduler/l3_agent_scheduler.py +++ b/quantum/scheduler/l3_agent_scheduler.py @@ -124,18 +124,18 @@ class ChanceScheduler(object): ' by L3 agent %(agent_id)s'), {'router_id': sync_router['id'], 'agent_id': l3_agents[0]['id']}) - return False + return active_l3_agents = plugin.get_l3_agents(context, active=True) if not active_l3_agents: LOG.warn(_('No active L3 agents')) - return False + return candidates = plugin.get_l3_agent_candidates(sync_router, active_l3_agents) if not candidates: LOG.warn(_('No L3 agents can host the router %s'), sync_router['id']) - return False + return chosen_agent = random.choice(candidates) binding = agentschedulers_db.RouterL3AgentBinding() @@ -146,4 +146,4 @@ class ChanceScheduler(object): 'L3 agent %(agent_id)s'), {'router_id': sync_router['id'], 'agent_id': chosen_agent['id']}) - return True + return chosen_agent diff --git a/quantum/tests/unit/openvswitch/test_agent_scheduler.py b/quantum/tests/unit/openvswitch/test_agent_scheduler.py index 38c74ca9bd..4c80ca15b4 100644 --- a/quantum/tests/unit/openvswitch/test_agent_scheduler.py +++ b/quantum/tests/unit/openvswitch/test_agent_scheduler.py @@ -20,6 +20,7 @@ import mock from webob import exc from quantum.api import extensions +from quantum.api.rpc.agentnotifiers import dhcp_rpc_agent_api from quantum.common import constants from quantum import context from quantum.db import agents_db @@ -28,7 +29,6 @@ from quantum.db import l3_rpc_base from quantum.extensions import agentscheduler from quantum import manager from quantum.openstack.common import uuidutils -from quantum.plugins.openvswitch.ovs_quantum_plugin import OVSQuantumPluginV2 from quantum.tests.unit import test_agent_ext_plugin from quantum.tests.unit.testlib_api import create_request from quantum.tests.unit import test_db_plugin as test_plugin @@ -183,27 +183,20 @@ class AgentSchedulerTestMixIn(object): return agent['id'] -class AgentSchedulerTestCase(test_l3_plugin.L3NatTestCaseMixin, - test_agent_ext_plugin.AgentDBTestMixIn, - AgentSchedulerTestMixIn, - test_plugin.QuantumDbPluginV2TestCase): +class OvsAgentSchedulerTestCase(test_l3_plugin.L3NatTestCaseMixin, + test_agent_ext_plugin.AgentDBTestMixIn, + AgentSchedulerTestMixIn, + test_plugin.QuantumDbPluginV2TestCase): fmt = 'json' + plugin_str = ('quantum.plugins.openvswitch.' + 'ovs_quantum_plugin.OVSQuantumPluginV2') def setUp(self): - plugin = ('quantum.plugins.openvswitch.' - 'ovs_quantum_plugin.OVSQuantumPluginV2') - self.dhcp_notifier_cls_p = mock.patch( - 'quantum.api.rpc.agentnotifiers.dhcp_rpc_agent_api.' - 'DhcpAgentNotifyAPI') - self.dhcp_notifier = mock.Mock(name='dhcp_notifier') - self.dhcp_notifier_cls = self.dhcp_notifier_cls_p.start() - self.dhcp_notifier_cls.return_value = self.dhcp_notifier - super(AgentSchedulerTestCase, self).setUp(plugin) + super(OvsAgentSchedulerTestCase, self).setUp(self.plugin_str) ext_mgr = extensions.PluginAwareExtensionManager.get_instance() self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr) self.adminContext = context.get_admin_context() self.agentscheduler_dbMinxin = manager.QuantumManager.get_plugin() - self.addCleanup(self.dhcp_notifier_cls_p.stop) def test_report_states(self): self._register_agent_states() @@ -215,7 +208,7 @@ class AgentSchedulerTestCase(test_l3_plugin.L3NatTestCaseMixin, with self.network() as net: dhcp_agents = self._list_dhcp_agents_hosting_network( net['network']['id']) - self.assertEqual(1, len(dhcp_agents['agents'])) + self.assertEqual(0, len(dhcp_agents['agents'])) def test_network_auto_schedule_with_disabled(self): with contextlib.nested(self.network(), @@ -328,15 +321,15 @@ class AgentSchedulerTestCase(test_l3_plugin.L3NatTestCaseMixin, }, 'agent_type': constants.AGENT_TYPE_DHCP} self._register_one_agent_state(dhcp_hosta) - with self.network() as net1: + with self.port() as port1: dhcp_agents = self._list_dhcp_agents_hosting_network( - net1['network']['id']) + port1['port']['network_id']) self.assertEqual(1, len(dhcp_agents['agents'])) agents = self._list_agents() self._disable_agent(agents['agents'][0]['id']) - with self.network() as net2: + with self.port() as port2: dhcp_agents = self._list_dhcp_agents_hosting_network( - net2['network']['id']) + port2['port']['network_id']) self.assertEqual(0, len(dhcp_agents['agents'])) def test_network_scheduler_with_down_agent(self): @@ -352,18 +345,19 @@ class AgentSchedulerTestCase(test_l3_plugin.L3NatTestCaseMixin, is_agent_down_str = 'quantum.db.agents_db.AgentDbMixin.is_agent_down' with mock.patch(is_agent_down_str) as mock_is_agent_down: mock_is_agent_down.return_value = False - with self.network() as net: + with self.port() as port: dhcp_agents = self._list_dhcp_agents_hosting_network( - net['network']['id']) + port['port']['network_id']) self.assertEqual(1, len(dhcp_agents['agents'])) with mock.patch(is_agent_down_str) as mock_is_agent_down: mock_is_agent_down.return_value = True - with self.network() as net: + with self.port() as port: dhcp_agents = self._list_dhcp_agents_hosting_network( - net['network']['id']) + port['port']['network_id']) self.assertEqual(0, len(dhcp_agents['agents'])) def test_network_scheduler_with_hosted_network(self): + plugin = manager.QuantumManager.get_plugin() dhcp_hosta = { 'binary': 'quantum-dhcp-agent', 'host': DHCP_HOSTA, @@ -373,20 +367,26 @@ class AgentSchedulerTestCase(test_l3_plugin.L3NatTestCaseMixin, }, 'agent_type': constants.AGENT_TYPE_DHCP} self._register_one_agent_state(dhcp_hosta) - agents = self._list_agents() - with self.network() as net1: + with self.port() as port1: dhcp_agents = self._list_dhcp_agents_hosting_network( - net1['network']['id']) + port1['port']['network_id']) self.assertEqual(1, len(dhcp_agents['agents'])) - with mock.patch.object(OVSQuantumPluginV2, + with mock.patch.object(plugin, 'get_dhcp_agents_hosting_networks', autospec=True) as mock_hosting_agents: - mock_hosting_agents.return_value = agents['agents'] - with self.network(do_delete=False) as net2: + mock_hosting_agents.return_value = plugin.get_agents_db( + self.adminContext) + with self.network('test', do_delete=False) as net1: + pass + with self.subnet(network=net1, + cidr='10.0.1.0/24', + do_delete=False) as subnet1: + pass + with self.port(subnet=subnet1, no_delete=True) as port2: pass dhcp_agents = self._list_dhcp_agents_hosting_network( - net2['network']['id']) + port2['port']['network_id']) self.assertEqual(0, len(dhcp_agents['agents'])) def test_network_policy(self): @@ -440,12 +440,12 @@ class AgentSchedulerTestCase(test_l3_plugin.L3NatTestCaseMixin, self._register_one_agent_state(dhcp_hosta) hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP, DHCP_HOSTA) - with self.network() as net1: + with self.port() as port1: num_before_remove = len( self._list_networks_hosted_by_dhcp_agent( hosta_id)['networks']) self._remove_network_from_dhcp_agent(hosta_id, - net1['network']['id']) + port1['port']['network_id']) num_after_remove = len( self._list_networks_hosted_by_dhcp_agent( hosta_id)['networks']) @@ -731,20 +731,119 @@ class AgentSchedulerTestCase(test_l3_plugin.L3NatTestCaseMixin, admin_context=False) -class L3AgentNotifierTestCase(test_l3_plugin.L3NatTestCaseMixin, - test_agent_ext_plugin.AgentDBTestMixIn, - AgentSchedulerTestMixIn, - test_plugin.QuantumDbPluginV2TestCase): - def setUp(self): - plugin = ('quantum.plugins.openvswitch.' +class OvsDhcpAgentNotifierTestCase(test_l3_plugin.L3NatTestCaseMixin, + test_agent_ext_plugin.AgentDBTestMixIn, + AgentSchedulerTestMixIn, + test_plugin.QuantumDbPluginV2TestCase): + plugin_str = ('quantum.plugins.openvswitch.' 'ovs_quantum_plugin.OVSQuantumPluginV2') + + def setUp(self): + self.dhcp_notifier = dhcp_rpc_agent_api.DhcpAgentNotifyAPI() + self.dhcp_notifier_cls_p = mock.patch( + 'quantum.api.rpc.agentnotifiers.dhcp_rpc_agent_api.' + 'DhcpAgentNotifyAPI') + self.dhcp_notifier_cls = self.dhcp_notifier_cls_p.start() + self.dhcp_notifier_cls.return_value = self.dhcp_notifier + super(OvsDhcpAgentNotifierTestCase, self).setUp(self.plugin_str) + ext_mgr = extensions.PluginAwareExtensionManager.get_instance() + self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr) + self.adminContext = context.get_admin_context() + self.addCleanup(self.dhcp_notifier_cls_p.stop) + + def test_network_add_to_dhcp_agent_notification(self): + with mock.patch.object(self.dhcp_notifier, 'cast') as mock_dhcp: + with self.network() as net1: + network_id = net1['network']['id'] + self._register_agent_states() + hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP, + DHCP_HOSTA) + self._add_network_to_dhcp_agent(hosta_id, + network_id) + mock_dhcp.assert_called_with( + mock.ANY, + self.dhcp_notifier.make_msg( + 'network_create_end', + payload={'network': {'id': network_id}}), + topic='dhcp_agent.' + DHCP_HOSTA) + + def test_network_remove_from_dhcp_agent_notification(self): + with self.network(do_delete=False) as net1: + network_id = net1['network']['id'] + self._register_agent_states() + hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP, + DHCP_HOSTA) + self._add_network_to_dhcp_agent(hosta_id, + network_id) + with mock.patch.object(self.dhcp_notifier, 'cast') as mock_dhcp: + self._remove_network_from_dhcp_agent(hosta_id, + network_id) + mock_dhcp.assert_called_with( + mock.ANY, + self.dhcp_notifier.make_msg( + 'network_delete_end', + payload={'network_id': network_id}), + topic='dhcp_agent.' + DHCP_HOSTA) + + def test_agent_updated_dhcp_agent_notification(self): + with mock.patch.object(self.dhcp_notifier, 'cast') as mock_dhcp: + self._register_agent_states() + hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP, + DHCP_HOSTA) + self._disable_agent(hosta_id, admin_state_up=False) + mock_dhcp.assert_called_with( + mock.ANY, self.dhcp_notifier.make_msg( + 'agent_updated', + payload={'admin_state_up': False}), + topic='dhcp_agent.' + DHCP_HOSTA) + + def test_network_port_create_notification(self): + dhcp_hosta = { + 'binary': 'quantum-dhcp-agent', + 'host': DHCP_HOSTA, + 'topic': 'dhcp_agent', + 'configurations': {'dhcp_driver': 'dhcp_driver', + 'use_namespaces': True, + }, + 'agent_type': constants.AGENT_TYPE_DHCP} + self._register_one_agent_state(dhcp_hosta) + with mock.patch.object(self.dhcp_notifier, 'cast') as mock_dhcp: + with self.network(do_delete=False) as net1: + with self.subnet(network=net1, + do_delete=False) as subnet1: + with self.port(subnet=subnet1, no_delete=True) as port: + network_id = port['port']['network_id'] + expected_calls = [ + mock.call( + mock.ANY, + self.dhcp_notifier.make_msg( + 'network_create_end', + payload={'network': {'id': network_id}}), + topic='dhcp_agent.' + DHCP_HOSTA), + mock.call( + mock.ANY, + self.dhcp_notifier.make_msg( + 'port_create_end', + payload={'port': port['port']}), + topic='dhcp_agent.' + DHCP_HOSTA)] + self.assertEqual(mock_dhcp.call_args_list, expected_calls) + + +class OvsL3AgentNotifierTestCase(test_l3_plugin.L3NatTestCaseMixin, + test_agent_ext_plugin.AgentDBTestMixIn, + AgentSchedulerTestMixIn, + test_plugin.QuantumDbPluginV2TestCase): + plugin_str = ('quantum.plugins.openvswitch.' + 'ovs_quantum_plugin.OVSQuantumPluginV2') + + def setUp(self): self.dhcp_notifier_cls_p = mock.patch( 'quantum.api.rpc.agentnotifiers.dhcp_rpc_agent_api.' 'DhcpAgentNotifyAPI') self.dhcp_notifier = mock.Mock(name='dhcp_notifier') self.dhcp_notifier_cls = self.dhcp_notifier_cls_p.start() self.dhcp_notifier_cls.return_value = self.dhcp_notifier - super(L3AgentNotifierTestCase, self).setUp(plugin) + super(OvsL3AgentNotifierTestCase, self).setUp(self.plugin_str) ext_mgr = extensions.PluginAwareExtensionManager.get_instance() self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr) self.adminContext = context.get_admin_context() @@ -799,5 +898,5 @@ class L3AgentNotifierTestCase(test_l3_plugin.L3NatTestCaseMixin, topic='l3_agent.hosta') -class AgentSchedulerTestCaseXML(AgentSchedulerTestCase): +class OvsAgentSchedulerTestCaseXML(OvsAgentSchedulerTestCase): fmt = 'xml'