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:
Salvatore Orlando 2012-08-21 03:53:31 -07:00
parent 4cb85bf3fd
commit b7f5f8e2fa
4 changed files with 80 additions and 7 deletions

View File

@ -275,6 +275,10 @@ RESOURCE_ATTRIBUTE_MAP = {
'convert_to': convert_to_boolean,
'validate': {'type:boolean': None},
'is_visible': True},
SHARED: {'allow_post': False,
'allow_put': False,
'default': False,
'is_visible': False},
}
}

View File

@ -142,6 +142,13 @@ class QuantumDbPluginV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
except exc.NoResultFound:
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):
if fields:
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:
self._validate_shared_update(context, id, network, 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)
def delete_network(self, context, id):
@ -836,6 +847,8 @@ class QuantumDbPluginV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
with context.session.begin(subtransactions=True):
network = self._get_network(context, s["network_id"])
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,
id=s.get('id') or utils.str_uuid(),
name=s['name'],
@ -843,7 +856,8 @@ class QuantumDbPluginV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
ip_version=s['ip_version'],
cidr=s['cidr'],
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
pools = self._allocate_pools_for_subnet(context, s)

View File

@ -140,6 +140,7 @@ class Subnet(model_base.BASEV2, HasId, HasTenant):
routes = orm.relationship(Route,
backref='subnet',
cascade='delete')
shared = sa.Column(sa.Boolean)
class Network(model_base.BASEV2, HasId, HasTenant):

View File

@ -200,7 +200,8 @@ class QuantumDbPluginV2TestCase(unittest2.TestCase):
for arg in ('allocation_pools',
'ip_version', 'tenant_id',
'enable_dhcp', 'allocation_pools',
'dns_nameservers', 'host_routes'):
'dns_nameservers', 'host_routes',
'shared'):
# Arg must be present and not null (but can be false)
if arg in kwargs and kwargs[arg] is not None:
data['subnet'][arg] = kwargs[arg]
@ -281,7 +282,7 @@ class QuantumDbPluginV2TestCase(unittest2.TestCase):
def _make_subnet(self, fmt, network, gateway, cidr,
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,
net_id=network['network']['id'],
cidr=cidr,
@ -291,7 +292,8 @@ class QuantumDbPluginV2TestCase(unittest2.TestCase):
ip_version=ip_version,
enable_dhcp=enable_dhcp,
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
# so it can be caught by unit tests
if res.status_int >= 400:
@ -384,7 +386,8 @@ class QuantumDbPluginV2TestCase(unittest2.TestCase):
allocation_pools=None,
enable_dhcp=True,
dns_nameservers=None,
host_routes=None):
host_routes=None,
shared=None):
# TODO(anyone) DRY this
# NOTE(salvatore-orlando): we can pass the network object
# to gen function anyway, and then avoid the repetition
@ -398,7 +401,8 @@ class QuantumDbPluginV2TestCase(unittest2.TestCase):
ip_version,
enable_dhcp,
dns_nameservers,
host_routes)
host_routes,
shared=shared)
yield subnet
self._delete('subnets', subnet['subnet']['id'])
else:
@ -410,7 +414,8 @@ class QuantumDbPluginV2TestCase(unittest2.TestCase):
ip_version,
enable_dhcp,
dns_nameservers,
host_routes)
host_routes,
shared=shared)
yield subnet
self._delete('subnets', subnet['subnet']['id'])
@ -573,6 +578,23 @@ class TestPortsV2(QuantumDbPluginV2TestCase):
self.assertTrue('mac_address' in port['port'])
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):
if self._skip_native_bulk:
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))
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):
with self.network(shared=True) as network:
res1 = self._create_port('json',
@ -1751,6 +1789,13 @@ class TestSubnetsV2(QuantumDbPluginV2TestCase):
allocation_pools=allocation_pools)
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):
with self.subnet() as subnet:
data = {'subnet': {'gateway_ip': '11.0.0.1'}}
@ -1760,6 +1805,15 @@ class TestSubnetsV2(QuantumDbPluginV2TestCase):
self.assertEqual(res['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):
with self.network() as network:
with self.subnet(network=network) as subnet: