
Now that we are python3 only, we should move to using the built in version of mock that supports all of our testing needs and remove the dependency on the "mock" package. Also see commit: Ifcaf1c21bea0ec3c35278e49cecc90a101a82113 Change-Id: I58da980351fe14357c210c02eb167a6c0af9d09e
329 lines
16 KiB
Python
329 lines
16 KiB
Python
# Copyright 2015 VMware, Inc.
|
|
# All Rights Reserved
|
|
#
|
|
# 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.
|
|
|
|
from unittest import mock
|
|
|
|
import netaddr
|
|
from neutron_lib import constants
|
|
from neutron_lib import exceptions as n_exc
|
|
from neutron_lib.plugins import directory
|
|
from oslo_config import cfg
|
|
import webob.exc
|
|
|
|
from vmware_nsx.api_client import exception as api_exc
|
|
from vmware_nsx.common import config
|
|
|
|
|
|
class MetaDataTestCase(object):
|
|
|
|
def _metadata_setup(self, mode=config.MetadataModes.DIRECT,
|
|
on_demand=False):
|
|
cfg.CONF.set_override('metadata_mode', mode, self.plugin.cfg_group)
|
|
if hasattr(getattr(cfg.CONF, self.plugin.cfg_group),
|
|
'metadata_on_demand'):
|
|
cfg.CONF.set_override('metadata_on_demand', on_demand,
|
|
self.plugin.cfg_group)
|
|
|
|
def _metadata_teardown(self):
|
|
cfg.CONF.set_override('metadata_mode', None, self.plugin.cfg_group)
|
|
if hasattr(getattr(cfg.CONF, self.plugin.cfg_group),
|
|
'metadata_on_demand'):
|
|
cfg.CONF.set_override('metadata_on_demand', False,
|
|
self.plugin.cfg_group)
|
|
|
|
def _check_metadata(self, expected_subnets, expected_ports):
|
|
subnets = self._list('subnets')['subnets']
|
|
self.assertEqual(len(subnets), expected_subnets)
|
|
meta_net_id, meta_sub_id = None, None
|
|
meta_cidr = netaddr.IPNetwork('169.254.0.0/16')
|
|
for subnet in subnets:
|
|
cidr = netaddr.IPNetwork(subnet['cidr'])
|
|
if meta_cidr == cidr or meta_cidr in cidr.supernet(16):
|
|
meta_sub_id = subnet['id']
|
|
meta_net_id = subnet['network_id']
|
|
break
|
|
ports = self._list(
|
|
'ports',
|
|
query_params='network_id=%s' % meta_net_id)['ports']
|
|
self.assertEqual(len(ports), expected_ports)
|
|
meta_port_id = ports[0]['id'] if ports else None
|
|
return meta_net_id, meta_sub_id, meta_port_id
|
|
|
|
def test_router_add_interface_subnet_with_metadata_access(self):
|
|
self._metadata_setup()
|
|
self.test_router_add_interface_subnet()
|
|
self._metadata_teardown()
|
|
|
|
def test_router_add_interface_port_with_metadata_access(self):
|
|
self._metadata_setup()
|
|
self.test_router_add_interface_port()
|
|
self._metadata_teardown()
|
|
|
|
def test_router_add_interface_dupsubnet_returns_400_with_metadata(self):
|
|
self._metadata_setup()
|
|
self.test_router_add_interface_dup_subnet1_returns_400()
|
|
self._metadata_teardown()
|
|
|
|
def test_router_add_interface_overlapped_cidr_returns_400_with(self):
|
|
self._metadata_setup()
|
|
self.test_router_add_interface_overlapped_cidr_returns_400()
|
|
self._metadata_teardown()
|
|
|
|
def test_router_remove_interface_inuse_returns_409_with_metadata(self):
|
|
self._metadata_setup()
|
|
self.test_router_remove_interface_inuse_returns_409()
|
|
self._metadata_teardown()
|
|
|
|
def test_router_remove_iface_wrong_sub_returns_400_with_metadata(self):
|
|
self._metadata_setup()
|
|
self.test_router_remove_interface_wrong_subnet_returns_400()
|
|
self._metadata_teardown()
|
|
|
|
def test_router_delete_with_metadata_access(self):
|
|
self._metadata_setup()
|
|
self.test_router_delete()
|
|
self._metadata_teardown()
|
|
|
|
def test_router_delete_with_port_existed_returns_409_with_metadata(self):
|
|
self._metadata_setup()
|
|
self.test_router_delete_with_port_existed_returns_409()
|
|
self._metadata_teardown()
|
|
|
|
def test_delete_port_with_metadata(self):
|
|
self._metadata_setup(config.MetadataModes.INDIRECT)
|
|
with self.subnet() as s:
|
|
with self.port(subnet=s, fixed_ips=[], device_id='1234',
|
|
device_owner=constants.DEVICE_OWNER_DHCP) as port:
|
|
self._delete('ports', port['port']['id'])
|
|
self._metadata_teardown()
|
|
|
|
def test_metadatata_network_created_with_router_interface_add(self):
|
|
self._metadata_setup()
|
|
with mock.patch.object(self._plugin_class, 'schedule_network') as f:
|
|
with self.router() as r:
|
|
with self.subnet() as s:
|
|
self._router_interface_action('add',
|
|
r['router']['id'],
|
|
s['subnet']['id'],
|
|
None)
|
|
r_ports = self._list('ports')['ports']
|
|
self.assertEqual(len(r_ports), 2)
|
|
ips = []
|
|
for port in r_ports:
|
|
ips.extend([netaddr.IPAddress(fixed_ip['ip_address'])
|
|
for fixed_ip in port['fixed_ips']])
|
|
meta_cidr = netaddr.IPNetwork('169.254.0.0/16')
|
|
self.assertTrue(any([ip in meta_cidr for ip in ips]))
|
|
# Needed to avoid 409.
|
|
self._router_interface_action('remove',
|
|
r['router']['id'],
|
|
s['subnet']['id'],
|
|
None)
|
|
# Verify that there has been a schedule_network all for the
|
|
# metadata network
|
|
expected_net_name = 'meta-%s' % r['router']['id']
|
|
found = False
|
|
for call in f.call_args_list:
|
|
# The network data are the last of the positional arguments
|
|
net_dict = call[0][-1]
|
|
if net_dict['name'] == expected_net_name:
|
|
self.assertFalse(net_dict['port_security_enabled'])
|
|
self.assertFalse(net_dict['shared'])
|
|
self.assertFalse(net_dict['tenant_id'])
|
|
found = True
|
|
break
|
|
else:
|
|
self.fail("Expected schedule_network call for metadata "
|
|
"network %s not found" % expected_net_name)
|
|
self.assertTrue(found)
|
|
self._metadata_teardown()
|
|
|
|
def test_metadata_network_create_rollback_on_create_subnet_failure(self):
|
|
self._metadata_setup()
|
|
with self.router() as r:
|
|
with self.subnet() as s:
|
|
# Raise a NeutronException (eg: NotFound).
|
|
with mock.patch.object(self._plugin_class,
|
|
'create_subnet',
|
|
side_effect=n_exc.NotFound):
|
|
self._router_interface_action(
|
|
'add', r['router']['id'], s['subnet']['id'], None)
|
|
# Ensure metadata network was removed.
|
|
nets = self._list('networks')['networks']
|
|
self.assertEqual(len(nets), 1)
|
|
# Needed to avoid 409.
|
|
self._router_interface_action('remove',
|
|
r['router']['id'],
|
|
s['subnet']['id'],
|
|
None)
|
|
self._metadata_teardown()
|
|
|
|
def test_metadata_network_create_rollback_on_add_rtr_iface_failure(self):
|
|
self._metadata_setup()
|
|
with self.router() as r:
|
|
with self.subnet() as s:
|
|
# Save function being mocked.
|
|
real_func = self._plugin_class.add_router_interface
|
|
plugin_instance = directory.get_plugin()
|
|
|
|
# Raise a NeutronException when adding metadata subnet
|
|
# to router.
|
|
def side_effect(*args):
|
|
if args[-1]['subnet_id'] == s['subnet']['id']:
|
|
# Do the real thing.
|
|
return real_func(plugin_instance, *args)
|
|
# Otherwise raise.
|
|
raise api_exc.NsxApiException()
|
|
|
|
with mock.patch.object(self._plugin_class,
|
|
'add_router_interface',
|
|
side_effect=side_effect):
|
|
self._router_interface_action(
|
|
'add', r['router']['id'], s['subnet']['id'], None)
|
|
# Ensure metadata network was removed.
|
|
nets = self._list('networks')['networks']
|
|
self.assertEqual(len(nets), 1)
|
|
# Needed to avoid 409.
|
|
self._router_interface_action('remove',
|
|
r['router']['id'],
|
|
s['subnet']['id'],
|
|
None)
|
|
self._metadata_teardown()
|
|
|
|
def test_metadata_network_removed_with_router_interface_remove(self):
|
|
self._metadata_setup()
|
|
with self.router() as r:
|
|
with self.subnet() as s:
|
|
self._router_interface_action('add', r['router']['id'],
|
|
s['subnet']['id'], None)
|
|
meta_net_id, meta_sub_id, meta_port_id = self._check_metadata(
|
|
expected_subnets=2, expected_ports=1)
|
|
self._router_interface_action('remove', r['router']['id'],
|
|
s['subnet']['id'], None)
|
|
self._show('networks', meta_net_id,
|
|
webob.exc.HTTPNotFound.code)
|
|
self._show('ports', meta_port_id,
|
|
webob.exc.HTTPNotFound.code)
|
|
self._show('subnets', meta_sub_id,
|
|
webob.exc.HTTPNotFound.code)
|
|
self._metadata_teardown()
|
|
|
|
def test_metadata_network_remove_rollback_on_failure(self):
|
|
self._metadata_setup()
|
|
with self.router() as r:
|
|
with self.subnet() as s:
|
|
self._router_interface_action('add', r['router']['id'],
|
|
s['subnet']['id'], None)
|
|
networks = self._list('networks')['networks']
|
|
for network in networks:
|
|
if network['id'] != s['subnet']['network_id']:
|
|
meta_net_id = network['id']
|
|
ports = self._list(
|
|
'ports',
|
|
query_params='network_id=%s' % meta_net_id)['ports']
|
|
meta_port_id = ports[0]['id']
|
|
# Save function being mocked.
|
|
real_func = self._plugin_class.remove_router_interface
|
|
plugin_instance = directory.get_plugin()
|
|
|
|
# Raise a NeutronException when removing metadata subnet
|
|
# from router.
|
|
def side_effect(*args):
|
|
if args[-1].get('subnet_id') == s['subnet']['id']:
|
|
# Do the real thing.
|
|
return real_func(plugin_instance, *args)
|
|
# Otherwise raise.
|
|
raise api_exc.NsxApiException()
|
|
|
|
with mock.patch.object(self._plugin_class,
|
|
'remove_router_interface',
|
|
side_effect=side_effect):
|
|
self._router_interface_action('remove', r['router']['id'],
|
|
s['subnet']['id'], None)
|
|
# Metadata network and subnet should still be there.
|
|
self._show('networks', meta_net_id,
|
|
webob.exc.HTTPOk.code)
|
|
self._show('ports', meta_port_id,
|
|
webob.exc.HTTPOk.code)
|
|
self._metadata_teardown()
|
|
|
|
def test_metadata_network_with_update_subnet_dhcp_enable(self):
|
|
self._metadata_setup(on_demand=True)
|
|
with self.router() as r:
|
|
# Create a DHCP-disabled subnet.
|
|
with self.subnet(enable_dhcp=False) as s:
|
|
self._router_interface_action('add', r['router']['id'],
|
|
s['subnet']['id'], None)
|
|
meta_net_id, meta_sub_id, meta_port_id = self._check_metadata(
|
|
expected_subnets=2, expected_ports=1)
|
|
# Update subnet to DHCP-enabled.
|
|
data = {'subnet': {'enable_dhcp': True}}
|
|
req = self.new_update_request('subnets', data,
|
|
s['subnet']['id'])
|
|
res = self.deserialize(self.fmt, req.get_response(self.api))
|
|
self.assertEqual(True, res['subnet']['enable_dhcp'])
|
|
self._check_metadata(expected_subnets=1, expected_ports=0)
|
|
self._show('networks', meta_net_id,
|
|
webob.exc.HTTPNotFound.code)
|
|
self._show('ports', meta_port_id,
|
|
webob.exc.HTTPNotFound.code)
|
|
self._show('subnets', meta_sub_id,
|
|
webob.exc.HTTPNotFound.code)
|
|
self._metadata_teardown()
|
|
|
|
def test_metadata_network_with_update_subnet_dhcp_disable(self):
|
|
self._metadata_setup(on_demand=True)
|
|
with self.router() as r:
|
|
# Create a DHCP-enabled subnet.
|
|
with self.subnet(enable_dhcp=True) as s:
|
|
self._router_interface_action('add', r['router']['id'],
|
|
s['subnet']['id'], None)
|
|
self._check_metadata(expected_subnets=1, expected_ports=0)
|
|
# Update subnet to DHCP-disabled.
|
|
data = {'subnet': {'enable_dhcp': False}}
|
|
req = self.new_update_request('subnets', data,
|
|
s['subnet']['id'])
|
|
res = self.deserialize(self.fmt, req.get_response(self.api))
|
|
self.assertEqual(False, res['subnet']['enable_dhcp'])
|
|
meta_net_id, meta_sub_id, meta_port_id = self._check_metadata(
|
|
expected_subnets=2, expected_ports=1)
|
|
self._show('networks', meta_net_id,
|
|
webob.exc.HTTPOk.code)
|
|
self._show('ports', meta_port_id,
|
|
webob.exc.HTTPOk.code)
|
|
self._show('subnets', meta_sub_id,
|
|
webob.exc.HTTPOk.code)
|
|
self._metadata_teardown()
|
|
|
|
def test_metadata_dhcp_host_route(self):
|
|
self._metadata_setup(config.MetadataModes.INDIRECT)
|
|
subnets = self._list('subnets')['subnets']
|
|
with self.subnet() as s:
|
|
with self.port(subnet=s, device_id='1234',
|
|
device_owner=constants.DEVICE_OWNER_DHCP) as port:
|
|
subnets = self._list('subnets')['subnets']
|
|
self.assertEqual(len(subnets), 1)
|
|
subnet_ip_net = netaddr.IPNetwork(s['subnet']['cidr'])
|
|
self.assertIn(netaddr.IPAddress(
|
|
subnets[0]['host_routes'][0]['nexthop']),
|
|
subnet_ip_net)
|
|
self.assertEqual(subnets[0]['host_routes'][0]['destination'],
|
|
'169.254.169.254/32')
|
|
self._delete('ports', port['port']['id'])
|
|
subnets = self._list('subnets')['subnets']
|
|
# Test that route is deleted after dhcp port is removed.
|
|
self.assertEqual(len(subnets[0]['host_routes']), 0)
|
|
self._metadata_teardown()
|