OpenStack Networking (Neutron)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1717 lines
81 KiB

# Copyright (c) 2013 OpenStack Foundation.
#
# 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.
import datetime
import mock
from neutron_lib.api.definitions import dhcpagentscheduler as das_apidef
from neutron_lib.api.definitions import portbindings
from neutron_lib.callbacks import events
from neutron_lib.callbacks import resources
from neutron_lib import constants
from neutron_lib import context
from neutron_lib.plugins import constants as plugin_constants
from neutron_lib.plugins import directory
from oslo_config import cfg
from oslo_db import exception as db_exc
import oslo_messaging
from oslo_utils import uuidutils
from webob import exc
from neutron.api import extensions
from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
from neutron.api.rpc.handlers import dhcp_rpc
from neutron.api.rpc.handlers import l3_rpc
from neutron.db import agents_db
from neutron.db import agentschedulers_db
from neutron.db.models import agent as agent_model
from neutron.extensions import l3agentscheduler
from neutron.objects import agent as ag_obj
from neutron.objects import l3agent as rb_obj
from neutron.tests.common import helpers
from neutron.tests import fake_notifier
from neutron.tests.unit.api import test_extensions
from neutron.tests.unit.db import test_db_base_plugin_v2 as test_plugin
from neutron.tests.unit.extensions import test_agent
from neutron.tests.unit.extensions import test_l3
from neutron.tests.unit import testlib_api
from neutron import wsgi
L3_HOSTA = 'hosta'
DHCP_HOSTA = 'hosta'
L3_HOSTB = 'hostb'
DHCP_HOSTC = 'hostc'
DHCP_HOSTD = 'hostd'
DEVICE_OWNER_COMPUTE = ''.join([constants.DEVICE_OWNER_COMPUTE_PREFIX,
'test:',
DHCP_HOSTA])
class AgentSchedulerTestMixIn(object):
block_dhcp_notifier = False
def _request_list(self, path, admin_context=True,
expected_code=exc.HTTPOk.code):
req = self._path_req(path, admin_context=admin_context)
res = req.get_response(self.ext_api)
self.assertEqual(expected_code, res.status_int)
return self.deserialize(self.fmt, res)
def _path_req(self, path, method='GET', data=None,
query_string=None,
admin_context=True):
content_type = 'application/%s' % self.fmt
body = None
if data is not None: # empty dict is valid
body = wsgi.Serializer().serialize(data, content_type)
if admin_context:
return testlib_api.create_request(
path, body, content_type, method, query_string=query_string)
else:
return testlib_api.create_request(
path, body, content_type, method, query_string=query_string,
context=context.Context('', 'tenant_id'))
def _path_create_request(self, path, data, admin_context=True):
return self._path_req(path, method='POST', data=data,
admin_context=admin_context)
def _path_show_request(self, path, admin_context=True):
return self._path_req(path, admin_context=admin_context)
def _path_delete_request(self, path, admin_context=True):
return self._path_req(path, method='DELETE',
admin_context=admin_context)
def _path_update_request(self, path, data, admin_context=True):
return self._path_req(path, method='PUT', data=data,
admin_context=admin_context)
def _list_routers_hosted_by_l3_agent(self, agent_id,
expected_code=exc.HTTPOk.code,
admin_context=True):
path = "/agents/%s/%s.%s" % (agent_id,
l3agentscheduler.L3_ROUTERS,
self.fmt)
return self._request_list(path, expected_code=expected_code,
admin_context=admin_context)
def _list_networks_hosted_by_dhcp_agent(self, agent_id,
expected_code=exc.HTTPOk.code,
admin_context=True):
path = "/agents/%s/%s.%s" % (agent_id,
das_apidef.DHCP_NETS,
self.fmt)
return self._request_list(path, expected_code=expected_code,
admin_context=admin_context)
def _list_l3_agents_hosting_router(self, router_id,
expected_code=exc.HTTPOk.code,
admin_context=True):
path = "/routers/%s/%s.%s" % (router_id,
l3agentscheduler.L3_AGENTS,
self.fmt)
return self._request_list(path, expected_code=expected_code,
admin_context=admin_context)
def _list_dhcp_agents_hosting_network(self, network_id,
expected_code=exc.HTTPOk.code,
admin_context=True):
path = "/networks/%s/%s.%s" % (network_id,
das_apidef.DHCP_AGENTS,
self.fmt)
return self._request_list(path, expected_code=expected_code,
admin_context=admin_context)
def _add_router_to_l3_agent(self, id, router_id,
expected_code=exc.HTTPCreated.code,
admin_context=True):
path = "/agents/%s/%s.%s" % (id,
l3agentscheduler.L3_ROUTERS,
self.fmt)
req = self._path_create_request(path,
{'router_id': router_id},
admin_context=admin_context)
res = req.get_response(self.ext_api)
self.assertEqual(expected_code, res.status_int)
def _add_network_to_dhcp_agent(self, id, network_id,
expected_code=exc.HTTPCreated.code,
admin_context=True):
path = "/agents/%s/%s.%s" % (id,
das_apidef.DHCP_NETS,
self.fmt)
req = self._path_create_request(path,
{'network_id': network_id},
admin_context=admin_context)
res = req.get_response(self.ext_api)
self.assertEqual(expected_code, res.status_int)
def _remove_network_from_dhcp_agent(self, id, network_id,
expected_code=exc.HTTPNoContent.code,
admin_context=True):
path = "/agents/%s/%s/%s.%s" % (id,
das_apidef.DHCP_NETS,
network_id,
self.fmt)
req = self._path_delete_request(path,
admin_context=admin_context)
res = req.get_response(self.ext_api)
self.assertEqual(expected_code, res.status_int)
def _remove_router_from_l3_agent(self, id, router_id,
expected_code=exc.HTTPNoContent.code,
admin_context=True):
path = "/agents/%s/%s/%s.%s" % (id,
l3agentscheduler.L3_ROUTERS,
router_id,
self.fmt)
req = self._path_delete_request(path, admin_context=admin_context)
res = req.get_response(self.ext_api)
self.assertEqual(expected_code, res.status_int)
def _assert_notify(self, notifications, expected_event_type):
event_types = [event['event_type'] for event in notifications]
self.assertIn(expected_event_type, event_types)
def test_agent_registration_bad_timestamp(self):
callback = agents_db.AgentExtRpcCallback()
delta_time = datetime.datetime.now() - datetime.timedelta(days=1)
str_time = delta_time.strftime('%Y-%m-%dT%H:%M:%S.%f')
callback.report_state(
self.adminContext,
agent_state={
'agent_state': helpers._get_dhcp_agent_dict(DHCP_HOSTA)},
time=str_time)
def test_agent_registration_invalid_timestamp_allowed(self):
callback = agents_db.AgentExtRpcCallback()
utc_time = datetime.datetime.utcnow()
delta_time = utc_time - datetime.timedelta(seconds=10)
str_time = delta_time.strftime('%Y-%m-%dT%H:%M:%S.%f')
callback.report_state(
self.adminContext,
agent_state={
'agent_state': helpers._get_dhcp_agent_dict(DHCP_HOSTA)},
time=str_time)
def _disable_agent(self, agent_id, admin_state_up=False):
new_agent = {}
new_agent['agent'] = {}
new_agent['agent']['admin_state_up'] = admin_state_up
self._update('agents', agent_id, new_agent)
def _get_agent_id(self, agent_type, host):
agents = self._list_agents()
for agent_data in agents['agents']:
if (agent_data['agent_type'] == agent_type and
agent_data['host'] == host):
return agent_data['id']
class OvsAgentSchedulerTestCaseBase(test_l3.L3NatTestCaseMixin,
test_agent.AgentDBTestMixIn,
AgentSchedulerTestMixIn,
test_plugin.NeutronDbPluginV2TestCase):
fmt = 'json'
l3_plugin = ('neutron.tests.unit.extensions.test_l3.'
'TestL3NatAgentSchedulingServicePlugin')
def setUp(self):
if self.l3_plugin:
service_plugins = {
'l3_plugin_name': self.l3_plugin,
'flavors_plugin_name': 'neutron.services.flavors.'
'flavors_plugin.FlavorsPlugin'
}
else:
service_plugins = None
# NOTE(ivasilevskaya) mocking this way allows some control over mocked
# client like further method mocking with asserting calls
self.client_mock = mock.MagicMock(name="mocked client")
mock.patch('neutron.common.rpc.get_client'
).start().return_value = self.client_mock
super(OvsAgentSchedulerTestCaseBase, self).setUp(
'ml2', service_plugins=service_plugins)
mock.patch.object(
self.plugin, 'filter_hosts_with_network_access',
side_effect=lambda context, network_id, hosts: hosts).start()
ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr)
self.adminContext = context.get_admin_context()
self.l3plugin = directory.get_plugin(plugin_constants.L3)
self.l3_notify_p = mock.patch(
'neutron.extensions.l3agentscheduler.notify')
self.patched_l3_notify = self.l3_notify_p.start()
self.l3_periodic_p = mock.patch('neutron.db.l3_agentschedulers_db.'
'L3AgentSchedulerDbMixin.'
'add_periodic_l3_agent_status_check')
self.patched_l3_periodic = self.l3_periodic_p.start()
self.dhcp_notify_p = mock.patch(
'neutron.extensions.dhcpagentscheduler.notify')
self.patched_dhcp_notify = self.dhcp_notify_p.start()
class OvsAgentSchedulerTestCase(OvsAgentSchedulerTestCaseBase):
def test_report_states(self):
self._register_agent_states()
agents = self._list_agents()
self.assertEqual(4, len(agents['agents']))
def test_list_router_ids_on_host_no_l3_agent(self):
l3_rpc_cb = l3_rpc.L3RpcCallback()
self.assertEqual(
[],
l3_rpc_cb.get_router_ids(self.adminContext, host="fake host"))
def test_network_scheduling_on_network_creation(self):
self._register_agent_states()
with self.network() as net:
dhcp_agents = self._list_dhcp_agents_hosting_network(
net['network']['id'])
self.assertEqual(0, len(dhcp_agents['agents']))
def test_network_auto_schedule_with_disabled(self):
cfg.CONF.set_override('allow_overlapping_ips', True)
with self.subnet(), self.subnet():
dhcp_rpc_cb = dhcp_rpc.DhcpRpcCallback()
self._register_agent_states()
hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP,
DHCP_HOSTA)
hostc_id = self._get_agent_id(constants.AGENT_TYPE_DHCP,
DHCP_HOSTC)
self._disable_agent(hosta_id)
dhcp_rpc_cb.get_active_networks_info(
self.adminContext, host=DHCP_HOSTA)
# second agent will host all the networks since first is disabled.
dhcp_rpc_cb.get_active_networks_info(
self.adminContext, host=DHCP_HOSTC)
networks = self._list_networks_hosted_by_dhcp_agent(hostc_id)
num_hostc_nets = len(networks['networks'])
networks = self._list_networks_hosted_by_dhcp_agent(hosta_id)
num_hosta_nets = len(networks['networks'])
self.assertEqual(0, num_hosta_nets)
self.assertEqual(2, num_hostc_nets)
def test_network_auto_schedule_with_no_dhcp(self):
cfg.CONF.set_override('allow_overlapping_ips', True)
with self.subnet(enable_dhcp=False), self.subnet(enable_dhcp=False):
dhcp_rpc_cb = dhcp_rpc.DhcpRpcCallback()
self._register_agent_states()
hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP,
DHCP_HOSTA)
hostc_id = self._get_agent_id(constants.AGENT_TYPE_DHCP,
DHCP_HOSTC)
self._disable_agent(hosta_id)
dhcp_rpc_cb.get_active_networks_info(
self.adminContext, host=DHCP_HOSTA)
dhcp_rpc_cb.get_active_networks_info(
self.adminContext, host=DHCP_HOSTC)
networks = self._list_networks_hosted_by_dhcp_agent(hostc_id)
num_hostc_nets = len(networks['networks'])
networks = self._list_networks_hosted_by_dhcp_agent(hosta_id)
num_hosta_nets = len(networks['networks'])
self.assertEqual(0, num_hosta_nets)
self.assertEqual(0, num_hostc_nets)
def test_network_auto_schedule_with_multiple_agents(self):
cfg.CONF.set_override('dhcp_agents_per_network', 2)
cfg.CONF.set_override('allow_overlapping_ips', True)
with self.subnet(), self.subnet():
dhcp_rpc_cb = dhcp_rpc.DhcpRpcCallback()
self._register_agent_states()
hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP,
DHCP_HOSTA)
hostc_id = self._get_agent_id(constants.AGENT_TYPE_DHCP,
DHCP_HOSTC)
dhcp_rpc_cb.get_active_networks_info(
self.adminContext, host=DHCP_HOSTA)
dhcp_rpc_cb.get_active_networks_info(
self.adminContext, host=DHCP_HOSTC)
networks = self._list_networks_hosted_by_dhcp_agent(hostc_id)
num_hostc_nets = len(networks['networks'])
networks = self._list_networks_hosted_by_dhcp_agent(hosta_id)
num_hosta_nets = len(networks['networks'])
self.assertEqual(2, num_hosta_nets)
self.assertEqual(2, num_hostc_nets)
def test_network_auto_schedule_restart_dhcp_agent(self):
cfg.CONF.set_override('dhcp_agents_per_network', 2)
with self.subnet() as sub1:
dhcp_rpc_cb = dhcp_rpc.DhcpRpcCallback()
self._register_agent_states()
dhcp_rpc_cb.get_active_networks_info(
self.adminContext, host=DHCP_HOSTA)
dhcp_rpc_cb.get_active_networks_info(
self.adminContext, host=DHCP_HOSTA)
dhcp_agents = self._list_dhcp_agents_hosting_network(
sub1['subnet']['network_id'])
self.assertEqual(1, len(dhcp_agents['agents']))
def test_network_auto_schedule_with_hosted(self):
# one agent hosts all the networks, other hosts none
cfg.CONF.set_override('allow_overlapping_ips', True)
with self.subnet() as sub1, self.subnet():
dhcp_rpc_cb = dhcp_rpc.DhcpRpcCallback()
self._register_agent_states()
dhcp_rpc_cb.get_active_networks_info(
self.adminContext, host=DHCP_HOSTA)
# second agent will not host the network since first has got it.
dhcp_rpc_cb.get_active_networks_info(
self.adminContext, host=DHCP_HOSTC)
dhcp_agents = self._list_dhcp_agents_hosting_network(
sub1['subnet']['network_id'])
hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP,
DHCP_HOSTA)
hostc_id = self._get_agent_id(constants.AGENT_TYPE_DHCP,
DHCP_HOSTC)
hosta_nets = self._list_networks_hosted_by_dhcp_agent(hosta_id)
num_hosta_nets = len(hosta_nets['networks'])
hostc_nets = self._list_networks_hosted_by_dhcp_agent(hostc_id)
num_hostc_nets = len(hostc_nets['networks'])
self.assertEqual(2, num_hosta_nets)
self.assertEqual(0, num_hostc_nets)
self.assertEqual(1, len(dhcp_agents['agents']))
self.assertEqual(DHCP_HOSTA, dhcp_agents['agents'][0]['host'])
def test_network_auto_schedule_with_hosted_2(self):
# one agent hosts one network
dhcp_rpc_cb = dhcp_rpc.DhcpRpcCallback()
cfg.CONF.set_override('allow_overlapping_ips', True)
with self.subnet() as sub1:
helpers.register_dhcp_agent(DHCP_HOSTA)
dhcp_rpc_cb.get_active_networks_info(
self.adminContext, host=DHCP_HOSTA)
hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP,
DHCP_HOSTA)
self._disable_agent(hosta_id, admin_state_up=False)
with self.subnet() as sub2:
helpers.register_dhcp_agent(DHCP_HOSTC)
dhcp_rpc_cb.get_active_networks_info(self.adminContext,
host=DHCP_HOSTC)
dhcp_agents_1 = self._list_dhcp_agents_hosting_network(
sub1['subnet']['network_id'])
dhcp_agents_2 = self._list_dhcp_agents_hosting_network(
sub2['subnet']['network_id'])
hosta_nets = self._list_networks_hosted_by_dhcp_agent(hosta_id)
num_hosta_nets = len(hosta_nets['networks'])
hostc_id = self._get_agent_id(
constants.AGENT_TYPE_DHCP,
DHCP_HOSTC)
hostc_nets = self._list_networks_hosted_by_dhcp_agent(hostc_id)
num_hostc_nets = len(hostc_nets['networks'])
self.assertEqual(1, num_hosta_nets)
self.assertEqual(1, num_hostc_nets)
self.assertEqual(1, len(dhcp_agents_1['agents']))
self.assertEqual(1, len(dhcp_agents_2['agents']))
self.assertEqual(DHCP_HOSTA, dhcp_agents_1['agents'][0]['host'])
self.assertEqual(DHCP_HOSTC, dhcp_agents_2['agents'][0]['host'])
def test_network_scheduling_on_port_creation(self):
with self.subnet() as subnet:
dhcp_agents = self._list_dhcp_agents_hosting_network(
subnet['subnet']['network_id'])
result0 = len(dhcp_agents['agents'])
self._register_agent_states()
with self.port(subnet=subnet,
device_owner=DEVICE_OWNER_COMPUTE) as port:
dhcp_agents = self._list_dhcp_agents_hosting_network(
port['port']['network_id'])
result1 = len(dhcp_agents['agents'])
self.assertEqual(0, result0)
self.assertEqual(1, result1)
def test_network_ha_scheduling_on_port_creation(self):
cfg.CONF.set_override('dhcp_agents_per_network', 2)
with self.subnet() as subnet:
dhcp_agents = self._list_dhcp_agents_hosting_network(
subnet['subnet']['network_id'])
result0 = len(dhcp_agents['agents'])
self._register_agent_states()
with self.port(subnet=subnet,
device_owner=DEVICE_OWNER_COMPUTE) as port:
dhcp_agents = self._list_dhcp_agents_hosting_network(
port['port']['network_id'])
result1 = len(dhcp_agents['agents'])
self.assertEqual(0, result0)
self.assertEqual(2, result1)
def test_network_ha_scheduling_on_port_creation_with_new_agent(self):
cfg.CONF.set_override('dhcp_agents_per_network', 3)
with self.subnet() as subnet:
dhcp_agents = self._list_dhcp_agents_hosting_network(
subnet['subnet']['network_id'])
result0 = len(dhcp_agents['agents'])
self._register_agent_states()
with self.port(subnet=subnet,
device_owner=DEVICE_OWNER_COMPUTE) as port:
dhcp_agents = self._list_dhcp_agents_hosting_network(
port['port']['network_id'])
result1 = len(dhcp_agents['agents'])
helpers.register_dhcp_agent('host1')
with self.port(subnet=subnet,
device_owner=DEVICE_OWNER_COMPUTE) as port:
dhcp_agents = self._list_dhcp_agents_hosting_network(
port['port']['network_id'])
result2 = len(dhcp_agents['agents'])
self.assertEqual(0, result0)
self.assertEqual(2, result1)
self.assertEqual(3, result2)
def test_network_scheduler_with_disabled_agent(self):
helpers.register_dhcp_agent(DHCP_HOSTA)
with self.port() as port1:
dhcp_agents = self._list_dhcp_agents_hosting_network(
port1['port']['network_id'])
self._delete('ports', port1['port']['id'])
self._delete('networks', port1['port']['network_id'])
self.assertEqual(1, len(dhcp_agents['agents']))
agents = self._list_agents()
self._disable_agent(agents['agents'][0]['id'])
with self.port() as port2:
dhcp_agents = self._list_dhcp_agents_hosting_network(
port2['port']['network_id'])
self._delete('ports', port2['port']['id'])
self.assertEqual(0, len(dhcp_agents['agents']))
def test_is_eligible_agent(self):
agent_startup = ('neutron.db.agentschedulers_db.'
'DhcpAgentSchedulerDbMixin.agent_starting_up')
is_eligible_agent = ('neutron.db.agentschedulers_db.'
'AgentSchedulerDbMixin.is_eligible_agent')
dhcp_mixin = agentschedulers_db.DhcpAgentSchedulerDbMixin()
with mock.patch(agent_startup) as startup,\
mock.patch(is_eligible_agent) as elig:
tests = [(True, True),
(True, False),
(False, True),
(False, False)]
for rv1, rv2 in tests:
startup.return_value = rv1
elig.return_value = rv2
self.assertEqual(rv1 or rv2,
dhcp_mixin.is_eligible_agent(None,
None, None))
def test_network_scheduler_with_down_agent(self):
helpers.register_dhcp_agent(DHCP_HOSTA)
eligible_agent_str = ('neutron.db.agentschedulers_db.'
'DhcpAgentSchedulerDbMixin.is_eligible_agent')
with mock.patch(eligible_agent_str) as eligible_agent:
eligible_agent.return_value = True
with self.port() as port:
dhcp_agents = self._list_dhcp_agents_hosting_network(
port['port']['network_id'])
self._delete('ports', port['port']['id'])
self._delete('networks', port['port']['network_id'])
self.assertEqual(1, len(dhcp_agents['agents']))
with mock.patch(eligible_agent_str) as eligible_agent:
eligible_agent.return_value = False
with self.port() as port:
dhcp_agents = self._list_dhcp_agents_hosting_network(
port['port']['network_id'])
self._delete('ports', port['port']['id'])
self.assertEqual(0, len(dhcp_agents['agents']))
def test_network_scheduler_with_hosted_network(self):
plugin = directory.get_plugin()
helpers.register_dhcp_agent(DHCP_HOSTA)
with self.port() as port1:
dhcp_agents = self._list_dhcp_agents_hosting_network(
port1['port']['network_id'])
self.assertEqual(1, len(dhcp_agents['agents']))
with mock.patch.object(plugin,
'get_dhcp_agents_hosting_networks',
autospec=True) as mock_hosting_agents:
mock_hosting_agents.return_value = plugin.get_agent_objects(
self.adminContext)
with self.network('test') as net1:
pass
with self.subnet(network=net1,
cidr='10.0.1.0/24') as subnet1:
pass
with self.port(subnet=subnet1) as port2:
pass
dhcp_agents = self._list_dhcp_agents_hosting_network(
port2['port']['network_id'])
self.assertEqual(0, len(dhcp_agents['agents']))
def test_network_policy(self):
with self.network() as net1:
self._register_agent_states()
hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP,
DHCP_HOSTA)
self._list_networks_hosted_by_dhcp_agent(
hosta_id, expected_code=exc.HTTPForbidden.code,
admin_context=False)
self._add_network_to_dhcp_agent(
hosta_id, net1['network']['id'],
expected_code=exc.HTTPForbidden.code,
admin_context=False)
self._add_network_to_dhcp_agent(hosta_id,
net1['network']['id'])
self._remove_network_from_dhcp_agent(
hosta_id, net1['network']['id'],
expected_code=exc.HTTPForbidden.code,
admin_context=False)
self._list_dhcp_agents_hosting_network(
net1['network']['id'],
expected_code=exc.HTTPForbidden.code,
admin_context=False)
def _test_network_add_to_dhcp_agent(self, admin_state_up=True):
with self.network() as net1:
self._register_agent_states()
hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP,
DHCP_HOSTA)
if not admin_state_up:
self._set_agent_admin_state_up(DHCP_HOSTA, False)
num_before_add = len(
self._list_networks_hosted_by_dhcp_agent(
hosta_id)['networks'])
self._add_network_to_dhcp_agent(hosta_id,
net1['network']['id'])
num_after_add = len(
self._list_networks_hosted_by_dhcp_agent(
hosta_id)['networks'])
self.assertEqual(0, num_before_add)
self.assertEqual(1, num_after_add)
def test_network_add_to_dhcp_agent(self):
self._test_network_add_to_dhcp_agent()
def test_network_add_to_dhcp_agent_with_admin_state_down(self):
cfg.CONF.set_override(
'enable_services_on_agents_with_admin_state_down', True)
self._test_network_add_to_dhcp_agent(admin_state_up=False)
def _test_network_remove_from_dhcp_agent(self,
concurrent_port_delete=False):
agent = helpers.register_dhcp_agent(DHCP_HOSTA)
hosta_id = agent.id
with self.port(device_owner=constants.DEVICE_OWNER_DHCP,
host=DHCP_HOSTA) as port1:
num_before_remove = len(
self._list_networks_hosted_by_dhcp_agent(
hosta_id)['networks'])
if concurrent_port_delete:
plugin = directory.get_plugin()
# Return a foo port to emulate the port not found scenario
# caused by a concurrent port deletion during unscheduling
port = {'id': 'foo_port_id', 'device_id': 'foo_device_id'}
mock.patch.object(plugin, 'get_ports',
return_value=[port]).start()
self._remove_network_from_dhcp_agent(hosta_id,
port1['port']['network_id'])
num_after_remove = len(
self._list_networks_hosted_by_dhcp_agent(
hosta_id)['networks'])
self.assertEqual(1, num_before_remove)
self.assertEqual(0, num_after_remove)
def test_network_remove_from_dhcp_agent(self):
self._test_network_remove_from_dhcp_agent()
def test_network_remove_from_dhcp_agent_on_concurrent_port_delete(self):
self._test_network_remove_from_dhcp_agent(concurrent_port_delete=True)
def test_list_active_networks_on_not_registered_yet_dhcp_agent(self):
plugin = directory.get_plugin()
nets = plugin.list_active_networks_on_active_dhcp_agent(
self.adminContext, host=DHCP_HOSTA)
self.assertEqual([], nets)
def test_reserved_port_after_network_remove_from_dhcp_agent(self):
helpers.register_dhcp_agent(DHCP_HOSTA)
hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP,
DHCP_HOSTA)
with self.port(device_owner=constants.DEVICE_OWNER_DHCP,
host=DHCP_HOSTA) as port1:
self._remove_network_from_dhcp_agent(hosta_id,
port1['port']['network_id'])
port_res = self._list_ports(
'json',
200,
network_id=port1['port']['network_id'])
port_list = self.deserialize('json', port_res)
self.assertEqual(port_list['ports'][0]['device_id'],
constants.DEVICE_ID_RESERVED_DHCP_PORT)
def _test_get_active_networks_from_admin_state_down_agent(self,
keep_services):
if keep_services:
cfg.CONF.set_override(
'enable_services_on_agents_with_admin_state_down', True)
helpers.register_dhcp_agent(DHCP_HOSTA)
dhcp_rpc_cb = dhcp_rpc.DhcpRpcCallback()
with self.port():
nets = dhcp_rpc_cb.get_active_networks_info(self.adminContext,
host=DHCP_HOSTA)
self.assertEqual(1, len(nets))
self._set_agent_admin_state_up(DHCP_HOSTA, False)
nets = dhcp_rpc_cb.get_active_networks_info(self.adminContext,
host=DHCP_HOSTA)
if keep_services:
self.assertEqual(1, len(nets))
else:
self.assertEqual(0, len(nets))
def test_dhcp_agent_keep_services_off(self):
self._test_get_active_networks_from_admin_state_down_agent(False)
def test_dhcp_agent_keep_services_on(self):
self._test_get_active_networks_from_admin_state_down_agent(True)
def _take_down_agent_and_run_reschedule(self, host):
# take down the agent on host A and ensure B is alive
self.adminContext.session.begin(subtransactions=True)
query = self.adminContext.session.query(agent_model.Agent)
agt = query.filter_by(host=host).first()
agt.heartbeat_timestamp = (
agt.heartbeat_timestamp - datetime.timedelta(hours=1))
self.adminContext.session.commit()
plugin = directory.get_plugin(plugin_constants.L3)
plugin.reschedule_routers_from_down_agents()
def _set_agent_admin_state_up(self, host, state):
self.adminContext.session.begin(subtransactions=True)
query = self.adminContext.session.query(agent_model.Agent)
agt_db = query.filter_by(host=host).first()
agt_db.admin_state_up = state
self.adminContext.session.commit()
def test_router_rescheduler_catches_rpc_db_and_reschedule_exceptions(self):
with self.router():
l3_rpc_cb = l3_rpc.L3RpcCallback()
self._register_agent_states()
# schedule the router to host A
l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA)
plugin = directory.get_plugin(plugin_constants.L3)
mock.patch.object(
plugin, 'reschedule_router',
side_effect=[
db_exc.DBError(), oslo_messaging.RemoteError(),
l3agentscheduler.RouterReschedulingFailed(router_id='f',
agent_id='f'),
ValueError('this raises'),
Exception()
]).start()
self._take_down_agent_and_run_reschedule(L3_HOSTA) # DBError
self._take_down_agent_and_run_reschedule(L3_HOSTA) # RemoteError
self._take_down_agent_and_run_reschedule(L3_HOSTA) # schedule err
self._take_down_agent_and_run_reschedule(L3_HOSTA) # Value error
self._take_down_agent_and_run_reschedule(L3_HOSTA) # Exception
def test_router_rescheduler_catches_exceptions_on_fetching_bindings(self):
with mock.patch('neutron_lib.context.get_admin_context') as get_ctx:
mock_ctx = mock.Mock()
get_ctx.return_value = mock_ctx
mock_ctx.session.query.side_effect = db_exc.DBError()
plugin = directory.get_plugin(plugin_constants.L3)
# check that no exception is raised
plugin.reschedule_routers_from_down_agents()
def test_router_rescheduler_iterates_after_reschedule_failure(self):
plugin = directory.get_plugin(plugin_constants.L3)
l3_rpc_cb = l3_rpc.L3RpcCallback()
self._register_agent_states()
with self.router() as r1, self.router() as r2:
# schedule the routers to host A
l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA)
rs_mock = mock.patch.object(
plugin, 'reschedule_router',
side_effect=l3agentscheduler.RouterReschedulingFailed(
router_id='f', agent_id='f'),
).start()
self._take_down_agent_and_run_reschedule(L3_HOSTA)
# make sure both had a reschedule attempt even though first failed
rs_mock.assert_has_calls([mock.call(mock.ANY, r1['router']['id']),
mock.call(mock.ANY, r2['router']['id'])],
any_order=True)
def test_router_is_not_rescheduled_from_alive_agent(self):
with self.router():
l3_rpc_cb = l3_rpc.L3RpcCallback()
self._register_agent_states()
# schedule the router to host A
l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA)
with mock.patch('neutron.db.l3_agentschedulers_db.'
'L3AgentSchedulerDbMixin.reschedule_router') as rr:
# take down some unrelated agent and run reschedule check
self._take_down_agent_and_run_reschedule(DHCP_HOSTC)
self.assertFalse(rr.called)
def test_router_is_not_rescheduled_if_agent_is_back_online(self):
plugin = directory.get_plugin(plugin_constants.L3)
l3_rpc_cb = l3_rpc.L3RpcCallback()
agent = helpers.register_l3_agent(host=L3_HOSTA)
with self.router(),\
self.router(),\
mock.patch.object(plugin, 'reschedule_router') as rs_mock,\
mock.patch.object(plugin, '_get_agent') as get_agent_mock:
# schedule the routers to the agent
l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA)
self._take_down_agent_and_run_reschedule(L3_HOSTA)
# since _get_agent is mocked it will return Mock object and
# agent.is_active will return true, so no rescheduling will be done
self.assertFalse(rs_mock.called)
# should be called only once as for second router alive agent id
# will be in cache
get_agent_mock.assert_called_once_with(mock.ANY, agent['id'])
def test_router_reschedule_from_dead_agent(self):
with self.router():
l3_rpc_cb = l3_rpc.L3RpcCallback()
self._register_agent_states()
# schedule the router to host A
ret_a = l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA)
self._take_down_agent_and_run_reschedule(L3_HOSTA)
# B should now pick up the router
ret_b = l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTB)
self.assertEqual(ret_b, ret_a)
def test_router_no_reschedule_from_dead_admin_down_agent(self):
with self.router() as r:
l3_rpc_cb = l3_rpc.L3RpcCallback()
self._register_agent_states()
# schedule the router to host A
l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA)
self._set_agent_admin_state_up(L3_HOSTA, False)
self._take_down_agent_and_run_reschedule(L3_HOSTA)
# A should still have it even though it was inactive due to the
# admin_state being down
bindings = rb_obj.RouterL3AgentBinding.get_objects(
self.adminContext, router_id=r['router']['id'])
binding = bindings.pop() if bindings else None
l3_agent = ag_obj.Agent.get_objects(
self.adminContext, id=binding.l3_agent_id)
self.assertEqual(l3_agent[0].host, L3_HOSTA)
# B should not pick up the router
ret_b = l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTB)
self.assertFalse(ret_b)
def test_router_reschedule_succeeded_after_failed_notification(self):
l3_rpc_cb = l3_rpc.L3RpcCallback()
self._register_agent_states()
with self.router() as router:
# schedule the router to host A
l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA)
ctxt_mock = mock.MagicMock()
call_mock = mock.MagicMock(
side_effect=[oslo_messaging.MessagingTimeout, None])
ctxt_mock.call = call_mock
self.client_mock.prepare = mock.MagicMock(return_value=ctxt_mock)
self._take_down_agent_and_run_reschedule(L3_HOSTA)
self.assertEqual(2, call_mock.call_count)
# make sure router was rescheduled even when first attempt
# failed to notify l3 agent
l3_agents = self._list_l3_agents_hosting_router(
router['router']['id'])['agents']
self.assertEqual(1, len(l3_agents))
self.assertEqual(L3_HOSTB, l3_agents[0]['host'])
def test_router_reschedule_failed_notification_all_attempts(self):
l3_rpc_cb = l3_rpc.L3RpcCallback()
self._register_agent_states()
with self.router() as router:
# schedule the router to host A
l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA)
# mock client.prepare and context.call
ctxt_mock = mock.MagicMock()
call_mock = mock.MagicMock(
side_effect=oslo_messaging.MessagingTimeout)
ctxt_mock.call = call_mock
self.client_mock.prepare = mock.MagicMock(return_value=ctxt_mock)
# perform operations
self._take_down_agent_and_run_reschedule(L3_HOSTA)
self.assertEqual(
l3_rpc_agent_api.AGENT_NOTIFY_MAX_ATTEMPTS,
call_mock.call_count)
l3_agents = self._list_l3_agents_hosting_router(
router['router']['id'])['agents']
self.assertEqual(0, len(l3_agents))
def test_router_reschedule_no_remove_if_agent_has_dvr_service_ports(self):
l3_notifier = self.l3plugin.agent_notifiers[constants.AGENT_TYPE_L3]
agent_a = helpers.register_l3_agent(
host=L3_HOSTA, agent_mode=constants.L3_AGENT_MODE_DVR_SNAT)
agent_b = helpers.register_l3_agent(
host=L3_HOSTB, agent_mode=constants.L3_AGENT_MODE_DVR_SNAT)
with self.subnet() as s, \
mock.patch.object(l3_notifier.client, 'prepare',
return_value=l3_notifier.client) as mock_prepare, \
mock.patch.object(l3_notifier.client, 'cast') as mock_cast, \
mock.patch.object(l3_notifier.client, 'call'):
net_id = s['subnet']['network_id']
self._set_net_external(net_id)
router = {'name': 'router1',
'external_gateway_info': {'network_id': net_id},
'tenant_id': 'tenant_id',
'admin_state_up': True,
'distributed': True}
r = self.l3plugin.create_router(self.adminContext,
{'router': router})
# schedule the dvr to one of the agents
self.l3plugin.schedule_router(self.adminContext, r['id'])
l3agents = self.l3plugin.list_l3_agents_hosting_router(
self.adminContext, r['id'])
agent = l3agents['agents'][0]
# emulating dvr serviceable ports exist on the host
with mock.patch.object(
self.l3plugin, '_check_dvr_serviceable_ports_on_host') \
as ports_exist:
ports_exist.return_value = True
# reschedule the dvr to one of the other agent
candidate_agent = (agent_b if agent['host'] == L3_HOSTA
else agent_a)
self.l3plugin.reschedule_router(self.adminContext, r['id'],
candidates=[candidate_agent])
# make sure dvr serviceable ports are checked when rescheduling
self.assertTrue(ports_exist.called)
# make sure sending update instead of removing for dvr
mock_prepare.assert_called_with(server=candidate_agent['host'])
mock_cast.assert_called_with(
mock.ANY, 'routers_updated',
routers=[r['id']])
# make sure the rescheduling completes
l3agents = self.l3plugin.list_l3_agents_hosting_router(
self.adminContext, r['id'])
self.assertEqual(1, len(l3agents['agents']))
new_agent_host = l3agents['agents'][0]['host']
self.assertNotEqual(agent['host'], new_agent_host)
def test_router_auto_schedule_with_invalid_router(self):
with self.router() as router:
l3_rpc_cb = l3_rpc.L3RpcCallback()
self._register_agent_states()
self._delete('routers', router['router']['id'])
# deleted router
ret_a = l3_rpc_cb.sync_routers(self.adminContext, host=L3_HOSTA,
router_ids=[router['router']['id']])
self.assertFalse(ret_a)
# non-existent router
ret_a = l3_rpc_cb.sync_routers(self.adminContext, host=L3_HOSTA,
router_ids=[uuidutils.generate_uuid()])
self.assertFalse(ret_a)
def test_router_auto_schedule_with_hosted(self):
with self.router() as router:
l3_rpc_cb = l3_rpc.L3RpcCallback()
self._register_agent_states()
ret_a = l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA)
ret_b = l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTB)
l3_agents = self._list_l3_agents_hosting_router(
router['router']['id'])
self.assertEqual(1, len(ret_a))
self.assertIn(router['router']['id'], ret_a)
self.assertFalse(len(ret_b))
self.assertEqual(1, len(l3_agents['agents']))
self.assertEqual(L3_HOSTA, l3_agents['agents'][0]['host'])
def test_router_auto_schedule_restart_l3_agent(self):
with self.router():
l3_rpc_cb = l3_rpc.L3RpcCallback()
self._register_agent_states()
l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA)
l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA)
def test_router_auto_schedule_with_hosted_2(self):
# one agent hosts one router
l3_rpc_cb = l3_rpc.L3RpcCallback()
with self.router() as router1:
hosta_id = helpers.register_l3_agent(host=L3_HOSTA).id
l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA)
self._disable_agent(hosta_id, admin_state_up=False)
with self.router() as router2:
hostb_id = helpers.register_l3_agent(host=L3_HOSTB).id
l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTB)
l3_agents_1 = self._list_l3_agents_hosting_router(
router1['router']['id'])
l3_agents_2 = self._list_l3_agents_hosting_router(
router2['router']['id'])
hosta_routers = self._list_routers_hosted_by_l3_agent(hosta_id)
num_hosta_routers = len(hosta_routers['routers'])
hostb_routers = self._list_routers_hosted_by_l3_agent(hostb_id)
num_hostb_routers = len(hostb_routers['routers'])
self.assertEqual(1, num_hosta_routers)
self.assertEqual(1, num_hostb_routers)
self.assertEqual(1, len(l3_agents_1['agents']))
self.assertEqual(1, len(l3_agents_2['agents']))
self.assertEqual(L3_HOSTA, l3_agents_1['agents'][0]['host'])
self.assertEqual(L3_HOSTB, l3_agents_2['agents'][0]['host'])
def test_router_auto_schedule_with_disabled(self):
with self.router(), self.router():
l3_rpc_cb = l3_rpc.L3RpcCallback()
self._register_agent_states()
hosta_id = self._get_agent_id(constants.AGENT_TYPE_L3,
L3_HOSTA)
hostb_id = self._get_agent_id(constants.AGENT_TYPE_L3,
L3_HOSTB)
self._disable_agent(hosta_id)
# first agent will not host router since it is disabled
l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA)
# second agent will host all the routers since first is disabled.
l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTB)
hostb_routers = self._list_routers_hosted_by_l3_agent(hostb_id)
num_hostb_routers = len(hostb_routers['routers'])
hosta_routers = self._list_routers_hosted_by_l3_agent(hosta_id)
num_hosta_routers = len(hosta_routers['routers'])
self.assertEqual(2, num_hostb_routers)
self.assertEqual(0, num_hosta_routers)
def test_rpc_sync_routers(self):
l3_rpc_cb = l3_rpc.L3RpcCallback()
self._register_agent_states()
# No routers
ret_a = l3_rpc_cb.sync_routers(self.adminContext, host=L3_HOSTA)
self.assertEqual(0, len(ret_a))
with self.router() as v1, self.router() as v2, self.router() as v3:
routers = (v1, v2, v3)
router_ids = [r['router']['id'] for r in routers]
# auto schedule routers first
l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA)
# Get all routers
ret_a = l3_rpc_cb.sync_routers(self.adminContext, host=L3_HOSTA)
self.assertEqual(3, len(ret_a))
self.assertEqual(set(router_ids), set([r['id'] for r in ret_a]))
# Get all routers (router_ids=None)
ret_a = l3_rpc_cb.sync_routers(self.adminContext, host=L3_HOSTA,
router_ids=None)
self.assertEqual(3, len(ret_a))
self.assertEqual(set(router_ids), set([r['id'] for r in ret_a]))
# Get router2 only
ret_a = l3_rpc_cb.sync_routers(self.adminContext, host=L3_HOSTA,
router_ids=[router_ids[1]])
self.assertEqual(1, len(ret_a))
self.assertIn(router_ids[1], [r['id'] for r in ret_a])
# Get router1 and router3
ret_a = l3_rpc_cb.sync_routers(self.adminContext, host=L3_HOSTA,
router_ids=[router_ids[0],
router_ids[2]])
self.assertEqual(2, len(ret_a))
self.assertIn(router_ids[0], [r['id'] for r in ret_a])
self.assertIn(router_ids[2], [r['id'] for r in ret_a])
def test_sync_router(self):
l3_rpc_cb = l3_rpc.L3RpcCallback()
self._register_agent_states()
hosta_id = self._get_agent_id(constants.AGENT_TYPE_L3, L3_HOSTA)
with self.router() as r1:
ret_a = l3_rpc_cb.sync_routers(self.adminContext, host=L3_HOSTA,
router_ids=[r1['router']['id']])
# Not return router to agent if the router is not bound to it.
self.assertEqual([], ret_a)
host_routers = self._list_routers_hosted_by_l3_agent(hosta_id)
# No router will be auto scheduled.
self.assertEqual(0, len(host_routers['routers']))
def test_sync_dvr_router(self):
l3_rpc_cb = l3_rpc.L3RpcCallback()
dvr_agents = self._register_dvr_agents()
with self.router() as r1, \
mock.patch.object(self.l3plugin, 'get_subnet_ids_on_router',
return_value=['fake_subnet_id']), \
mock.patch.object(self.l3plugin,
'_check_dvr_serviceable_ports_on_host',
return_value=True):
for l3_agent in dvr_agents:
host = l3_agent['host']
ret_a = l3_rpc_cb.sync_routers(self.adminContext, host=host,
router_ids=[r1['router']['id']])
router_ids = [r['id'] for r in ret_a]
# Return router to agent if there is dvr service port in agent.
self.assertIn(r1['router']['id'], router_ids)
host_routers = self._list_routers_hosted_by_l3_agent(
l3_agent['id'])
# No router will be auto scheduled.
self.assertEqual(0, len(host_routers['routers']))
def test_sync_dvr_router_with_fixedip_on_fip_net(self):
l3_rpc_cb = l3_rpc.L3RpcCallback()
self._register_dvr_agents()
with self.subnet() as s:
# first create an external network
net_id = s['subnet']['network_id']
self._set_net_external(net_id)
# create router with external gateway
router_data = {'name': 'router1',
'external_gateway_info': {'network_id': net_id},
'tenant_id': 'tenant_id',
'admin_state_up': True,
'distributed': True}
router = self.l3plugin.create_router(self.adminContext,
{'router': router_data})
self.l3plugin.schedule_router(self.adminContext, router['id'])
with self.port(subnet=s,
device_owner=DEVICE_OWNER_COMPUTE) as port:
# bind port to L3_HOSTB
updated_port = {
"port": {
portbindings.HOST_ID: L3_HOSTB
}
}
self.plugin.update_port(
self.adminContext,
port['port']['id'],
updated_port
)
ret_b = l3_rpc_cb.sync_routers(
self.adminContext,
host=L3_HOSTB,
router_ids=[router['id']])
router_ids = [r['id'] for r in ret_b]
self.assertEqual(0, len(router_ids))
def test_router_without_l3_agents(self):
with self.subnet() as s:
self._set_net_external(s['subnet']['network_id'])
data = {'router': {'tenant_id': uuidutils.generate_uuid()}}
data['router']['name'] = 'router1'
data['router']['external_gateway_info'] = {
'network_id': s['subnet']['network_id']}
router_req = self.new_create_request('routers', data, self.fmt)
res = router_req.get_response(self.ext_api)
router = self.deserialize(self.fmt, res)
l3agents = (
self.l3plugin.get_l3_agents_hosting_routers(
self.adminContext, [router['router']['id']]))
self._delete('routers', router['router']['id'])
self.assertEqual(0, len(l3agents))
def test_dvr_router_scheduling_to_only_dvr_snat_agent(self):
self._register_dvr_agents()
with self.subnet() as s:
net_id = s['subnet']['network_id']
self._set_net_external(net_id)
router = {'name': 'router1',
'external_gateway_info': {'network_id': net_id},
'tenant_id': 'tenant_id',
'admin_state_up': True,
'distributed': True}
r = self.l3plugin.create_router(self.adminContext,
{'router': router})
with mock.patch.object(
self.l3plugin,
'_check_dvr_serviceable_ports_on_host') as ports_exist:
# emulating dvr serviceable ports exist on compute node
ports_exist.return_value = True
self.l3plugin.schedule_router(
self.adminContext, r['id'])
l3agents = self._list_l3_agents_hosting_router(r['id'])
self.assertEqual(1, len(l3agents['agents']))
agent = l3agents['agents'][0]
self.assertEqual('dvr_snat',
agent['configurations']['agent_mode'])
def test_dvr_router_csnat_rescheduling(self):
helpers.register_l3_agent(
host=L3_HOSTA, agent_mode=constants.L3_AGENT_MODE_DVR_SNAT)
helpers.register_l3_agent(
host=L3_HOSTB, agent_mode=constants.L3_AGENT_MODE_DVR_SNAT)
with self.subnet() as s:
net_id = s['subnet']['network_id']
self._set_net_external(net_id)
router = {'name': 'router1',
'external_gateway_info': {'network_id': net_id},
'tenant_id': 'tenant_id',
'admin_state_up': True,
'distributed': True}
r = self.l3plugin.create_router(self.adminContext,
{'router': router})
self.l3plugin.schedule_router(
self.adminContext, r['id'])
l3agents = self._list_l3_agents_hosting_router(r['id'])
self.assertEqual(1, len(l3agents['agents']))
agent_host = l3agents['agents'][0]['host']
self._take_down_agent_and_run_reschedule(agent_host)
l3agents = self._list_l3_agents_hosting_router(r['id'])
self.assertEqual(1, len(l3agents['agents']))
new_agent_host = l3agents['agents'][0]['host']
self.assertNotEqual(agent_host, new_agent_host)
def test_dvr_router_manual_rescheduling(self):
helpers.register_l3_agent(
host=L3_HOSTA, agent_mode=constants.L3_AGENT_MODE_DVR_SNAT)
helpers.register_l3_agent(
host=L3_HOSTB, agent_mode=constants.L3_AGENT_MODE_DVR_SNAT)
with self.subnet() as s:
net_id = s['subnet']['network_id']
self._set_net_external(net_id)
router = {'name': 'router1',
'external_gateway_info': {'network_id': net_id},
'tenant_id': 'tenant_id',
'admin_state_up': True,
'distributed': True}
r = self.l3plugin.create_router(self.adminContext,
{'router': router})
self.l3plugin.schedule_router(
self.adminContext, r['id'])
l3agents = self.l3plugin.list_l3_agents_hosting_router(
self.adminContext, r['id'])
self.assertEqual(1, len(l3agents['agents']))
agent = l3agents['agents'][0]
# NOTE: Removing the router from the l3_agent will
# remove all the namespace since there is no other
# serviceable ports in the node that requires it.
self.l3plugin.remove_router_from_l3_agent(
self.adminContext, agent['id'], r['id'])
l3agents = self.l3plugin.list_l3_agents_hosting_router(
self.adminContext, r['id'])
self.assertEqual(0, len(l3agents['agents']))
self.l3plugin.add_router_to_l3_agent(
self.adminContext, agent['id'], r['id'])
l3agents = self.l3plugin.list_l3_agents_hosting_router(
self.adminContext, r['id'])
self.assertEqual(1, len(l3agents['agents']))
new_agent = l3agents['agents'][0]
self.assertEqual(agent['id'], new_agent['id'])
def test_router_sync_data(self):
with self.subnet() as s1,\
self.subnet(cidr='10.0.2.0/24') as s2,\
self.subnet(cidr='10.0.3.0/24') as s3:
self._register_agent_states()
self._set_net_external(s1['subnet']['network_id'])
data = {'router': {'tenant_id': uuidutils.generate_uuid()}}
data['router']['name'] = 'router1'
data['router']['external_gateway_info'] = {
'network_id': s1['subnet']['network_id']}
router_req = self.new_create_request('routers', data, self.fmt)
res = router_req.get_response(self.ext_api)
router = self.deserialize(self.fmt, res)
self._router_interface_action('add',
router['router']['id'],
s2['subnet']['id'],
None)
self._router_interface_action('add',
router['router']['id'],
s3['subnet']['id'],
None)
l3agents = self._list_l3_agents_hosting_router(
router['router']['id'])
self.assertEqual(1, len(l3agents['agents']))
agents = self._list_agents()
another_l3_agent_id = None
another_l3_agent_host = None
default = l3agents['agents'][0]['id']
for com in agents['agents']:
if (com['id'] != default and
com['agent_type'] == constants.AGENT_TYPE_L3):
another_l3_agent_id = com['id']
another_l3_agent_host = com['host']
break
self.assertIsNotNone(another_l3_agent_id)
self._add_router_to_l3_agent(another_l3_agent_id,
router['router']['id'],
expected_code=exc.HTTPConflict.code)
self._remove_router_from_l3_agent(default,
router['router']['id'])
self._add_router_to_l3_agent(another_l3_agent_id,
router['router']['id'])
l3agents = self._list_l3_agents_hosting_router(
router['router']['id'])
self.assertEqual(another_l3_agent_host,
l3agents['agents'][0]['host'])
self._remove_router_from_l3_agent(another_l3_agent_id,
router['router']['id'])
self._router_interface_action('remove',
router['router']['id'],
s2['subnet']['id'],
None)
l3agents = self._list_l3_agents_hosting_router(
router['router']['id'])
self.assertEqual(1,
len(l3agents['agents']))
self._router_interface_action('remove',
router['router']['id'],
s3['subnet']['id'],
None)
self._delete('routers', router['router']['id'])
def _test_router_add_to_l3_agent(self, admin_state_up=True):
with self.router() as router1:
self._register_agent_states()
hosta_id = self._get_agent_id(constants.AGENT_TYPE_L3,
L3_HOSTA)
if not admin_state_up:
self._set_agent_admin_state_up(L3_HOSTA, False)
num_before_add = len(
self._list_routers_hosted_by_l3_agent(
hosta_id)['routers'])
self._add_router_to_l3_agent(hosta_id,
router1['router']['id'])
hostb_id = self._get_agent_id(constants.AGENT_TYPE_L3,
L3_HOSTB)
self._add_router_to_l3_agent(hostb_id,
router1['router']['id'],
expected_code=exc.HTTPConflict.code)
num_after_add = len(
self._list_routers_hosted_by_l3_agent(
hosta_id)['routers'])
self.assertEqual(0, num_before_add)
self.assertEqual(1, num_after_add)
def test_router_add_to_l3_agent(self):
self._test_router_add_to_l3_agent()
def test_router_add_to_l3_agent_with_admin_state_down(self):
cfg.CONF.set_override(
'enable_services_on_agents_with_admin_state_down', True)
self._test_router_add_to_l3_agent(admin_state_up=False)
def test_router_add_to_l3_agent_two_times(self):
with self.router() as router1:
self._register_agent_states()
hosta_id = self._get_agent_id(constants.AGENT_TYPE_L3,
L3_HOSTA)
self._add_router_to_l3_agent(hosta_id,
router1['router']['id'])
# scheduling twice on the same agent is fine
self._add_router_to_l3_agent(hosta_id,
router1['router']['id'])
def test_router_add_to_two_l3_agents(self):
with self.router() as router1:
self._register_agent_states()
hosta_id = self._get_agent_id(constants.AGENT_TYPE_L3,
L3_HOSTA)
hostb_id = self._get_agent_id(constants.AGENT_TYPE_L3,
L3_HOSTB)
self._add_router_to_l3_agent(hosta_id,
router1['router']['id'])
self._add_router_to_l3_agent(hostb_id,
router1['router']['id'],
expected_code=exc.HTTPConflict.code)
def test_router_policy(self):
with self.router() as router1:
self._register_agent_states()
hosta_id = self._get_agent_id(constants.AGENT_TYPE_L3,
L3_HOSTA)
self._list_routers_hosted_by_l3_agent(
hosta_id, expected_code=exc.HTTPForbidden.code,
admin_context=False)
self._add_router_to_l3_agent(
hosta_id, router1['router']['id'],
expected_code=exc.HTTPForbidden.code,
admin_context=False)
self._add_router_to_l3_agent(
hosta_id, router1['router']['id'])
self._remove_router_from_l3_agent(
hosta_id, router1['router']['id'],
expected_code=exc.HTTPForbidden.code,
admin_context=False)
self._list_l3_agents_hosting_router(
router1['router']['id'],
expected_code=exc.HTTPForbidden.code,
admin_context=False)
def _test_sync_routers_from_admin_state_down_agent(self, keep_services):
if keep_services:
cfg.CONF.set_override(
'enable_services_on_agents_with_admin_state_down', True)
l3_rpc_cb = l3_rpc.L3RpcCallback()
self._register_agent_states()
hosta_id = self._get_agent_id(constants.AGENT_TYPE_L3, L3_HOSTA)
with self.router() as router:
self._add_router_to_l3_agent(hosta_id,
router['router']['id'])
routers = l3_rpc_cb.sync_routers(self.adminContext, host=L3_HOSTA)
self.assertEqual(1, len(routers))
self._set_agent_admin_state_up(L3_HOSTA, False)
routers = l3_rpc_cb.sync_routers(self.adminContext, host=L3_HOSTA)
if keep_services:
self.assertEqual(1, len(routers))
else:
self.assertEqual(0, len(routers))
def test_l3_agent_keep_services_off(self):
self._test_sync_routers_from_admin_state_down_agent(False)
def test_l3_agent_keep_services_on(self):
self._test_sync_routers_from_admin_state_down_agent(True)
def test_list_routers_hosted_by_l3_agent_with_invalid_agent(self):
invalid_agentid = 'non_existing_agent'
self._list_routers_hosted_by_l3_agent(invalid_agentid,
exc.HTTPNotFound.code)
def test_list_networks_hosted_by_dhcp_agent_with_invalid_agent(self):
invalid_agentid = 'non_existing_agent'
self._list_networks_hosted_by_dhcp_agent(invalid_agentid,
exc.HTTPNotFound.code)
class OvsDhcpAgentNotifierTestCase(test_agent.AgentDBTestMixIn,
AgentSchedulerTestMixIn,
test_plugin.NeutronDbPluginV2TestCase):
def setUp(self):
super(OvsDhcpAgentNotifierTestCase, self).setUp('ml2')
mock.patch.object(
self.plugin, 'filter_hosts_with_network_access',
side_effect=lambda context, network_id, hosts: hosts).start()
plugin = directory.get_plugin()
self.dhcp_notifier = plugin.agent_notifiers[constants.AGENT_TYPE_DHCP]
self.dhcp_notifier_cast = mock.patch(
'neutron.api.rpc.agentnotifiers.dhcp_rpc_agent_api.'
'DhcpAgentNotifyAPI._cast_message').start()
ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr)
self.adminContext = context.get_admin_context()
fake_notifier.reset()
def test_network_add_to_dhcp_agent_notification(self):
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)
self.dhcp_notifier_cast.assert_called_with(
mock.ANY, 'network_create_end',
{'network': {'id': network_id},
'priority': dhcp_rpc_agent_api.PRIORITY_NETWORK_CREATE},
DHCP_HOSTA)
notifications = fake_notifier.NOTIFICATIONS
expected_event_type = 'dhcp_agent.network.add'
self._assert_notify(notifications, expected_event_type)
def test_network_remove_from_dhcp_agent_notification(self):
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)
self._remove_network_from_dhcp_agent(hosta_id,
network_id)
self.dhcp_notifier_cast.assert_called_with(
mock.ANY, 'network_delete_end',
{'network_id': network_id,
'priority': dhcp_rpc_agent_api.PRIORITY_NETWORK_DELETE},
DHCP_HOSTA)
notifications = fake_notifier.NOTIFICATIONS
expected_event_type = 'dhcp_agent.network.remove'
self._assert_notify(notifications, expected_event_type)
def test_agent_updated_dhcp_agent_notification(self):
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)
self.dhcp_notifier_cast.assert_called_with(
mock.ANY, 'agent_updated',
{'admin_state_up': False}, DHCP_HOSTA)
def _api_network_port_create(
self, hosts, gateway=constants.ATTR_NOT_SPECIFIED, owner=None):
for host in hosts:
helpers.register_dhcp_agent(host)
with self.network() as net1:
with self.subnet(network=net1,
gateway_ip=gateway) as subnet1:
if owner:
with self.port(subnet=subnet1,
device_owner=owner) as port:
return [net1, subnet1, port]
else:
with self.port(subnet=subnet1) as port:
return [net1, subnet1, port]
def _network_port_create(self, *args, **kwargs):
net, sub, port = self._api_network_port_create(*args, **kwargs)
dhcp_notifier = self.plugin.agent_notifiers[constants.AGENT_TYPE_DHCP]
if (not hasattr(dhcp_notifier, 'uses_native_notifications') or
not all(dhcp_notifier.uses_native_notifications[r]['create']
for r in ('port', 'subnet', 'network'))):
return net, sub, port
# since plugin has native dhcp notifications, the payloads will be the
# same as the getter outputs
ctx = context.get_admin_context()
net['network'] = self.plugin.get_network(ctx, net['network']['id'])
sub['subnet'] = self.plugin.get_subnet(ctx, sub['subnet']['id'])
sub['priority'] = dhcp_rpc_agent_api.PRIORITY_SUBNET_UPDATE
port['port'] = self.plugin.get_port(ctx, port['port']['id'])
return net, sub, port
def _notification_mocks(self, hosts, net, subnet, port, port_priority):
host_calls = {}
for host in hosts:
expected_calls = [
mock.call(
mock.ANY,
'network_create_end',
{'priority': dhcp_rpc_agent_api.PRIORITY_NETWORK_CREATE,
'network': {'id': net['network']['id']}},
host),
mock.call(
mock.ANY,
'subnet_create_end',
subnet,
host, 'dhcp_agent'),
mock.call(
mock.ANY,
'port_create_end',
{'port': port['port'],
'priority': port_priority},
host, 'dhcp_agent')]
host_calls[host] = expected_calls
return host_calls
def test_network_port_create_notification(self):
hosts = [DHCP_HOSTA]
net, subnet, port = self._network_port_create(hosts)
expected_calls = self._notification_mocks(
hosts, net, subnet, port,
dhcp_rpc_agent_api.PRIORITY_PORT_CREATE_HIGH)
self.assertEqual(
expected_calls[DHCP_HOSTA], self.dhcp_notifier_cast.call_args_list)
def test_network_ha_port_create_notification(self):
cfg.CONF.set_override('dhcp_agents_per_network', 3)
hosts = [DHCP_HOSTA, DHCP_HOSTC, DHCP_HOSTD]
net, subnet, port = self._network_port_create(hosts)
for host_call in self.dhcp_notifier_cast.call_args_list:
if ("'priority': " + str(
dhcp_rpc_agent_api.PRIORITY_PORT_CREATE_HIGH)
in str(host_call)):
if DHCP_HOSTA in str(host_call):
expected_high_calls = self._notification_mocks(
[DHCP_HOSTA], net, subnet, port,
dhcp_rpc_agent_api.PRIORITY_PORT_CREATE_HIGH)
high_host = DHCP_HOSTA
hosts.pop(0)
elif DHCP_HOSTC in str(host_call):
expected_high_calls = self._notification_mocks(
[DHCP_HOSTC], net, subnet, port,
dhcp_rpc_agent_api.PRIORITY_PORT_CREATE_HIGH)
high_host = DHCP_HOSTC
hosts.pop(1)
elif DHCP_HOSTD in str(host_call):
expected_high_calls = self._notification_mocks(
[DHCP_HOSTD], net, subnet, port,
dhcp_rpc_agent_api.PRIORITY_PORT_CREATE_HIGH)
high_host = DHCP_HOSTD
hosts.pop(2)
expected_low_calls = self._notification_mocks(
hosts, net, subnet, port,
dhcp_rpc_agent_api.PRIORITY_PORT_CREATE_LOW)
for expected in expected_high_calls[high_host]:
self.assertIn(expected, self.dhcp_notifier_cast.call_args_list)
for host, low_expecteds in expected_low_calls.items():
for expected in low_expecteds:
self.assertIn(expected, self.dhcp_notifier_cast.call_args_list)
def _test_auto_schedule_new_network_segments(self, subnet_on_segment):
ctx = mock.Mock()
payload = events.DBEventPayload(
ctx,
metadata={'host': 'HOST A',
'current_segment_ids': set(['segment-1'])})
segments_plugin = mock.Mock()
segments_plugin.get_segments.return_value = [
{'id': 'segment-1', 'hosts': ['HOST A']}]
dhcp_notifier = mock.Mock()
dhcp_mixin = agentschedulers_db.DhcpAgentSchedulerDbMixin()
with mock.patch(
'neutron_lib.plugins.directory.get_plugin',
return_value=segments_plugin), \
mock.patch(
'neutron.objects.subnet.Subnet.get_objects') as get_subnets, \
mock.patch.object(
dhcp_mixin, '_schedule_network') as schedule_network:
get_subnets.return_value = (
[subnet_on_segment] if subnet_on_segment else [])
dhcp_mixin.agent_notifiers[constants.AGENT_TYPE_DHCP] = (
dhcp_notifier)
dhcp_mixin.auto_schedule_new_network_segments(
resources.SEGMENT_HOST_MAPPING, events.AFTER_CREATE,
ctx, payload)
if subnet_on_segment:
schedule_network.assert_called_once_with(
ctx, subnet_on_segment.network_id,
dhcp_notifier, candidate_hosts=['HOST A'])
else:
schedule_network.assert_not_called()
def test_auto_schedule_new_network_segments(self):
self._test_auto_schedule_new_network_segments(
subnet_on_segment=mock.Mock(network_id='net-1'))
def test_auto_schedule_new_network_segments_no_networks_on_segment(self):
self._test_auto_schedule_new_network_segments(subnet_on_segment=None)
def _is_schedule_network_called(self, device_id):
dhcp_notifier_schedule = mock.patch(
'neutron.api.rpc.agentnotifiers.dhcp_rpc_agent_api.'
'DhcpAgentNotifyAPI._schedule_network').start()
plugin = directory.get_plugin()
with self.subnet() as subnet,\
self.port(subnet=subnet, device_id=device_id),\
mock.patch.object(plugin,
'get_dhcp_agents_hosting_networks',
return_value=[]):
return dhcp_notifier_schedule.call_count > 1
def test_reserved_dhcp_port_creation(self):
device_id = constants.DEVICE_ID_RESERVED_DHCP_PORT
self.assertFalse(self._is_schedule_network_called(device_id))
def test_unreserved_dhcp_port_creation(self):
device_id = 'not_reserved'
self.assertTrue(self._is_schedule_network_called(device_id))
class OvsL3AgentNotifierTestCase(test_l3.L3NatTestCaseMixin,
test_agent.AgentDBTestMixIn,
AgentSchedulerTestMixIn,
test_plugin.NeutronDbPluginV2TestCase):
l3_plugin = ('neutron.tests.unit.extensions.test_l3.'
'TestL3NatAgentSchedulingServicePlugin')
def setUp(self):
self.dhcp_notifier_cls_p = mock.patch(
'neutron.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
if self.l3_plugin:
service_plugins = {
'l3_plugin_name': self.l3_plugin,
'flavors_plugin_name': 'neutron.services.flavors.'
'flavors_plugin.FlavorsPlugin'
}
else:
service_plugins = None
super(OvsL3AgentNotifierTestCase, self).setUp(
'ml2', service_plugins=service_plugins)
ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr)
self.adminContext = context.get_admin_context()
fake_notifier.reset()
def test_router_add_to_l3_agent_notification(self):
l3_plugin = directory.get_plugin(plugin_constants.L3)
l3_notifier = l3_plugin.agent_notifiers[constants.AGENT_TYPE_L3]
with mock.patch.object(
l3_notifier.client,
'prepare',
return_value=l3_notifier.client) as mock_prepare,\
mock.patch.object(l3_notifier.client, 'call') as mock_call,\
self.router() as router1:
self._register_agent_states()
hosta_id = self._get_agent_id(constants.AGENT_TYPE_L3,
L3_HOSTA)
self._add_router_to_l3_agent(hosta_id,
router1['router']['id'])
routers = [router1['router']['id']]
mock_prepare.assert_called_with(server='hosta')
mock_call.assert_called_with(
mock.ANY, 'router_added_to_agent', payload=routers)
notifications = fake_notifier.NOTIFICATIONS
expected_event_type = 'l3_agent.router.add'
self._assert_notify(notifications, expected_event_type)
def test_router_remove_from_l3_agent_notification(self):
l3_plugin = directory.get_plugin(plugin_constants.L3)
l3_notifier = l3_plugin.agent_notifiers[constants.AGENT_TYPE_L3]
with mock.patch.object(
l3_notifier.client,
'prepare',
return_value=l3_notifier.client) as mock_prepare,\
mock.patch.object(l3_notifier.client, 'cast') as mock_cast,\
mock.patch.object(l3_notifier.client, 'call'),\
self.router() as router1:
self._register_agent_states()
hosta_id = self._get_agent_id(constants.AGENT_TYPE_L3,
L3_HOSTA)
self._add_router_to_l3_agent(hosta_id,
router1['router']['id'])
self._remove_router_from_l3_agent(hosta_id,
router1['router']['id'])
mock_prepare.assert_called_with(server='hosta')
mock_cast.assert_called_with(
mock.ANY, 'router_removed_from_agent',
payload={'router_id': router1['router']['id']})
notifications = fake_notifier.NOTIFICATIONS
expected_event_type = 'l3_agent.router.remove'
self._assert_notify(notifications, expected_event_type)
def test_agent_updated_l3_agent_notification(self):
l3_plugin = directory.get_plugin(plugin_constants.L3)
l3_notifier = l3_plugin.agent_notifiers[constants.AGENT_TYPE_L3]
with mock.patch.object(
l3_notifier.client,
'prepare',
return_value=l3_notifier.client) as mock_prepare,\
mock.patch.object(l3_notifier.client, 'cast') as mock_cast:
agent_id = helpers.register_l3_agent(L3_HOSTA).id
self._disable_agent(agent_id, admin_state_up=False)
mock_prepare.assert_called_with(server=L3_HOSTA)
mock_cast.assert_called_with(
mock.ANY, 'agent_updated', payload={'admin_state_up': False})