af020da544
Logical Service Nodes (LSN) are those that provide dhcp and metadata services to networks when the NSX plugin is used without the openstack dhcp and metadata proxy agents. External networks do not work with Neutron's dhcp and metadata services, therefore there is no need to provision a LSN in this case. Added UT to avoid regression. Closes-bug: #1316795 Change-Id: I3dab0c9cbecdd9296466c991d02d590d6bd5bd49
1420 lines
61 KiB
Python
1420 lines
61 KiB
Python
# Copyright 2013 VMware, Inc.
|
|
#
|
|
# 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 mock
|
|
|
|
from oslo.config import cfg
|
|
|
|
from neutron.common import constants as n_consts
|
|
from neutron.common import exceptions as n_exc
|
|
from neutron import context
|
|
from neutron.db import api as db
|
|
from neutron.plugins.vmware.api_client import exception
|
|
from neutron.plugins.vmware.common import exceptions as p_exc
|
|
from neutron.plugins.vmware.dbexts import lsn_db
|
|
from neutron.plugins.vmware.dhcp_meta import constants
|
|
from neutron.plugins.vmware.dhcp_meta import lsnmanager as lsn_man
|
|
from neutron.plugins.vmware.dhcp_meta import migration as mig_man
|
|
from neutron.plugins.vmware.dhcp_meta import nsx
|
|
from neutron.plugins.vmware.dhcp_meta import rpc
|
|
from neutron.tests import base
|
|
|
|
|
|
class DhcpMetadataBuilderTestCase(base.BaseTestCase):
|
|
|
|
def setUp(self):
|
|
super(DhcpMetadataBuilderTestCase, self).setUp()
|
|
self.builder = mig_man.DhcpMetadataBuilder(mock.Mock(), mock.Mock())
|
|
self.network_id = 'foo_network_id'
|
|
self.subnet_id = 'foo_subnet_id'
|
|
self.router_id = 'foo_router_id'
|
|
|
|
def test_dhcp_agent_get_all(self):
|
|
expected = []
|
|
self.builder.plugin.list_dhcp_agents_hosting_network.return_value = (
|
|
{'agents': expected})
|
|
agents = self.builder.dhcp_agent_get_all(mock.ANY, self.network_id)
|
|
self.assertEqual(expected, agents)
|
|
|
|
def test_dhcp_port_get_all(self):
|
|
expected = []
|
|
self.builder.plugin.get_ports.return_value = expected
|
|
ports = self.builder.dhcp_port_get_all(mock.ANY, self.network_id)
|
|
self.assertEqual(expected, ports)
|
|
|
|
def test_router_id_get(self):
|
|
port = {
|
|
'device_id': self.router_id,
|
|
'network_id': self.network_id,
|
|
'fixed_ips': [{'subnet_id': self.subnet_id}]
|
|
}
|
|
subnet = {
|
|
'id': self.subnet_id,
|
|
'network_id': self.network_id
|
|
}
|
|
self.builder.plugin.get_ports.return_value = [port]
|
|
result = self.builder.router_id_get(context, subnet)
|
|
self.assertEqual(self.router_id, result)
|
|
|
|
def test_router_id_get_none_subnet(self):
|
|
self.assertIsNone(self.builder.router_id_get(mock.ANY, None))
|
|
|
|
def test_metadata_deallocate(self):
|
|
self.builder.metadata_deallocate(
|
|
mock.ANY, self.router_id, self.subnet_id)
|
|
self.assertTrue(self.builder.plugin.remove_router_interface.call_count)
|
|
|
|
def test_metadata_allocate(self):
|
|
self.builder.metadata_allocate(
|
|
mock.ANY, self.router_id, self.subnet_id)
|
|
self.assertTrue(self.builder.plugin.add_router_interface.call_count)
|
|
|
|
def test_dhcp_deallocate(self):
|
|
agents = [{'id': 'foo_agent_id'}]
|
|
ports = [{'id': 'foo_port_id'}]
|
|
self.builder.dhcp_deallocate(mock.ANY, self.network_id, agents, ports)
|
|
self.assertTrue(
|
|
self.builder.plugin.remove_network_from_dhcp_agent.call_count)
|
|
self.assertTrue(self.builder.plugin.delete_port.call_count)
|
|
|
|
def _test_dhcp_allocate(self, subnet, expected_notify_count):
|
|
with mock.patch.object(mig_man.nsx, 'handle_network_dhcp_access') as f:
|
|
self.builder.dhcp_allocate(mock.ANY, self.network_id, subnet)
|
|
self.assertTrue(f.call_count)
|
|
self.assertEqual(expected_notify_count,
|
|
self.builder.notifier.notify.call_count)
|
|
|
|
def test_dhcp_allocate(self):
|
|
subnet = {'network_id': self.network_id, 'id': self.subnet_id}
|
|
self._test_dhcp_allocate(subnet, 2)
|
|
|
|
def test_dhcp_allocate_none_subnet(self):
|
|
self._test_dhcp_allocate(None, 0)
|
|
|
|
|
|
class MigrationManagerTestCase(base.BaseTestCase):
|
|
|
|
def setUp(self):
|
|
super(MigrationManagerTestCase, self).setUp()
|
|
self.manager = mig_man.MigrationManager(mock.Mock(),
|
|
mock.Mock(),
|
|
mock.Mock())
|
|
self.network_id = 'foo_network_id'
|
|
self.router_id = 'foo_router_id'
|
|
self.subnet_id = 'foo_subnet_id'
|
|
self.mock_builder_p = mock.patch.object(self.manager, 'builder')
|
|
self.mock_builder = self.mock_builder_p.start()
|
|
|
|
def _test_validate(self, lsn_exists=False, ext_net=False, subnets=None):
|
|
network = {'router:external': ext_net}
|
|
self.manager.manager.lsn_exists.return_value = lsn_exists
|
|
self.manager.plugin.get_network.return_value = network
|
|
self.manager.plugin.get_subnets.return_value = subnets
|
|
result = self.manager.validate(mock.ANY, self.network_id)
|
|
if len(subnets):
|
|
self.assertEqual(subnets[0], result)
|
|
else:
|
|
self.assertIsNone(result)
|
|
|
|
def test_validate_no_subnets(self):
|
|
self._test_validate(subnets=[])
|
|
|
|
def test_validate_with_one_subnet(self):
|
|
self._test_validate(subnets=[{'cidr': '0.0.0.0/0'}])
|
|
|
|
def test_validate_raise_conflict_many_subnets(self):
|
|
self.assertRaises(p_exc.LsnMigrationConflict,
|
|
self._test_validate,
|
|
subnets=[{'id': 'sub1'}, {'id': 'sub2'}])
|
|
|
|
def test_validate_raise_conflict_lsn_exists(self):
|
|
self.assertRaises(p_exc.LsnMigrationConflict,
|
|
self._test_validate,
|
|
lsn_exists=True)
|
|
|
|
def test_validate_raise_badrequest_external_net(self):
|
|
self.assertRaises(n_exc.BadRequest,
|
|
self._test_validate,
|
|
ext_net=True)
|
|
|
|
def test_validate_raise_badrequest_metadata_net(self):
|
|
self.assertRaises(n_exc.BadRequest,
|
|
self._test_validate,
|
|
ext_net=False,
|
|
subnets=[{'cidr': rpc.METADATA_SUBNET_CIDR}])
|
|
|
|
def _test_migrate(self, router, subnet, expected_calls):
|
|
self.mock_builder.router_id_get.return_value = router
|
|
self.manager.migrate(mock.ANY, self.network_id, subnet)
|
|
# testing the exact the order of calls is important
|
|
self.assertEqual(expected_calls, self.mock_builder.mock_calls)
|
|
|
|
def test_migrate(self):
|
|
subnet = {
|
|
'id': self.subnet_id,
|
|
'network_id': self.network_id
|
|
}
|
|
call_sequence = [
|
|
mock.call.router_id_get(mock.ANY, subnet),
|
|
mock.call.metadata_deallocate(
|
|
mock.ANY, self.router_id, self.subnet_id),
|
|
mock.call.dhcp_agent_get_all(mock.ANY, self.network_id),
|
|
mock.call.dhcp_port_get_all(mock.ANY, self.network_id),
|
|
mock.call.dhcp_deallocate(
|
|
mock.ANY, self.network_id, mock.ANY, mock.ANY),
|
|
mock.call.dhcp_allocate(mock.ANY, self.network_id, subnet),
|
|
mock.call.metadata_allocate(
|
|
mock.ANY, self.router_id, self.subnet_id)
|
|
]
|
|
self._test_migrate(self.router_id, subnet, call_sequence)
|
|
|
|
def test_migrate_no_router_uplink(self):
|
|
subnet = {
|
|
'id': self.subnet_id,
|
|
'network_id': self.network_id
|
|
}
|
|
call_sequence = [
|
|
mock.call.router_id_get(mock.ANY, subnet),
|
|
mock.call.dhcp_agent_get_all(mock.ANY, self.network_id),
|
|
mock.call.dhcp_port_get_all(mock.ANY, self.network_id),
|
|
mock.call.dhcp_deallocate(
|
|
mock.ANY, self.network_id, mock.ANY, mock.ANY),
|
|
mock.call.dhcp_allocate(mock.ANY, self.network_id, subnet),
|
|
]
|
|
self._test_migrate(None, subnet, call_sequence)
|
|
|
|
def test_migrate_no_subnet(self):
|
|
call_sequence = [
|
|
mock.call.router_id_get(mock.ANY, None),
|
|
mock.call.dhcp_allocate(mock.ANY, self.network_id, None),
|
|
]
|
|
self._test_migrate(None, None, call_sequence)
|
|
|
|
def _test_report(self, lsn_attrs, expected):
|
|
self.manager.manager.lsn_port_get.return_value = lsn_attrs
|
|
report = self.manager.report(mock.ANY, self.network_id, self.subnet_id)
|
|
self.assertEqual(expected, report)
|
|
|
|
def test_report_for_lsn(self):
|
|
self._test_report(('foo_lsn_id', 'foo_lsn_port_id'),
|
|
{'ports': ['foo_lsn_port_id'],
|
|
'services': ['foo_lsn_id'], 'type': 'lsn'})
|
|
|
|
def test_report_for_lsn_without_lsn_port(self):
|
|
self._test_report(('foo_lsn_id', None),
|
|
{'ports': [],
|
|
'services': ['foo_lsn_id'], 'type': 'lsn'})
|
|
|
|
def _test_report_for_lsn_without_subnet(self, validated_subnet):
|
|
with mock.patch.object(self.manager.plugin, 'get_subnets',
|
|
return_value=validated_subnet):
|
|
self.manager.manager.lsn_port_get.return_value = (
|
|
('foo_lsn_id', 'foo_lsn_port_id'))
|
|
report = self.manager.report(context, self.network_id)
|
|
expected = {
|
|
'ports': ['foo_lsn_port_id'] if validated_subnet else [],
|
|
'services': ['foo_lsn_id'], 'type': 'lsn'
|
|
}
|
|
self.assertEqual(expected, report)
|
|
|
|
def test_report_for_lsn_without_subnet_subnet_found(self):
|
|
self._test_report_for_lsn_without_subnet([{'id': self.subnet_id}])
|
|
|
|
def test_report_for_lsn_without_subnet_subnet_not_found(self):
|
|
self.manager.manager.lsn_get.return_value = 'foo_lsn_id'
|
|
self._test_report_for_lsn_without_subnet(None)
|
|
|
|
def test_report_for_dhcp_agent(self):
|
|
self.manager.manager.lsn_port_get.return_value = (None, None)
|
|
self.mock_builder.dhcp_agent_get_all.return_value = (
|
|
[{'id': 'foo_agent_id'}])
|
|
self.mock_builder.dhcp_port_get_all.return_value = (
|
|
[{'id': 'foo_dhcp_port_id'}])
|
|
result = self.manager.report(mock.ANY, self.network_id, self.subnet_id)
|
|
expected = {
|
|
'ports': ['foo_dhcp_port_id'],
|
|
'services': ['foo_agent_id'],
|
|
'type': 'agent'
|
|
}
|
|
self.assertEqual(expected, result)
|
|
|
|
|
|
class LsnManagerTestCase(base.BaseTestCase):
|
|
|
|
def setUp(self):
|
|
super(LsnManagerTestCase, self).setUp()
|
|
self.net_id = 'foo_network_id'
|
|
self.sub_id = 'foo_subnet_id'
|
|
self.port_id = 'foo_port_id'
|
|
self.lsn_id = 'foo_lsn_id'
|
|
self.mac = 'aa:bb:cc:dd:ee:ff'
|
|
self.switch_id = 'foo_switch_id'
|
|
self.lsn_port_id = 'foo_lsn_port_id'
|
|
self.tenant_id = 'foo_tenant_id'
|
|
self.manager = lsn_man.LsnManager(mock.Mock())
|
|
self.mock_lsn_api_p = mock.patch.object(lsn_man, 'lsn_api')
|
|
self.mock_lsn_api = self.mock_lsn_api_p.start()
|
|
self.mock_nsx_utils_p = mock.patch.object(lsn_man, 'nsx_utils')
|
|
self.mock_nsx_utils = self.mock_nsx_utils_p.start()
|
|
nsx.register_dhcp_opts(cfg)
|
|
nsx.register_metadata_opts(cfg)
|
|
|
|
def test_lsn_get(self):
|
|
self.mock_lsn_api.lsn_for_network_get.return_value = self.lsn_id
|
|
expected = self.manager.lsn_get(mock.ANY, self.net_id)
|
|
self.mock_lsn_api.lsn_for_network_get.assert_called_once_with(
|
|
mock.ANY, self.net_id)
|
|
self.assertEqual(expected, self.lsn_id)
|
|
|
|
def _test_lsn_get_raise_not_found_with_exc(self, exc):
|
|
self.mock_lsn_api.lsn_for_network_get.side_effect = exc
|
|
self.assertRaises(p_exc.LsnNotFound,
|
|
self.manager.lsn_get,
|
|
mock.ANY, self.net_id)
|
|
self.mock_lsn_api.lsn_for_network_get.assert_called_once_with(
|
|
mock.ANY, self.net_id)
|
|
|
|
def test_lsn_get_raise_not_found_with_not_found(self):
|
|
self._test_lsn_get_raise_not_found_with_exc(n_exc.NotFound)
|
|
|
|
def test_lsn_get_raise_not_found_with_api_error(self):
|
|
self._test_lsn_get_raise_not_found_with_exc(exception.NsxApiException)
|
|
|
|
def _test_lsn_get_silent_raise_with_exc(self, exc):
|
|
self.mock_lsn_api.lsn_for_network_get.side_effect = exc
|
|
expected = self.manager.lsn_get(
|
|
mock.ANY, self.net_id, raise_on_err=False)
|
|
self.mock_lsn_api.lsn_for_network_get.assert_called_once_with(
|
|
mock.ANY, self.net_id)
|
|
self.assertIsNone(expected)
|
|
|
|
def test_lsn_get_silent_raise_with_not_found(self):
|
|
self._test_lsn_get_silent_raise_with_exc(n_exc.NotFound)
|
|
|
|
def test_lsn_get_silent_raise_with_api_error(self):
|
|
self._test_lsn_get_silent_raise_with_exc(exception.NsxApiException)
|
|
|
|
def test_lsn_create(self):
|
|
self.mock_lsn_api.lsn_for_network_create.return_value = self.lsn_id
|
|
self.manager.lsn_create(mock.ANY, self.net_id)
|
|
self.mock_lsn_api.lsn_for_network_create.assert_called_once_with(
|
|
mock.ANY, self.net_id)
|
|
|
|
def test_lsn_create_raise_api_error(self):
|
|
self.mock_lsn_api.lsn_for_network_create.side_effect = (
|
|
exception.NsxApiException)
|
|
self.assertRaises(p_exc.NsxPluginException,
|
|
self.manager.lsn_create,
|
|
mock.ANY, self.net_id)
|
|
self.mock_lsn_api.lsn_for_network_create.assert_called_once_with(
|
|
mock.ANY, self.net_id)
|
|
|
|
def test_lsn_delete(self):
|
|
self.manager.lsn_delete(mock.ANY, self.lsn_id)
|
|
self.mock_lsn_api.lsn_delete.assert_called_once_with(
|
|
mock.ANY, self.lsn_id)
|
|
|
|
def _test_lsn_delete_with_exc(self, exc):
|
|
self.mock_lsn_api.lsn_delete.side_effect = exc
|
|
self.manager.lsn_delete(mock.ANY, self.lsn_id)
|
|
self.mock_lsn_api.lsn_delete.assert_called_once_with(
|
|
mock.ANY, self.lsn_id)
|
|
|
|
def test_lsn_delete_with_not_found(self):
|
|
self._test_lsn_delete_with_exc(n_exc.NotFound)
|
|
|
|
def test_lsn_delete_api_exception(self):
|
|
self._test_lsn_delete_with_exc(exception.NsxApiException)
|
|
|
|
def test_lsn_delete_by_network(self):
|
|
self.mock_lsn_api.lsn_for_network_get.return_value = self.lsn_id
|
|
with mock.patch.object(self.manager, 'lsn_delete') as f:
|
|
self.manager.lsn_delete_by_network(mock.ANY, self.net_id)
|
|
self.mock_lsn_api.lsn_for_network_get.assert_called_once_with(
|
|
mock.ANY, self.net_id)
|
|
f.assert_called_once_with(mock.ANY, self.lsn_id)
|
|
|
|
def _test_lsn_delete_by_network_with_exc(self, exc):
|
|
self.mock_lsn_api.lsn_for_network_get.side_effect = exc
|
|
with mock.patch.object(lsn_man.LOG, 'warn') as l:
|
|
self.manager.lsn_delete_by_network(mock.ANY, self.net_id)
|
|
self.assertEqual(1, l.call_count)
|
|
|
|
def test_lsn_delete_by_network_with_not_found(self):
|
|
self._test_lsn_delete_by_network_with_exc(n_exc.NotFound)
|
|
|
|
def test_lsn_delete_by_network_with_not_api_error(self):
|
|
self._test_lsn_delete_by_network_with_exc(exception.NsxApiException)
|
|
|
|
def test_lsn_port_get(self):
|
|
self.mock_lsn_api.lsn_port_by_subnet_get.return_value = (
|
|
self.lsn_port_id)
|
|
with mock.patch.object(
|
|
self.manager, 'lsn_get', return_value=self.lsn_id):
|
|
expected = self.manager.lsn_port_get(
|
|
mock.ANY, self.net_id, self.sub_id)
|
|
self.assertEqual(expected, (self.lsn_id, self.lsn_port_id))
|
|
|
|
def test_lsn_port_get_lsn_not_found_on_raise(self):
|
|
with mock.patch.object(
|
|
self.manager, 'lsn_get',
|
|
side_effect=p_exc.LsnNotFound(entity='network',
|
|
entity_id=self.net_id)):
|
|
self.assertRaises(p_exc.LsnNotFound,
|
|
self.manager.lsn_port_get,
|
|
mock.ANY, self.net_id, self.sub_id)
|
|
|
|
def test_lsn_port_get_lsn_not_found_silent_raise(self):
|
|
with mock.patch.object(self.manager, 'lsn_get', return_value=None):
|
|
expected = self.manager.lsn_port_get(
|
|
mock.ANY, self.net_id, self.sub_id, raise_on_err=False)
|
|
self.assertEqual(expected, (None, None))
|
|
|
|
def test_lsn_port_get_port_not_found_on_raise(self):
|
|
self.mock_lsn_api.lsn_port_by_subnet_get.side_effect = n_exc.NotFound
|
|
with mock.patch.object(
|
|
self.manager, 'lsn_get', return_value=self.lsn_id):
|
|
self.assertRaises(p_exc.LsnPortNotFound,
|
|
self.manager.lsn_port_get,
|
|
mock.ANY, self.net_id, self.sub_id)
|
|
|
|
def test_lsn_port_get_port_not_found_silent_raise(self):
|
|
self.mock_lsn_api.lsn_port_by_subnet_get.side_effect = n_exc.NotFound
|
|
with mock.patch.object(
|
|
self.manager, 'lsn_get', return_value=self.lsn_id):
|
|
expected = self.manager.lsn_port_get(
|
|
mock.ANY, self.net_id, self.sub_id, raise_on_err=False)
|
|
self.assertEqual(expected, (self.lsn_id, None))
|
|
|
|
def test_lsn_port_create(self):
|
|
self.mock_lsn_api.lsn_port_create.return_value = self.lsn_port_id
|
|
expected = self.manager.lsn_port_create(mock.ANY, mock.ANY, mock.ANY)
|
|
self.assertEqual(expected, self.lsn_port_id)
|
|
|
|
def _test_lsn_port_create_with_exc(self, exc, expected):
|
|
self.mock_lsn_api.lsn_port_create.side_effect = exc
|
|
self.assertRaises(expected,
|
|
self.manager.lsn_port_create,
|
|
mock.ANY, mock.ANY, mock.ANY)
|
|
|
|
def test_lsn_port_create_with_not_found(self):
|
|
self._test_lsn_port_create_with_exc(n_exc.NotFound, p_exc.LsnNotFound)
|
|
|
|
def test_lsn_port_create_api_exception(self):
|
|
self._test_lsn_port_create_with_exc(exception.NsxApiException,
|
|
p_exc.NsxPluginException)
|
|
|
|
def test_lsn_port_delete(self):
|
|
self.manager.lsn_port_delete(mock.ANY, mock.ANY, mock.ANY)
|
|
self.assertEqual(1, self.mock_lsn_api.lsn_port_delete.call_count)
|
|
|
|
def _test_lsn_port_delete_with_exc(self, exc):
|
|
self.mock_lsn_api.lsn_port_delete.side_effect = exc
|
|
with mock.patch.object(lsn_man.LOG, 'warn') as l:
|
|
self.manager.lsn_port_delete(mock.ANY, mock.ANY, mock.ANY)
|
|
self.assertEqual(1, self.mock_lsn_api.lsn_port_delete.call_count)
|
|
self.assertEqual(1, l.call_count)
|
|
|
|
def test_lsn_port_delete_with_not_found(self):
|
|
self._test_lsn_port_delete_with_exc(n_exc.NotFound)
|
|
|
|
def test_lsn_port_delete_api_exception(self):
|
|
self._test_lsn_port_delete_with_exc(exception.NsxApiException)
|
|
|
|
def _test_lsn_port_dhcp_setup(self, ret_val, sub):
|
|
self.mock_nsx_utils.get_nsx_switch_ids.return_value = [self.switch_id]
|
|
self.mock_lsn_api.lsn_port_create.return_value = self.lsn_port_id
|
|
with mock.patch.object(
|
|
self.manager, 'lsn_get', return_value=self.lsn_id):
|
|
with mock.patch.object(lsn_man.switch_api,
|
|
'get_port_by_neutron_tag'):
|
|
expected = self.manager.lsn_port_dhcp_setup(
|
|
mock.Mock(), mock.ANY, mock.ANY,
|
|
mock.ANY, subnet_config=sub)
|
|
self.assertEqual(
|
|
1, self.mock_lsn_api.lsn_port_create.call_count)
|
|
self.assertEqual(
|
|
1, self.mock_lsn_api.lsn_port_plug_network.call_count)
|
|
self.assertEqual(expected, ret_val)
|
|
|
|
def test_lsn_port_dhcp_setup(self):
|
|
self._test_lsn_port_dhcp_setup((self.lsn_id, self.lsn_port_id), None)
|
|
|
|
def test_lsn_port_dhcp_setup_with_config(self):
|
|
with mock.patch.object(self.manager, 'lsn_port_dhcp_configure') as f:
|
|
self._test_lsn_port_dhcp_setup(None, mock.ANY)
|
|
self.assertEqual(1, f.call_count)
|
|
|
|
def test_lsn_port_dhcp_setup_with_not_found(self):
|
|
self.mock_nsx_utils.get_nsx_switch_ids.return_value = [self.switch_id]
|
|
with mock.patch.object(lsn_man.switch_api,
|
|
'get_port_by_neutron_tag') as f:
|
|
f.side_effect = n_exc.NotFound
|
|
self.assertRaises(p_exc.PortConfigurationError,
|
|
self.manager.lsn_port_dhcp_setup,
|
|
mock.Mock(), mock.ANY, mock.ANY, mock.ANY)
|
|
|
|
def test_lsn_port_dhcp_setup_with_conflict(self):
|
|
self.mock_lsn_api.lsn_port_plug_network.side_effect = (
|
|
p_exc.LsnConfigurationConflict(lsn_id=self.lsn_id))
|
|
self.mock_nsx_utils.get_nsx_switch_ids.return_value = [self.switch_id]
|
|
with mock.patch.object(lsn_man.switch_api, 'get_port_by_neutron_tag'):
|
|
with mock.patch.object(self.manager, 'lsn_port_delete') as g:
|
|
self.assertRaises(p_exc.PortConfigurationError,
|
|
self.manager.lsn_port_dhcp_setup,
|
|
mock.Mock(), mock.ANY, mock.ANY, mock.ANY)
|
|
self.assertEqual(1, g.call_count)
|
|
|
|
def _test_lsn_port_dhcp_configure_with_subnet(
|
|
self, expected, dns=None, gw=None, routes=None):
|
|
subnet = {
|
|
'enable_dhcp': True,
|
|
'dns_nameservers': dns or [],
|
|
'gateway_ip': gw,
|
|
'host_routes': routes
|
|
}
|
|
self.manager.lsn_port_dhcp_configure(mock.ANY, self.lsn_id,
|
|
self.lsn_port_id, subnet)
|
|
self.mock_lsn_api.lsn_port_dhcp_configure.assert_called_once_with(
|
|
mock.ANY, self.lsn_id, self.lsn_port_id, subnet['enable_dhcp'],
|
|
expected)
|
|
|
|
def test_lsn_port_dhcp_configure(self):
|
|
expected = {
|
|
'routers': '127.0.0.1',
|
|
'default_lease_time': cfg.CONF.NSX_DHCP.default_lease_time,
|
|
'domain_name': cfg.CONF.NSX_DHCP.domain_name
|
|
}
|
|
self._test_lsn_port_dhcp_configure_with_subnet(
|
|
expected, dns=[], gw='127.0.0.1', routes=[])
|
|
|
|
def test_lsn_port_dhcp_configure_gatewayless(self):
|
|
expected = {
|
|
'default_lease_time': cfg.CONF.NSX_DHCP.default_lease_time,
|
|
'domain_name': cfg.CONF.NSX_DHCP.domain_name
|
|
}
|
|
self._test_lsn_port_dhcp_configure_with_subnet(expected, gw=None)
|
|
|
|
def test_lsn_port_dhcp_configure_with_extra_dns_servers(self):
|
|
expected = {
|
|
'default_lease_time': cfg.CONF.NSX_DHCP.default_lease_time,
|
|
'domain_name_servers': '8.8.8.8,9.9.9.9',
|
|
'domain_name': cfg.CONF.NSX_DHCP.domain_name
|
|
}
|
|
self._test_lsn_port_dhcp_configure_with_subnet(
|
|
expected, dns=['8.8.8.8', '9.9.9.9'])
|
|
|
|
def test_lsn_port_dhcp_configure_with_host_routes(self):
|
|
expected = {
|
|
'default_lease_time': cfg.CONF.NSX_DHCP.default_lease_time,
|
|
'domain_name': cfg.CONF.NSX_DHCP.domain_name,
|
|
'classless_static_routes': '8.8.8.8,9.9.9.9'
|
|
}
|
|
self._test_lsn_port_dhcp_configure_with_subnet(
|
|
expected, routes=['8.8.8.8', '9.9.9.9'])
|
|
|
|
def _test_lsn_metadata_configure(self, is_enabled):
|
|
with mock.patch.object(self.manager, 'lsn_port_dispose') as f:
|
|
self.manager.plugin.get_subnet.return_value = (
|
|
{'network_id': self.net_id})
|
|
self.manager.lsn_metadata_configure(mock.ANY,
|
|
self.sub_id, is_enabled)
|
|
expected = {
|
|
'metadata_server_port': 8775,
|
|
'metadata_server_ip': '127.0.0.1',
|
|
'metadata_proxy_shared_secret': ''
|
|
}
|
|
self.mock_lsn_api.lsn_metadata_configure.assert_called_once_with(
|
|
mock.ANY, mock.ANY, is_enabled, expected)
|
|
if is_enabled:
|
|
self.assertEqual(
|
|
1, self.mock_lsn_api.lsn_port_by_subnet_get.call_count)
|
|
else:
|
|
self.assertEqual(1, f.call_count)
|
|
|
|
def test_lsn_metadata_configure_enabled(self):
|
|
self._test_lsn_metadata_configure(True)
|
|
|
|
def test_lsn_metadata_configure_disabled(self):
|
|
self._test_lsn_metadata_configure(False)
|
|
|
|
def test_lsn_metadata_configure_not_found(self):
|
|
self.mock_lsn_api.lsn_metadata_configure.side_effect = (
|
|
p_exc.LsnNotFound(entity='lsn', entity_id=self.lsn_id))
|
|
self.manager.plugin.get_subnet.return_value = (
|
|
{'network_id': self.net_id})
|
|
self.assertRaises(p_exc.NsxPluginException,
|
|
self.manager.lsn_metadata_configure,
|
|
mock.ANY, self.sub_id, True)
|
|
|
|
def test_lsn_port_metadata_setup(self):
|
|
subnet = {
|
|
'cidr': '0.0.0.0/0',
|
|
'id': self.sub_id,
|
|
'network_id': self.net_id,
|
|
'tenant_id': self.tenant_id
|
|
}
|
|
self.mock_nsx_utils.get_nsx_switch_ids.return_value = [self.switch_id]
|
|
with mock.patch.object(lsn_man.switch_api, 'create_lport') as f:
|
|
f.return_value = {'uuid': self.port_id}
|
|
self.manager.lsn_port_metadata_setup(
|
|
mock.Mock(), self.lsn_id, subnet)
|
|
self.assertEqual(1, self.mock_lsn_api.lsn_port_create.call_count)
|
|
self.mock_lsn_api.lsn_port_plug_network.assert_called_once_with(
|
|
mock.ANY, self.lsn_id, mock.ANY, self.port_id)
|
|
|
|
def test_lsn_port_metadata_setup_raise_not_found(self):
|
|
subnet = {
|
|
'cidr': '0.0.0.0/0',
|
|
'id': self.sub_id,
|
|
'network_id': self.net_id,
|
|
'tenant_id': self.tenant_id
|
|
}
|
|
self.mock_nsx_utils.get_nsx_switch_ids.return_value = [self.switch_id]
|
|
with mock.patch.object(lsn_man.switch_api, 'create_lport') as f:
|
|
f.side_effect = n_exc.NotFound
|
|
self.assertRaises(p_exc.PortConfigurationError,
|
|
self.manager.lsn_port_metadata_setup,
|
|
mock.Mock(), self.lsn_id, subnet)
|
|
|
|
def test_lsn_port_metadata_setup_raise_conflict(self):
|
|
subnet = {
|
|
'cidr': '0.0.0.0/0',
|
|
'id': self.sub_id,
|
|
'network_id': self.net_id,
|
|
'tenant_id': self.tenant_id
|
|
}
|
|
self.mock_nsx_utils.get_nsx_switch_ids.return_value = [self.switch_id]
|
|
with mock.patch.object(lsn_man.switch_api, 'create_lport') as f:
|
|
with mock.patch.object(lsn_man.switch_api, 'delete_port') as g:
|
|
f.return_value = {'uuid': self.port_id}
|
|
self.mock_lsn_api.lsn_port_plug_network.side_effect = (
|
|
p_exc.LsnConfigurationConflict(lsn_id=self.lsn_id))
|
|
self.assertRaises(p_exc.PortConfigurationError,
|
|
self.manager.lsn_port_metadata_setup,
|
|
mock.Mock(), self.lsn_id, subnet)
|
|
self.assertEqual(1,
|
|
self.mock_lsn_api.lsn_port_delete.call_count)
|
|
self.assertEqual(1, g.call_count)
|
|
|
|
def _test_lsn_port_dispose_with_values(self, lsn_id, lsn_port_id, count):
|
|
with mock.patch.object(self.manager,
|
|
'lsn_port_get_by_mac',
|
|
return_value=(lsn_id, lsn_port_id)):
|
|
self.manager.lsn_port_dispose(mock.ANY, self.net_id, self.mac)
|
|
self.assertEqual(count,
|
|
self.mock_lsn_api.lsn_port_delete.call_count)
|
|
|
|
def test_lsn_port_dispose(self):
|
|
self._test_lsn_port_dispose_with_values(
|
|
self.lsn_id, self.lsn_port_id, 1)
|
|
|
|
def test_lsn_port_dispose_meta_mac(self):
|
|
self.mac = constants.METADATA_MAC
|
|
with mock.patch.object(lsn_man.switch_api,
|
|
'get_port_by_neutron_tag') as f:
|
|
with mock.patch.object(lsn_man.switch_api, 'delete_port') as g:
|
|
f.return_value = {'uuid': self.port_id}
|
|
self._test_lsn_port_dispose_with_values(
|
|
self.lsn_id, self.lsn_port_id, 1)
|
|
f.assert_called_once_with(
|
|
mock.ANY, self.net_id, constants.METADATA_PORT_ID)
|
|
g.assert_called_once_with(mock.ANY, self.net_id, self.port_id)
|
|
|
|
def test_lsn_port_dispose_lsn_not_found(self):
|
|
self._test_lsn_port_dispose_with_values(None, None, 0)
|
|
|
|
def test_lsn_port_dispose_lsn_port_not_found(self):
|
|
self._test_lsn_port_dispose_with_values(self.lsn_id, None, 0)
|
|
|
|
def test_lsn_port_dispose_api_error(self):
|
|
self.mock_lsn_api.lsn_port_delete.side_effect = (
|
|
exception.NsxApiException)
|
|
with mock.patch.object(lsn_man.LOG, 'warn') as l:
|
|
self.manager.lsn_port_dispose(mock.ANY, self.net_id, self.mac)
|
|
self.assertEqual(1, l.call_count)
|
|
|
|
def test_lsn_port_host_conf(self):
|
|
with mock.patch.object(self.manager,
|
|
'lsn_port_get',
|
|
return_value=(self.lsn_id, self.lsn_port_id)):
|
|
f = mock.Mock()
|
|
self.manager._lsn_port_host_conf(mock.ANY, self.net_id,
|
|
self.sub_id, mock.ANY, f)
|
|
self.assertEqual(1, f.call_count)
|
|
|
|
def test_lsn_port_host_conf_lsn_port_not_found(self):
|
|
with mock.patch.object(
|
|
self.manager,
|
|
'lsn_port_get',
|
|
side_effect=p_exc.LsnPortNotFound(lsn_id=self.lsn_id,
|
|
entity='subnet',
|
|
entity_id=self.sub_id)):
|
|
self.assertRaises(p_exc.PortConfigurationError,
|
|
self.manager._lsn_port_host_conf, mock.ANY,
|
|
self.net_id, self.sub_id, mock.ANY, mock.Mock())
|
|
|
|
def _test_lsn_port_update(self, dhcp=None, meta=None):
|
|
self.manager.lsn_port_update(
|
|
mock.ANY, self.net_id, self.sub_id, dhcp, meta)
|
|
count = 1 if dhcp else 0
|
|
count = count + 1 if meta else count
|
|
self.assertEqual(count, (self.mock_lsn_api.
|
|
lsn_port_host_entries_update.call_count))
|
|
|
|
def test_lsn_port_update(self):
|
|
self._test_lsn_port_update()
|
|
|
|
def test_lsn_port_update_dhcp_meta(self):
|
|
self._test_lsn_port_update(mock.ANY, mock.ANY)
|
|
|
|
def test_lsn_port_update_dhcp_and_nometa(self):
|
|
self._test_lsn_port_update(mock.ANY, None)
|
|
|
|
def test_lsn_port_update_nodhcp_and_nmeta(self):
|
|
self._test_lsn_port_update(None, mock.ANY)
|
|
|
|
def test_lsn_port_update_raise_error(self):
|
|
self.mock_lsn_api.lsn_port_host_entries_update.side_effect = (
|
|
exception.NsxApiException)
|
|
self.assertRaises(p_exc.PortConfigurationError,
|
|
self.manager.lsn_port_update,
|
|
mock.ANY, mock.ANY, mock.ANY, mock.ANY)
|
|
|
|
|
|
class PersistentLsnManagerTestCase(base.BaseTestCase):
|
|
|
|
def setUp(self):
|
|
super(PersistentLsnManagerTestCase, self).setUp()
|
|
self.net_id = 'foo_network_id'
|
|
self.sub_id = 'foo_subnet_id'
|
|
self.port_id = 'foo_port_id'
|
|
self.lsn_id = 'foo_lsn_id'
|
|
self.mac = 'aa:bb:cc:dd:ee:ff'
|
|
self.lsn_port_id = 'foo_lsn_port_id'
|
|
self.tenant_id = 'foo_tenant_id'
|
|
db.configure_db()
|
|
nsx.register_dhcp_opts(cfg)
|
|
nsx.register_metadata_opts(cfg)
|
|
lsn_man.register_lsn_opts(cfg)
|
|
self.manager = lsn_man.PersistentLsnManager(mock.Mock())
|
|
self.context = context.get_admin_context()
|
|
self.mock_lsn_api_p = mock.patch.object(lsn_man, 'lsn_api')
|
|
self.mock_lsn_api = self.mock_lsn_api_p.start()
|
|
self.addCleanup(db.clear_db)
|
|
|
|
def test_lsn_get(self):
|
|
lsn_db.lsn_add(self.context, self.net_id, self.lsn_id)
|
|
result = self.manager.lsn_get(self.context, self.net_id)
|
|
self.assertEqual(self.lsn_id, result)
|
|
|
|
def test_lsn_get_raise_not_found(self):
|
|
self.assertRaises(p_exc.LsnNotFound,
|
|
self.manager.lsn_get, self.context, self.net_id)
|
|
|
|
def test_lsn_get_silent_not_found(self):
|
|
result = self.manager.lsn_get(
|
|
self.context, self.net_id, raise_on_err=False)
|
|
self.assertIsNone(result)
|
|
|
|
def test_lsn_get_sync_on_missing(self):
|
|
cfg.CONF.set_override('sync_on_missing_data', True, 'NSX_LSN')
|
|
self.manager = lsn_man.PersistentLsnManager(mock.Mock())
|
|
with mock.patch.object(self.manager, 'lsn_save') as f:
|
|
self.manager.lsn_get(self.context, self.net_id, raise_on_err=True)
|
|
self.assertTrue(self.mock_lsn_api.lsn_for_network_get.call_count)
|
|
self.assertTrue(f.call_count)
|
|
|
|
def test_lsn_save(self):
|
|
self.manager.lsn_save(self.context, self.net_id, self.lsn_id)
|
|
result = self.manager.lsn_get(self.context, self.net_id)
|
|
self.assertEqual(self.lsn_id, result)
|
|
|
|
def test_lsn_create(self):
|
|
self.mock_lsn_api.lsn_for_network_create.return_value = self.lsn_id
|
|
with mock.patch.object(self.manager, 'lsn_save') as f:
|
|
result = self.manager.lsn_create(self.context, self.net_id)
|
|
self.assertTrue(
|
|
self.mock_lsn_api.lsn_for_network_create.call_count)
|
|
self.assertTrue(f.call_count)
|
|
self.assertEqual(self.lsn_id, result)
|
|
|
|
def test_lsn_create_failure(self):
|
|
with mock.patch.object(
|
|
self.manager, 'lsn_save',
|
|
side_effect=p_exc.NsxPluginException(err_msg='')):
|
|
self.assertRaises(p_exc.NsxPluginException,
|
|
self.manager.lsn_create,
|
|
self.context, self.net_id)
|
|
self.assertTrue(self.mock_lsn_api.lsn_delete.call_count)
|
|
|
|
def test_lsn_delete(self):
|
|
self.mock_lsn_api.lsn_for_network_create.return_value = self.lsn_id
|
|
self.manager.lsn_create(self.context, self.net_id)
|
|
self.manager.lsn_delete(self.context, self.lsn_id)
|
|
self.assertIsNone(self.manager.lsn_get(
|
|
self.context, self.net_id, raise_on_err=False))
|
|
|
|
def test_lsn_delete_not_existent(self):
|
|
self.manager.lsn_delete(self.context, self.lsn_id)
|
|
self.assertTrue(self.mock_lsn_api.lsn_delete.call_count)
|
|
|
|
def test_lsn_port_get(self):
|
|
lsn_db.lsn_add(self.context, self.net_id, self.lsn_id)
|
|
lsn_db.lsn_port_add_for_lsn(self.context, self.lsn_port_id,
|
|
self.sub_id, self.mac, self.lsn_id)
|
|
res = self.manager.lsn_port_get(self.context, self.net_id, self.sub_id)
|
|
self.assertEqual((self.lsn_id, self.lsn_port_id), res)
|
|
|
|
def test_lsn_port_get_raise_not_found(self):
|
|
self.assertRaises(p_exc.LsnPortNotFound,
|
|
self.manager.lsn_port_get,
|
|
self.context, self.net_id, self.sub_id)
|
|
|
|
def test_lsn_port_get_silent_not_found(self):
|
|
result = self.manager.lsn_port_get(
|
|
self.context, self.net_id, self.sub_id, raise_on_err=False)
|
|
self.assertEqual((None, None), result)
|
|
|
|
def test_lsn_port_get_sync_on_missing(self):
|
|
return
|
|
cfg.CONF.set_override('sync_on_missing_data', True, 'NSX_LSN')
|
|
self.manager = lsn_man.PersistentLsnManager(mock.Mock())
|
|
self.mock_lsn_api.lsn_for_network_get.return_value = self.lsn_id
|
|
self.mock_lsn_api.lsn_port_by_subnet_get.return_value = (
|
|
self.lsn_id, self.lsn_port_id)
|
|
with mock.patch.object(self.manager, 'lsn_save') as f:
|
|
with mock.patch.object(self.manager, 'lsn_port_save') as g:
|
|
self.manager.lsn_port_get(
|
|
self.context, self.net_id, self.sub_id)
|
|
self.assertTrue(
|
|
self.mock_lsn_api.lsn_port_by_subnet_get.call_count)
|
|
self.assertTrue(
|
|
self.mock_lsn_api.lsn_port_info_get.call_count)
|
|
self.assertTrue(f.call_count)
|
|
self.assertTrue(g.call_count)
|
|
|
|
def test_lsn_port_get_by_mac(self):
|
|
lsn_db.lsn_add(self.context, self.net_id, self.lsn_id)
|
|
lsn_db.lsn_port_add_for_lsn(self.context, self.lsn_port_id,
|
|
self.sub_id, self.mac, self.lsn_id)
|
|
res = self.manager.lsn_port_get_by_mac(
|
|
self.context, self.net_id, self.mac)
|
|
self.assertEqual((self.lsn_id, self.lsn_port_id), res)
|
|
|
|
def test_lsn_port_get_by_mac_raise_not_found(self):
|
|
self.assertRaises(p_exc.LsnPortNotFound,
|
|
self.manager.lsn_port_get_by_mac,
|
|
self.context, self.net_id, self.sub_id)
|
|
|
|
def test_lsn_port_get_by_mac_silent_not_found(self):
|
|
result = self.manager.lsn_port_get_by_mac(
|
|
self.context, self.net_id, self.sub_id, raise_on_err=False)
|
|
self.assertEqual((None, None), result)
|
|
|
|
def test_lsn_port_create(self):
|
|
lsn_db.lsn_add(self.context, self.net_id, self.lsn_id)
|
|
self.mock_lsn_api.lsn_port_create.return_value = self.lsn_port_id
|
|
subnet = {'subnet_id': self.sub_id, 'mac_address': self.mac}
|
|
with mock.patch.object(self.manager, 'lsn_port_save') as f:
|
|
result = self.manager.lsn_port_create(
|
|
self.context, self.net_id, subnet)
|
|
self.assertTrue(
|
|
self.mock_lsn_api.lsn_port_create.call_count)
|
|
self.assertTrue(f.call_count)
|
|
self.assertEqual(self.lsn_port_id, result)
|
|
|
|
def test_lsn_port_create_failure(self):
|
|
subnet = {'subnet_id': self.sub_id, 'mac_address': self.mac}
|
|
with mock.patch.object(
|
|
self.manager, 'lsn_port_save',
|
|
side_effect=p_exc.NsxPluginException(err_msg='')):
|
|
self.assertRaises(p_exc.NsxPluginException,
|
|
self.manager.lsn_port_create,
|
|
self.context, self.net_id, subnet)
|
|
self.assertTrue(self.mock_lsn_api.lsn_port_delete.call_count)
|
|
|
|
def test_lsn_port_delete(self):
|
|
lsn_db.lsn_add(self.context, self.net_id, self.lsn_id)
|
|
lsn_db.lsn_port_add_for_lsn(self.context, self.lsn_port_id,
|
|
self.sub_id, self.mac, self.lsn_id)
|
|
self.manager.lsn_port_delete(
|
|
self.context, self.lsn_id, self.lsn_port_id)
|
|
self.assertEqual((None, None), self.manager.lsn_port_get(
|
|
self.context, self.lsn_id, self.sub_id, raise_on_err=False))
|
|
|
|
def test_lsn_port_delete_not_existent(self):
|
|
self.manager.lsn_port_delete(
|
|
self.context, self.lsn_id, self.lsn_port_id)
|
|
self.assertTrue(self.mock_lsn_api.lsn_port_delete.call_count)
|
|
|
|
def test_lsn_port_save(self):
|
|
self.manager.lsn_save(self.context, self.net_id, self.lsn_id)
|
|
self.manager.lsn_port_save(self.context, self.lsn_port_id,
|
|
self.sub_id, self.mac, self.lsn_id)
|
|
result = self.manager.lsn_port_get(
|
|
self.context, self.net_id, self.sub_id, raise_on_err=False)
|
|
self.assertEqual((self.lsn_id, self.lsn_port_id), result)
|
|
|
|
|
|
class DhcpAgentNotifyAPITestCase(base.BaseTestCase):
|
|
|
|
def setUp(self):
|
|
super(DhcpAgentNotifyAPITestCase, self).setUp()
|
|
self.notifier = nsx.DhcpAgentNotifyAPI(mock.Mock(), mock.Mock())
|
|
self.plugin = self.notifier.plugin
|
|
self.lsn_manager = self.notifier.lsn_manager
|
|
|
|
def _test_notify_port_update(
|
|
self, ports, expected_count, expected_args=None):
|
|
port = {
|
|
'id': 'foo_port_id',
|
|
'network_id': 'foo_network_id',
|
|
'fixed_ips': [{'subnet_id': 'foo_subnet_id'}]
|
|
}
|
|
self.notifier.plugin.get_ports.return_value = ports
|
|
self.notifier.notify(mock.ANY, {'port': port}, 'port.update.end')
|
|
self.lsn_manager.lsn_port_update.assert_has_calls(expected_args)
|
|
|
|
def test_notify_ports_update_no_ports(self):
|
|
self._test_notify_port_update(None, 0, [])
|
|
self._test_notify_port_update([], 0, [])
|
|
|
|
def test_notify_ports_update_one_port(self):
|
|
ports = [{
|
|
'fixed_ips': [{'subnet_id': 'foo_subnet_id',
|
|
'ip_address': '1.2.3.4'}],
|
|
'device_id': 'foo_device_id',
|
|
'device_owner': 'foo_device_owner',
|
|
'mac_address': 'fa:16:3e:da:1d:46'
|
|
}]
|
|
call_args = mock.call(
|
|
mock.ANY, 'foo_network_id', 'foo_subnet_id',
|
|
dhcp=[{'ip_address': '1.2.3.4',
|
|
'mac_address': 'fa:16:3e:da:1d:46'}],
|
|
meta=[{'instance_id': 'foo_device_id',
|
|
'ip_address': '1.2.3.4'}])
|
|
self._test_notify_port_update(ports, 1, call_args)
|
|
|
|
def test_notify_ports_update_ports_with_empty_device_id(self):
|
|
ports = [{
|
|
'fixed_ips': [{'subnet_id': 'foo_subnet_id',
|
|
'ip_address': '1.2.3.4'}],
|
|
'device_id': '',
|
|
'device_owner': 'foo_device_owner',
|
|
'mac_address': 'fa:16:3e:da:1d:46'
|
|
}]
|
|
call_args = mock.call(
|
|
mock.ANY, 'foo_network_id', 'foo_subnet_id',
|
|
dhcp=[{'ip_address': '1.2.3.4',
|
|
'mac_address': 'fa:16:3e:da:1d:46'}],
|
|
meta=[]
|
|
)
|
|
self._test_notify_port_update(ports, 1, call_args)
|
|
|
|
def test_notify_ports_update_ports_with_no_fixed_ips(self):
|
|
ports = [{
|
|
'fixed_ips': [],
|
|
'device_id': 'foo_device_id',
|
|
'device_owner': 'foo_device_owner',
|
|
'mac_address': 'fa:16:3e:da:1d:46'
|
|
}]
|
|
call_args = mock.call(
|
|
mock.ANY, 'foo_network_id', 'foo_subnet_id', dhcp=[], meta=[])
|
|
self._test_notify_port_update(ports, 1, call_args)
|
|
|
|
def test_notify_ports_update_ports_with_no_fixed_ips_and_no_device(self):
|
|
ports = [{
|
|
'fixed_ips': [],
|
|
'device_id': '',
|
|
'device_owner': 'foo_device_owner',
|
|
'mac_address': 'fa:16:3e:da:1d:46'
|
|
}]
|
|
call_args = mock.call(
|
|
mock.ANY, 'foo_network_id', 'foo_subnet_id', dhcp=[], meta=[])
|
|
self._test_notify_port_update(ports, 0, call_args)
|
|
|
|
def test_notify_ports_update_with_special_ports(self):
|
|
ports = [{'fixed_ips': [],
|
|
'device_id': '',
|
|
'device_owner': n_consts.DEVICE_OWNER_DHCP,
|
|
'mac_address': 'fa:16:3e:da:1d:46'},
|
|
{'fixed_ips': [{'subnet_id': 'foo_subnet_id',
|
|
'ip_address': '1.2.3.4'}],
|
|
'device_id': 'foo_device_id',
|
|
'device_owner': n_consts.DEVICE_OWNER_ROUTER_GW,
|
|
'mac_address': 'fa:16:3e:da:1d:46'}]
|
|
call_args = mock.call(
|
|
mock.ANY, 'foo_network_id', 'foo_subnet_id', dhcp=[], meta=[])
|
|
self._test_notify_port_update(ports, 0, call_args)
|
|
|
|
def test_notify_ports_update_many_ports(self):
|
|
ports = [{'fixed_ips': [],
|
|
'device_id': '',
|
|
'device_owner': 'foo_device_owner',
|
|
'mac_address': 'fa:16:3e:da:1d:46'},
|
|
{'fixed_ips': [{'subnet_id': 'foo_subnet_id',
|
|
'ip_address': '1.2.3.4'}],
|
|
'device_id': 'foo_device_id',
|
|
'device_owner': 'foo_device_owner',
|
|
'mac_address': 'fa:16:3e:da:1d:46'}]
|
|
call_args = mock.call(
|
|
mock.ANY, 'foo_network_id', 'foo_subnet_id',
|
|
dhcp=[{'ip_address': '1.2.3.4',
|
|
'mac_address': 'fa:16:3e:da:1d:46'}],
|
|
meta=[{'instance_id': 'foo_device_id',
|
|
'ip_address': '1.2.3.4'}])
|
|
self._test_notify_port_update(ports, 1, call_args)
|
|
|
|
def _test_notify_subnet_action(self, action):
|
|
with mock.patch.object(self.notifier, '_subnet_%s' % action) as f:
|
|
self.notifier._handle_subnet_dhcp_access[action] = f
|
|
subnet = {'subnet': mock.ANY}
|
|
self.notifier.notify(
|
|
mock.ANY, subnet, 'subnet.%s.end' % action)
|
|
f.assert_called_once_with(mock.ANY, subnet)
|
|
|
|
def test_notify_subnet_create(self):
|
|
self._test_notify_subnet_action('create')
|
|
|
|
def test_notify_subnet_update(self):
|
|
self._test_notify_subnet_action('update')
|
|
|
|
def test_notify_subnet_delete(self):
|
|
self._test_notify_subnet_action('delete')
|
|
|
|
def _test_subnet_create(self, enable_dhcp, exc=None,
|
|
exc_obj=None, call_notify=True):
|
|
subnet = {
|
|
'id': 'foo_subnet_id',
|
|
'enable_dhcp': enable_dhcp,
|
|
'network_id': 'foo_network_id',
|
|
'tenant_id': 'foo_tenant_id',
|
|
'cidr': '0.0.0.0/0'
|
|
}
|
|
if exc:
|
|
self.plugin.create_port.side_effect = exc_obj or exc
|
|
self.assertRaises(exc,
|
|
self.notifier.notify,
|
|
mock.ANY,
|
|
{'subnet': subnet},
|
|
'subnet.create.end')
|
|
self.plugin.delete_subnet.assert_called_with(
|
|
mock.ANY, subnet['id'])
|
|
else:
|
|
if call_notify:
|
|
self.notifier.notify(
|
|
mock.ANY, {'subnet': subnet}, 'subnet.create.end')
|
|
if enable_dhcp:
|
|
dhcp_port = {
|
|
'name': '',
|
|
'admin_state_up': True,
|
|
'network_id': 'foo_network_id',
|
|
'tenant_id': 'foo_tenant_id',
|
|
'device_owner': n_consts.DEVICE_OWNER_DHCP,
|
|
'mac_address': mock.ANY,
|
|
'fixed_ips': [{'subnet_id': 'foo_subnet_id'}],
|
|
'device_id': ''
|
|
}
|
|
self.plugin.create_port.assert_called_once_with(
|
|
mock.ANY, {'port': dhcp_port})
|
|
else:
|
|
self.assertEqual(0, self.plugin.create_port.call_count)
|
|
|
|
def test_subnet_create_enabled_dhcp(self):
|
|
self._test_subnet_create(True)
|
|
|
|
def test_subnet_create_disabled_dhcp(self):
|
|
self._test_subnet_create(False)
|
|
|
|
def test_subnet_create_raise_port_config_error(self):
|
|
with mock.patch.object(nsx.db_base_plugin_v2.NeutronDbPluginV2,
|
|
'delete_port') as d:
|
|
self._test_subnet_create(
|
|
True,
|
|
exc=n_exc.Conflict,
|
|
exc_obj=p_exc.PortConfigurationError(lsn_id='foo_lsn_id',
|
|
net_id='foo_net_id',
|
|
port_id='foo_port_id'))
|
|
d.assert_called_once_with(self.plugin, mock.ANY, 'foo_port_id')
|
|
|
|
def test_subnet_update(self):
|
|
subnet = {
|
|
'id': 'foo_subnet_id',
|
|
'network_id': 'foo_network_id',
|
|
}
|
|
self.lsn_manager.lsn_port_get.return_value = ('foo_lsn_id',
|
|
'foo_lsn_port_id')
|
|
self.notifier.notify(
|
|
mock.ANY, {'subnet': subnet}, 'subnet.update.end')
|
|
self.lsn_manager.lsn_port_dhcp_configure.assert_called_once_with(
|
|
mock.ANY, 'foo_lsn_id', 'foo_lsn_port_id', subnet)
|
|
|
|
def test_subnet_update_raise_lsn_not_found(self):
|
|
subnet = {
|
|
'id': 'foo_subnet_id',
|
|
'network_id': 'foo_network_id',
|
|
}
|
|
self.lsn_manager.lsn_port_get.side_effect = (
|
|
p_exc.LsnNotFound(entity='network',
|
|
entity_id=subnet['network_id']))
|
|
self.assertRaises(p_exc.LsnNotFound,
|
|
self.notifier.notify,
|
|
mock.ANY, {'subnet': subnet}, 'subnet.update.end')
|
|
|
|
def _test_subnet_update_lsn_port_not_found(self, dhcp_port):
|
|
subnet = {
|
|
'id': 'foo_subnet_id',
|
|
'enable_dhcp': True,
|
|
'network_id': 'foo_network_id',
|
|
'tenant_id': 'foo_tenant_id'
|
|
}
|
|
self.lsn_manager.lsn_port_get.side_effect = (
|
|
p_exc.LsnPortNotFound(lsn_id='foo_lsn_id',
|
|
entity='subnet',
|
|
entity_id=subnet['id']))
|
|
self.notifier.plugin.get_ports.return_value = dhcp_port
|
|
count = 0 if dhcp_port is None else 1
|
|
with mock.patch.object(nsx, 'handle_port_dhcp_access') as h:
|
|
self.notifier.notify(
|
|
mock.ANY, {'subnet': subnet}, 'subnet.update.end')
|
|
self.assertEqual(count, h.call_count)
|
|
if not dhcp_port:
|
|
self._test_subnet_create(enable_dhcp=True,
|
|
exc=None, call_notify=False)
|
|
|
|
def test_subnet_update_lsn_port_not_found_without_dhcp_port(self):
|
|
self._test_subnet_update_lsn_port_not_found(None)
|
|
|
|
def test_subnet_update_lsn_port_not_found_with_dhcp_port(self):
|
|
self._test_subnet_update_lsn_port_not_found([mock.ANY])
|
|
|
|
def _test_subnet_delete(self, ports=None):
|
|
subnet = {
|
|
'id': 'foo_subnet_id',
|
|
'network_id': 'foo_network_id',
|
|
'cidr': '0.0.0.0/0'
|
|
}
|
|
self.plugin.get_ports.return_value = ports
|
|
self.notifier.notify(mock.ANY, {'subnet': subnet}, 'subnet.delete.end')
|
|
filters = {
|
|
'network_id': [subnet['network_id']],
|
|
'device_owner': [n_consts.DEVICE_OWNER_DHCP]
|
|
}
|
|
self.plugin.get_ports.assert_called_once_with(
|
|
mock.ANY, filters=filters)
|
|
if ports:
|
|
self.plugin.delete_port.assert_called_once_with(
|
|
mock.ANY, ports[0]['id'])
|
|
else:
|
|
self.assertEqual(0, self.plugin.delete_port.call_count)
|
|
|
|
def test_subnet_delete_enabled_dhcp_no_ports(self):
|
|
self._test_subnet_delete()
|
|
|
|
def test_subnet_delete_enabled_dhcp_with_dhcp_port(self):
|
|
self._test_subnet_delete([{'id': 'foo_port_id'}])
|
|
|
|
|
|
class DhcpTestCase(base.BaseTestCase):
|
|
|
|
def setUp(self):
|
|
super(DhcpTestCase, self).setUp()
|
|
self.plugin = mock.Mock()
|
|
self.plugin.lsn_manager = mock.Mock()
|
|
|
|
def test_handle_create_network(self):
|
|
network = {'id': 'foo_network_id'}
|
|
nsx.handle_network_dhcp_access(
|
|
self.plugin, mock.ANY, network, 'create_network')
|
|
self.plugin.lsn_manager.lsn_create.assert_called_once_with(
|
|
mock.ANY, network['id'])
|
|
|
|
def test_handle_create_network_router_external(self):
|
|
network = {'id': 'foo_network_id', 'router:external': True}
|
|
nsx.handle_network_dhcp_access(
|
|
self.plugin, mock.ANY, network, 'create_network')
|
|
self.assertFalse(self.plugin.lsn_manager.lsn_create.call_count)
|
|
|
|
def test_handle_delete_network(self):
|
|
network_id = 'foo_network_id'
|
|
self.plugin.lsn_manager.lsn_delete_by_network.return_value = (
|
|
'foo_lsn_id')
|
|
nsx.handle_network_dhcp_access(
|
|
self.plugin, mock.ANY, network_id, 'delete_network')
|
|
self.plugin.lsn_manager.lsn_delete_by_network.assert_called_once_with(
|
|
mock.ANY, 'foo_network_id')
|
|
|
|
def _test_handle_create_dhcp_owner_port(self, exc=None):
|
|
subnet = {
|
|
'cidr': '0.0.0.0/0',
|
|
'id': 'foo_subnet_id'
|
|
}
|
|
port = {
|
|
'id': 'foo_port_id',
|
|
'device_owner': n_consts.DEVICE_OWNER_DHCP,
|
|
'mac_address': 'aa:bb:cc:dd:ee:ff',
|
|
'network_id': 'foo_network_id',
|
|
'fixed_ips': [{'subnet_id': subnet['id']}]
|
|
}
|
|
expected_data = {
|
|
'subnet_id': subnet['id'],
|
|
'ip_address': subnet['cidr'],
|
|
'mac_address': port['mac_address']
|
|
}
|
|
self.plugin.get_subnet.return_value = subnet
|
|
if exc is None:
|
|
nsx.handle_port_dhcp_access(
|
|
self.plugin, mock.ANY, port, 'create_port')
|
|
(self.plugin.lsn_manager.lsn_port_dhcp_setup.
|
|
assert_called_once_with(mock.ANY, port['network_id'],
|
|
port['id'], expected_data, subnet))
|
|
else:
|
|
self.plugin.lsn_manager.lsn_port_dhcp_setup.side_effect = exc
|
|
self.assertRaises(n_exc.NeutronException,
|
|
nsx.handle_port_dhcp_access,
|
|
self.plugin, mock.ANY, port, 'create_port')
|
|
|
|
def test_handle_create_dhcp_owner_port(self):
|
|
self._test_handle_create_dhcp_owner_port()
|
|
|
|
def test_handle_create_dhcp_owner_port_raise_port_config_error(self):
|
|
config_error = p_exc.PortConfigurationError(lsn_id='foo_lsn_id',
|
|
net_id='foo_net_id',
|
|
port_id='foo_port_id')
|
|
self._test_handle_create_dhcp_owner_port(exc=config_error)
|
|
|
|
def test_handle_delete_dhcp_owner_port(self):
|
|
port = {
|
|
'id': 'foo_port_id',
|
|
'device_owner': n_consts.DEVICE_OWNER_DHCP,
|
|
'network_id': 'foo_network_id',
|
|
'fixed_ips': [],
|
|
'mac_address': 'aa:bb:cc:dd:ee:ff'
|
|
}
|
|
nsx.handle_port_dhcp_access(self.plugin, mock.ANY, port, 'delete_port')
|
|
self.plugin.lsn_manager.lsn_port_dispose.assert_called_once_with(
|
|
mock.ANY, port['network_id'], port['mac_address'])
|
|
|
|
def _test_handle_user_port(self, action, handler):
|
|
port = {
|
|
'id': 'foo_port_id',
|
|
'device_owner': 'foo_device_owner',
|
|
'network_id': 'foo_network_id',
|
|
'mac_address': 'aa:bb:cc:dd:ee:ff',
|
|
'fixed_ips': [{'subnet_id': 'foo_subnet_id',
|
|
'ip_address': '1.2.3.4'}]
|
|
}
|
|
expected_data = {
|
|
'ip_address': '1.2.3.4',
|
|
'mac_address': 'aa:bb:cc:dd:ee:ff'
|
|
}
|
|
self.plugin.get_subnet.return_value = {'enable_dhcp': True}
|
|
nsx.handle_port_dhcp_access(self.plugin, mock.ANY, port, action)
|
|
handler.assert_called_once_with(
|
|
mock.ANY, port['network_id'], 'foo_subnet_id', expected_data)
|
|
|
|
def test_handle_create_user_port(self):
|
|
self._test_handle_user_port(
|
|
'create_port', self.plugin.lsn_manager.lsn_port_dhcp_host_add)
|
|
|
|
def test_handle_delete_user_port(self):
|
|
self._test_handle_user_port(
|
|
'delete_port', self.plugin.lsn_manager.lsn_port_dhcp_host_remove)
|
|
|
|
def _test_handle_user_port_disabled_dhcp(self, action, handler):
|
|
port = {
|
|
'id': 'foo_port_id',
|
|
'device_owner': 'foo_device_owner',
|
|
'network_id': 'foo_network_id',
|
|
'mac_address': 'aa:bb:cc:dd:ee:ff',
|
|
'fixed_ips': [{'subnet_id': 'foo_subnet_id',
|
|
'ip_address': '1.2.3.4'}]
|
|
}
|
|
self.plugin.get_subnet.return_value = {'enable_dhcp': False}
|
|
nsx.handle_port_dhcp_access(self.plugin, mock.ANY, port, action)
|
|
self.assertEqual(0, handler.call_count)
|
|
|
|
def test_handle_create_user_port_disabled_dhcp(self):
|
|
self._test_handle_user_port_disabled_dhcp(
|
|
'create_port', self.plugin.lsn_manager.lsn_port_dhcp_host_add)
|
|
|
|
def test_handle_delete_user_port_disabled_dhcp(self):
|
|
self._test_handle_user_port_disabled_dhcp(
|
|
'delete_port', self.plugin.lsn_manager.lsn_port_dhcp_host_remove)
|
|
|
|
def _test_handle_user_port_no_fixed_ips(self, action, handler):
|
|
port = {
|
|
'id': 'foo_port_id',
|
|
'device_owner': 'foo_device_owner',
|
|
'network_id': 'foo_network_id',
|
|
'fixed_ips': []
|
|
}
|
|
nsx.handle_port_dhcp_access(self.plugin, mock.ANY, port, action)
|
|
self.assertEqual(0, handler.call_count)
|
|
|
|
def test_handle_create_user_port_no_fixed_ips(self):
|
|
self._test_handle_user_port_no_fixed_ips(
|
|
'create_port', self.plugin.lsn_manager.lsn_port_dhcp_host_add)
|
|
|
|
def test_handle_delete_user_port_no_fixed_ips(self):
|
|
self._test_handle_user_port_no_fixed_ips(
|
|
'delete_port', self.plugin.lsn_manager.lsn_port_dhcp_host_remove)
|
|
|
|
|
|
class MetadataTestCase(base.BaseTestCase):
|
|
|
|
def setUp(self):
|
|
super(MetadataTestCase, self).setUp()
|
|
self.plugin = mock.Mock()
|
|
self.plugin.lsn_manager = mock.Mock()
|
|
|
|
def _test_handle_port_metadata_access_special_owners(
|
|
self, owner, dev_id='foo_device_id', ips=None):
|
|
port = {
|
|
'id': 'foo_port_id',
|
|
'device_owner': owner,
|
|
'device_id': dev_id,
|
|
'fixed_ips': ips or []
|
|
}
|
|
nsx.handle_port_metadata_access(self.plugin, mock.ANY, port, mock.ANY)
|
|
self.assertFalse(
|
|
self.plugin.lsn_manager.lsn_port_meta_host_add.call_count)
|
|
self.assertFalse(
|
|
self.plugin.lsn_manager.lsn_port_meta_host_remove.call_count)
|
|
|
|
def test_handle_port_metadata_access_external_network(self):
|
|
port = {
|
|
'id': 'foo_port_id',
|
|
'device_owner': 'foo_device_owner',
|
|
'device_id': 'foo_device_id',
|
|
'network_id': 'foo_network_id',
|
|
'fixed_ips': [{'subnet_id': 'foo_subnet'}]
|
|
}
|
|
self.plugin.get_network.return_value = {'router:external': True}
|
|
nsx.handle_port_metadata_access(self.plugin, mock.ANY, port, mock.ANY)
|
|
self.assertFalse(
|
|
self.plugin.lsn_manager.lsn_port_meta_host_add.call_count)
|
|
self.assertFalse(
|
|
self.plugin.lsn_manager.lsn_port_meta_host_remove.call_count)
|
|
|
|
def test_handle_port_metadata_access_dhcp_port(self):
|
|
self._test_handle_port_metadata_access_special_owners(
|
|
n_consts.DEVICE_OWNER_DHCP, [{'subnet_id': 'foo_subnet'}])
|
|
|
|
def test_handle_port_metadata_access_router_port(self):
|
|
self._test_handle_port_metadata_access_special_owners(
|
|
n_consts.DEVICE_OWNER_ROUTER_INTF, [{'subnet_id': 'foo_subnet'}])
|
|
|
|
def test_handle_port_metadata_access_no_device_id(self):
|
|
self._test_handle_port_metadata_access_special_owners(
|
|
n_consts.DEVICE_OWNER_DHCP, '')
|
|
|
|
def test_handle_port_metadata_access_no_fixed_ips(self):
|
|
self._test_handle_port_metadata_access_special_owners(
|
|
'foo', 'foo', None)
|
|
|
|
def _test_handle_port_metadata_access(self, is_delete, raise_exc=False):
|
|
port = {
|
|
'id': 'foo_port_id',
|
|
'device_owner': 'foo_device_id',
|
|
'network_id': 'foo_network_id',
|
|
'device_id': 'foo_device_id',
|
|
'tenant_id': 'foo_tenant_id',
|
|
'fixed_ips': [
|
|
{'subnet_id': 'foo_subnet_id', 'ip_address': '1.2.3.4'}
|
|
]
|
|
}
|
|
meta = {
|
|
'instance_id': port['device_id'],
|
|
'tenant_id': port['tenant_id'],
|
|
'ip_address': port['fixed_ips'][0]['ip_address']
|
|
}
|
|
self.plugin.get_network.return_value = {'router:external': False}
|
|
if is_delete:
|
|
mock_func = self.plugin.lsn_manager.lsn_port_meta_host_remove
|
|
else:
|
|
mock_func = self.plugin.lsn_manager.lsn_port_meta_host_add
|
|
if raise_exc:
|
|
mock_func.side_effect = p_exc.PortConfigurationError(
|
|
lsn_id='foo_lsn_id', net_id='foo_net_id', port_id=None)
|
|
with mock.patch.object(nsx.db_base_plugin_v2.NeutronDbPluginV2,
|
|
'delete_port') as d:
|
|
self.assertRaises(p_exc.PortConfigurationError,
|
|
nsx.handle_port_metadata_access,
|
|
self.plugin, mock.ANY, port,
|
|
is_delete=is_delete)
|
|
if not is_delete:
|
|
d.assert_called_once_with(mock.ANY, mock.ANY, port['id'])
|
|
else:
|
|
self.assertFalse(d.call_count)
|
|
else:
|
|
nsx.handle_port_metadata_access(
|
|
self.plugin, mock.ANY, port, is_delete=is_delete)
|
|
mock_func.assert_called_once_with(mock.ANY, mock.ANY, mock.ANY, meta)
|
|
|
|
def test_handle_port_metadata_access_on_delete_true(self):
|
|
self._test_handle_port_metadata_access(True)
|
|
|
|
def test_handle_port_metadata_access_on_delete_false(self):
|
|
self._test_handle_port_metadata_access(False)
|
|
|
|
def test_handle_port_metadata_access_on_delete_true_raise(self):
|
|
self._test_handle_port_metadata_access(True, raise_exc=True)
|
|
|
|
def test_handle_port_metadata_access_on_delete_false_raise(self):
|
|
self._test_handle_port_metadata_access(False, raise_exc=True)
|
|
|
|
def _test_handle_router_metadata_access(
|
|
self, is_port_found, raise_exc=False):
|
|
subnet = {
|
|
'id': 'foo_subnet_id',
|
|
'network_id': 'foo_network_id'
|
|
}
|
|
interface = {
|
|
'subnet_id': subnet['id'],
|
|
'port_id': 'foo_port_id'
|
|
}
|
|
mock_func = self.plugin.lsn_manager.lsn_metadata_configure
|
|
if not is_port_found:
|
|
self.plugin.get_port.side_effect = n_exc.NotFound
|
|
if raise_exc:
|
|
with mock.patch.object(nsx.l3_db.L3_NAT_db_mixin,
|
|
'remove_router_interface') as d:
|
|
mock_func.side_effect = p_exc.NsxPluginException(err_msg='')
|
|
self.assertRaises(p_exc.NsxPluginException,
|
|
nsx.handle_router_metadata_access,
|
|
self.plugin, mock.ANY, 'foo_router_id',
|
|
interface)
|
|
d.assert_called_once_with(mock.ANY, mock.ANY, 'foo_router_id',
|
|
interface)
|
|
else:
|
|
nsx.handle_router_metadata_access(
|
|
self.plugin, mock.ANY, 'foo_router_id', interface)
|
|
mock_func.assert_called_once_with(
|
|
mock.ANY, subnet['id'], is_port_found)
|
|
|
|
def test_handle_router_metadata_access_add_interface(self):
|
|
self._test_handle_router_metadata_access(True)
|
|
|
|
def test_handle_router_metadata_access_delete_interface(self):
|
|
self._test_handle_router_metadata_access(False)
|
|
|
|
def test_handle_router_metadata_access_raise_error_on_add(self):
|
|
self._test_handle_router_metadata_access(True, raise_exc=True)
|
|
|
|
def test_handle_router_metadata_access_raise_error_on_delete(self):
|
|
self._test_handle_router_metadata_access(True, raise_exc=False)
|