670cc383e0
As part of the Secure RBAC community goal, we should switch options "enforce_new_defaults" and "enforce_scope" to be True by default. It will be still possible to fallback to old policy rules by configuring those config options to False in Neutron config. Change-Id: I09c0026ccf87e6c0bb1fa59165c03dc508fba6fa
1780 lines
84 KiB
Python
1780 lines
84 KiB
Python
# 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 copy
|
|
import datetime
|
|
from unittest 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.db import api as db_api
|
|
from neutron_lib.plugins import constants as plugin_constants
|
|
from neutron_lib.plugins import directory
|
|
from neutron_lib import rpc as n_rpc
|
|
from neutron_lib.tests.unit import fake_notifier
|
|
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 import policy
|
|
from neutron.tests.common import helpers
|
|
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,
|
|
req_tenant_id=None):
|
|
content_type = 'application/%s' % self.fmt
|
|
body = None
|
|
if data is not None: # empty dict is valid
|
|
body = wsgi.Serializer().serialize(data, content_type)
|
|
roles = ['member', 'reader']
|
|
req_tenant_id = req_tenant_id or self._tenant_id
|
|
if admin_context:
|
|
roles.append('admin')
|
|
req = testlib_api.create_request(
|
|
path, body, content_type, method, query_string=query_string)
|
|
req.environ['neutron.context'] = context.Context(
|
|
'', req_tenant_id, roles=roles, is_admin=admin_context)
|
|
return req
|
|
|
|
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, as_admin=True)
|
|
|
|
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.object(
|
|
n_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()
|
|
policy.init()
|
|
|
|
|
|
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):
|
|
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):
|
|
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)
|
|
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
|
|
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()
|
|
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
|
|
with db_api.CONTEXT_WRITER.using(self.adminContext):
|
|
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))
|
|
|
|
plugin = directory.get_plugin(plugin_constants.L3)
|
|
plugin.reschedule_routers_from_down_agents()
|
|
|
|
def _set_agent_admin_state_up(self, host, state):
|
|
with db_api.CONTEXT_WRITER.using(self.adminContext):
|
|
query = self.adminContext.session.query(agent_model.Agent)
|
|
agt_db = query.filter_by(host=host).first()
|
|
agt_db.admin_state_up = state
|
|
|
|
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):
|
|
project_id = uuidutils.generate_uuid()
|
|
with self.router(project_id=project_id) as router:
|
|
l3_rpc_cb = l3_rpc.L3RpcCallback()
|
|
self._register_agent_states()
|
|
self._delete('routers', router['router']['id'],
|
|
tenant_id=project_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 = {'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'])
|
|
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=[r['id']])
|
|
|
|
router_ids = [r['id'] for r in ret_b]
|
|
self.assertEqual(0, len(router_ids))
|
|
|
|
def test_router_without_l3_agents(self):
|
|
project_id = uuidutils.generate_uuid()
|
|
with self.subnet() as s:
|
|
self._set_net_external(s['subnet']['network_id'])
|
|
data = {'router': {'tenant_id': project_id}}
|
|
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, tenant_id=project_id)
|
|
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'], tenant_id=project_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):
|
|
project_id = uuidutils.generate_uuid()
|
|
with self.subnet(project_id=project_id) as s1,\
|
|
self.subnet(project_id=project_id, cidr='10.0.2.0/24') as s2,\
|
|
self.subnet(project_id=project_id, cidr='10.0.3.0/24') as s3:
|
|
self._register_agent_states()
|
|
self._set_net_external(s1['subnet']['network_id'])
|
|
data = {'router': {'tenant_id': project_id}}
|
|
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, tenant_id=project_id)
|
|
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,
|
|
tenant_id=project_id)
|
|
self._router_interface_action('add',
|
|
router['router']['id'],
|
|
s3['subnet']['id'],
|
|
None,
|
|
tenant_id=project_id)
|
|
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,
|
|
tenant_id=project_id)
|
|
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,
|
|
tenant_id=project_id)
|
|
self._delete('routers', router['router']['id'],
|
|
tenant_id=project_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)
|
|
|
|
def test_network_no_reschedule(self):
|
|
cfg.CONF.set_override('network_auto_schedule', False)
|
|
with self.subnet() as sb1, self.subnet():
|
|
network1_id = sb1['subnet']['network_id']
|
|
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(0, num_hosta_nets)
|
|
self.assertEqual(0, num_hostc_nets)
|
|
# After this patch, network which requires DHCP
|
|
# has to be manually mapped
|
|
self._add_network_to_dhcp_agent(hosta_id,
|
|
network1_id)
|
|
networks = self._list_networks_hosted_by_dhcp_agent(hosta_id)
|
|
num_hosta_nets = len(networks['networks'])
|
|
self.assertEqual(1, num_hosta_nets)
|
|
|
|
|
|
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)
|
|
# Call it second time, it should be already deleted so should 409 be
|
|
# returned this time
|
|
self._remove_network_from_dhcp_agent(
|
|
hosta_id, network_id,
|
|
expected_code=exc.HTTPConflict.code)
|
|
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):
|
|
subnet['subnet']['network'] = copy.deepcopy(net['network'])
|
|
# 'availability_zones' is empty at the time subnet_create_end
|
|
# notification is sent
|
|
subnet['subnet']['network']['availability_zones'] = []
|
|
port['port']['network'] = net['network']
|
|
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', 'segment-2', 'segment-3'])})
|
|
segments_plugin = mock.Mock()
|
|
segments_plugin.get_segments.return_value = [
|
|
{'id': 'segment-1', 'hosts': ['HOST A'],
|
|
'network_id': 'net-1'},
|
|
{'id': 'segment-2', 'hosts': ['HOST A', 'HOST B'],
|
|
'network_id': 'net-1'},
|
|
{'id': 'segment-3', 'hosts': ['HOST A', 'HOST C'],
|
|
'network_id': 'net-2'}]
|
|
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:
|
|
self.assertEqual(schedule_network.mock_calls, [
|
|
mock.call(
|
|
ctx, subnet_on_segment.network_id,
|
|
dhcp_notifier, candidate_hosts=['HOST A']),
|
|
mock.call(
|
|
ctx, subnet_on_segment.network_id,
|
|
dhcp_notifier, candidate_hosts=['HOST A', 'HOST B'])
|
|
])
|
|
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})
|