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
This commit is contained in:
gongysh 2013-03-01 16:23:32 +08:00
parent 9db3ea0b07
commit 7c8d2ad3a9
7 changed files with 169 additions and 59 deletions

View File

@ -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,

View File

@ -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:

View File

@ -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):

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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,
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,
class OvsDhcpAgentNotifierTestCase(test_l3_plugin.L3NatTestCaseMixin,
test_agent_ext_plugin.AgentDBTestMixIn,
AgentSchedulerTestMixIn,
test_plugin.QuantumDbPluginV2TestCase):
def setUp(self):
plugin = ('quantum.plugins.openvswitch.'
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'