Fix IP allocation on shared networks ports.
Fixes bug 1037589 Ensure non-owner ports on shared networks get an IP address for each subnet configured on the shared network. Change-Id: Ib687444b7ad75f3c773b0cb41c25ac3f700fbf0f
This commit is contained in:
parent
4cb85bf3fd
commit
b7f5f8e2fa
@ -275,6 +275,10 @@ RESOURCE_ATTRIBUTE_MAP = {
|
|||||||
'convert_to': convert_to_boolean,
|
'convert_to': convert_to_boolean,
|
||||||
'validate': {'type:boolean': None},
|
'validate': {'type:boolean': None},
|
||||||
'is_visible': True},
|
'is_visible': True},
|
||||||
|
SHARED: {'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'default': False,
|
||||||
|
'is_visible': False},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,6 +142,13 @@ class QuantumDbPluginV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
|
|||||||
except exc.NoResultFound:
|
except exc.NoResultFound:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
def _get_subnets_by_network(self, context, network_id):
|
||||||
|
try:
|
||||||
|
subnet_qry = context.session.query(models_v2.Subnet)
|
||||||
|
return subnet_qry.filter_by(network_id=network_id).all()
|
||||||
|
except exc.NoResultFound:
|
||||||
|
return []
|
||||||
|
|
||||||
def _fields(self, resource, fields):
|
def _fields(self, resource, fields):
|
||||||
if fields:
|
if fields:
|
||||||
return dict(((key, item) for key, item in resource.iteritems()
|
return dict(((key, item) for key, item in resource.iteritems()
|
||||||
@ -769,6 +776,10 @@ class QuantumDbPluginV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
|
|||||||
if 'shared' in n:
|
if 'shared' in n:
|
||||||
self._validate_shared_update(context, id, network, n)
|
self._validate_shared_update(context, id, network, n)
|
||||||
network.update(n)
|
network.update(n)
|
||||||
|
# also update shared in all the subnets for this network
|
||||||
|
subnets = self._get_subnets_by_network(context, id)
|
||||||
|
for subnet in subnets:
|
||||||
|
subnet['shared'] = network['shared']
|
||||||
return self._make_network_dict(network)
|
return self._make_network_dict(network)
|
||||||
|
|
||||||
def delete_network(self, context, id):
|
def delete_network(self, context, id):
|
||||||
@ -836,6 +847,8 @@ class QuantumDbPluginV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
|
|||||||
with context.session.begin(subtransactions=True):
|
with context.session.begin(subtransactions=True):
|
||||||
network = self._get_network(context, s["network_id"])
|
network = self._get_network(context, s["network_id"])
|
||||||
self._validate_subnet_cidr(network, s['cidr'])
|
self._validate_subnet_cidr(network, s['cidr'])
|
||||||
|
# The 'shared' attribute for subnets is for internal plugin
|
||||||
|
# use only. It is not exposed through the API
|
||||||
subnet = models_v2.Subnet(tenant_id=tenant_id,
|
subnet = models_v2.Subnet(tenant_id=tenant_id,
|
||||||
id=s.get('id') or utils.str_uuid(),
|
id=s.get('id') or utils.str_uuid(),
|
||||||
name=s['name'],
|
name=s['name'],
|
||||||
@ -843,7 +856,8 @@ class QuantumDbPluginV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
|
|||||||
ip_version=s['ip_version'],
|
ip_version=s['ip_version'],
|
||||||
cidr=s['cidr'],
|
cidr=s['cidr'],
|
||||||
enable_dhcp=s['enable_dhcp'],
|
enable_dhcp=s['enable_dhcp'],
|
||||||
gateway_ip=s['gateway_ip'])
|
gateway_ip=s['gateway_ip'],
|
||||||
|
shared=network.shared)
|
||||||
|
|
||||||
# perform allocate pools first, since it might raise an error
|
# perform allocate pools first, since it might raise an error
|
||||||
pools = self._allocate_pools_for_subnet(context, s)
|
pools = self._allocate_pools_for_subnet(context, s)
|
||||||
|
@ -140,6 +140,7 @@ class Subnet(model_base.BASEV2, HasId, HasTenant):
|
|||||||
routes = orm.relationship(Route,
|
routes = orm.relationship(Route,
|
||||||
backref='subnet',
|
backref='subnet',
|
||||||
cascade='delete')
|
cascade='delete')
|
||||||
|
shared = sa.Column(sa.Boolean)
|
||||||
|
|
||||||
|
|
||||||
class Network(model_base.BASEV2, HasId, HasTenant):
|
class Network(model_base.BASEV2, HasId, HasTenant):
|
||||||
|
@ -200,7 +200,8 @@ class QuantumDbPluginV2TestCase(unittest2.TestCase):
|
|||||||
for arg in ('allocation_pools',
|
for arg in ('allocation_pools',
|
||||||
'ip_version', 'tenant_id',
|
'ip_version', 'tenant_id',
|
||||||
'enable_dhcp', 'allocation_pools',
|
'enable_dhcp', 'allocation_pools',
|
||||||
'dns_nameservers', 'host_routes'):
|
'dns_nameservers', 'host_routes',
|
||||||
|
'shared'):
|
||||||
# Arg must be present and not null (but can be false)
|
# Arg must be present and not null (but can be false)
|
||||||
if arg in kwargs and kwargs[arg] is not None:
|
if arg in kwargs and kwargs[arg] is not None:
|
||||||
data['subnet'][arg] = kwargs[arg]
|
data['subnet'][arg] = kwargs[arg]
|
||||||
@ -281,7 +282,7 @@ class QuantumDbPluginV2TestCase(unittest2.TestCase):
|
|||||||
|
|
||||||
def _make_subnet(self, fmt, network, gateway, cidr,
|
def _make_subnet(self, fmt, network, gateway, cidr,
|
||||||
allocation_pools=None, ip_version=4, enable_dhcp=True,
|
allocation_pools=None, ip_version=4, enable_dhcp=True,
|
||||||
dns_nameservers=None, host_routes=None):
|
dns_nameservers=None, host_routes=None, shared=None):
|
||||||
res = self._create_subnet(fmt,
|
res = self._create_subnet(fmt,
|
||||||
net_id=network['network']['id'],
|
net_id=network['network']['id'],
|
||||||
cidr=cidr,
|
cidr=cidr,
|
||||||
@ -291,7 +292,8 @@ class QuantumDbPluginV2TestCase(unittest2.TestCase):
|
|||||||
ip_version=ip_version,
|
ip_version=ip_version,
|
||||||
enable_dhcp=enable_dhcp,
|
enable_dhcp=enable_dhcp,
|
||||||
dns_nameservers=dns_nameservers,
|
dns_nameservers=dns_nameservers,
|
||||||
host_routes=host_routes)
|
host_routes=host_routes,
|
||||||
|
shared=shared)
|
||||||
# Things can go wrong - raise HTTP exc with res code only
|
# Things can go wrong - raise HTTP exc with res code only
|
||||||
# so it can be caught by unit tests
|
# so it can be caught by unit tests
|
||||||
if res.status_int >= 400:
|
if res.status_int >= 400:
|
||||||
@ -384,7 +386,8 @@ class QuantumDbPluginV2TestCase(unittest2.TestCase):
|
|||||||
allocation_pools=None,
|
allocation_pools=None,
|
||||||
enable_dhcp=True,
|
enable_dhcp=True,
|
||||||
dns_nameservers=None,
|
dns_nameservers=None,
|
||||||
host_routes=None):
|
host_routes=None,
|
||||||
|
shared=None):
|
||||||
# TODO(anyone) DRY this
|
# TODO(anyone) DRY this
|
||||||
# NOTE(salvatore-orlando): we can pass the network object
|
# NOTE(salvatore-orlando): we can pass the network object
|
||||||
# to gen function anyway, and then avoid the repetition
|
# to gen function anyway, and then avoid the repetition
|
||||||
@ -398,7 +401,8 @@ class QuantumDbPluginV2TestCase(unittest2.TestCase):
|
|||||||
ip_version,
|
ip_version,
|
||||||
enable_dhcp,
|
enable_dhcp,
|
||||||
dns_nameservers,
|
dns_nameservers,
|
||||||
host_routes)
|
host_routes,
|
||||||
|
shared=shared)
|
||||||
yield subnet
|
yield subnet
|
||||||
self._delete('subnets', subnet['subnet']['id'])
|
self._delete('subnets', subnet['subnet']['id'])
|
||||||
else:
|
else:
|
||||||
@ -410,7 +414,8 @@ class QuantumDbPluginV2TestCase(unittest2.TestCase):
|
|||||||
ip_version,
|
ip_version,
|
||||||
enable_dhcp,
|
enable_dhcp,
|
||||||
dns_nameservers,
|
dns_nameservers,
|
||||||
host_routes)
|
host_routes,
|
||||||
|
shared=shared)
|
||||||
yield subnet
|
yield subnet
|
||||||
self._delete('subnets', subnet['subnet']['id'])
|
self._delete('subnets', subnet['subnet']['id'])
|
||||||
|
|
||||||
@ -573,6 +578,23 @@ class TestPortsV2(QuantumDbPluginV2TestCase):
|
|||||||
self.assertTrue('mac_address' in port['port'])
|
self.assertTrue('mac_address' in port['port'])
|
||||||
self._delete('ports', port['port']['id'])
|
self._delete('ports', port['port']['id'])
|
||||||
|
|
||||||
|
def test_create_port_public_network_with_ip(self):
|
||||||
|
with self.network(shared=True) as network:
|
||||||
|
with self.subnet(network=network, cidr='10.0.0.0/24') as subnet:
|
||||||
|
keys = [('admin_state_up', True), ('status', 'ACTIVE'),
|
||||||
|
('fixed_ips', [{'subnet_id': subnet['subnet']['id'],
|
||||||
|
'ip_address': '10.0.0.2'}])]
|
||||||
|
port_res = self._create_port('json',
|
||||||
|
network['network']['id'],
|
||||||
|
201,
|
||||||
|
tenant_id='another_tenant',
|
||||||
|
set_context=True)
|
||||||
|
port = self.deserialize('json', port_res)
|
||||||
|
for k, v in keys:
|
||||||
|
self.assertEquals(port['port'][k], v)
|
||||||
|
self.assertTrue('mac_address' in port['port'])
|
||||||
|
self._delete('ports', port['port']['id'])
|
||||||
|
|
||||||
def test_create_ports_bulk_native(self):
|
def test_create_ports_bulk_native(self):
|
||||||
if self._skip_native_bulk:
|
if self._skip_native_bulk:
|
||||||
self.skipTest("Plugin does not support native bulk port create")
|
self.skipTest("Plugin does not support native bulk port create")
|
||||||
@ -1249,6 +1271,22 @@ class TestNetworksV2(QuantumDbPluginV2TestCase):
|
|||||||
res = self.deserialize('json', req.get_response(self.api))
|
res = self.deserialize('json', req.get_response(self.api))
|
||||||
self.assertTrue(res['network']['shared'])
|
self.assertTrue(res['network']['shared'])
|
||||||
|
|
||||||
|
def test_update_network_with_subnet_set_shared(self):
|
||||||
|
with self.network(shared=False) as network:
|
||||||
|
with self.subnet(network=network) as subnet:
|
||||||
|
data = {'network': {'shared': True}}
|
||||||
|
req = self.new_update_request('networks',
|
||||||
|
data,
|
||||||
|
network['network']['id'])
|
||||||
|
res = self.deserialize('json', req.get_response(self.api))
|
||||||
|
self.assertTrue(res['network']['shared'])
|
||||||
|
# must query db to see whether subnet's shared attribute
|
||||||
|
# has been updated or not
|
||||||
|
ctx = context.Context('', '', is_admin=True)
|
||||||
|
subnet_db = QuantumManager.get_plugin()._get_subnet(
|
||||||
|
ctx, subnet['subnet']['id'])
|
||||||
|
self.assertEqual(subnet_db['shared'], True)
|
||||||
|
|
||||||
def test_update_network_set_not_shared_single_tenant(self):
|
def test_update_network_set_not_shared_single_tenant(self):
|
||||||
with self.network(shared=True) as network:
|
with self.network(shared=True) as network:
|
||||||
res1 = self._create_port('json',
|
res1 = self._create_port('json',
|
||||||
@ -1751,6 +1789,13 @@ class TestSubnetsV2(QuantumDbPluginV2TestCase):
|
|||||||
allocation_pools=allocation_pools)
|
allocation_pools=allocation_pools)
|
||||||
self.assertEquals(ctx_manager.exception.code, 400)
|
self.assertEquals(ctx_manager.exception.code, 400)
|
||||||
|
|
||||||
|
def test_create_subnet_shared_returns_422(self):
|
||||||
|
cidr = '10.0.0.0/24'
|
||||||
|
with self.assertRaises(webob.exc.HTTPClientError) as ctx_manager:
|
||||||
|
self._test_create_subnet(cidr=cidr,
|
||||||
|
shared=True)
|
||||||
|
self.assertEquals(ctx_manager.exception.code, 422)
|
||||||
|
|
||||||
def test_update_subnet(self):
|
def test_update_subnet(self):
|
||||||
with self.subnet() as subnet:
|
with self.subnet() as subnet:
|
||||||
data = {'subnet': {'gateway_ip': '11.0.0.1'}}
|
data = {'subnet': {'gateway_ip': '11.0.0.1'}}
|
||||||
@ -1760,6 +1805,15 @@ class TestSubnetsV2(QuantumDbPluginV2TestCase):
|
|||||||
self.assertEqual(res['subnet']['gateway_ip'],
|
self.assertEqual(res['subnet']['gateway_ip'],
|
||||||
data['subnet']['gateway_ip'])
|
data['subnet']['gateway_ip'])
|
||||||
|
|
||||||
|
def test_update_subnet_shared_returns_422(self):
|
||||||
|
with self.network(shared=True) as network:
|
||||||
|
with self.subnet(network=network) as subnet:
|
||||||
|
data = {'subnet': {'shared': True}}
|
||||||
|
req = self.new_update_request('subnets', data,
|
||||||
|
subnet['subnet']['id'])
|
||||||
|
res = req.get_response(self.api)
|
||||||
|
self.assertEqual(res.status_int, 422)
|
||||||
|
|
||||||
def test_show_subnet(self):
|
def test_show_subnet(self):
|
||||||
with self.network() as network:
|
with self.network() as network:
|
||||||
with self.subnet(network=network) as subnet:
|
with self.subnet(network=network) as subnet:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user