Merge "Add address scope to floating IPs in RPC response to L3 agent"
This commit is contained in:
commit
bf9e4133eb
@ -100,8 +100,9 @@ Routing
|
||||
|
||||
The reference implementation honors address scopes. Within an address scope,
|
||||
addresses route freely (barring any FW rules or other external restrictions).
|
||||
Between scopes, routed is prevented unless address translation is used. Future
|
||||
patches will expand on this.
|
||||
Between scopes, routed is prevented unless address translation is used. For
|
||||
now, floating IPs are the only place where traffic crosses scope boundaries.
|
||||
The 1-1 NAT allows this to happen.
|
||||
|
||||
.. TODO (Carl) Implement NAT for floating ips crossing scopes
|
||||
.. TODO (Carl) Implement SNAT for crossing scopes
|
||||
@ -135,6 +136,23 @@ Here is an example of how the json will look in the context of a router port::
|
||||
"6": null
|
||||
},
|
||||
|
||||
To implement floating IPs crossing scope boundaries, the L3 agent needs to know
|
||||
the target scope of the floating ip. The fixed address is not enough to
|
||||
disambiguate because, theoritically, there could be overlapping addresses from
|
||||
different scopes. The scope is computed [#]_ from the floating ip fixed port
|
||||
and attached to the floating ip dict under the 'fixed_ip_address_scope'
|
||||
attribute. Here's what the json looks like (trimmed)::
|
||||
|
||||
{
|
||||
...
|
||||
"floating_ip_address": "172.24.4.4",
|
||||
"fixed_ip_address": "172.16.0.3",
|
||||
"fixed_ip_address_scope": "d010a0ea-660e-4df4-86ca-ae2ed96da5c1",
|
||||
...
|
||||
}
|
||||
|
||||
.. [#] neutron/db/l3_db.py (_get_sync_floating_ips)
|
||||
|
||||
Model
|
||||
~~~~~
|
||||
|
||||
|
@ -82,6 +82,7 @@ class L3PluginApi(object):
|
||||
1.6 - Added process_prefix_update
|
||||
1.7 - DVR support: new L3 plugin methods added.
|
||||
- delete_agent_gateway_port
|
||||
1.8 - Added address scope information
|
||||
"""
|
||||
|
||||
def __init__(self, topic, host):
|
||||
|
@ -45,7 +45,8 @@ class L3RpcCallback(object):
|
||||
# 1.5 Added update_ha_routers_states
|
||||
# 1.6 Added process_prefix_update to support IPv6 Prefix Delegation
|
||||
# 1.7 Added method delete_agent_gateway_port for DVR Routers
|
||||
target = oslo_messaging.Target(version='1.7')
|
||||
# 1.8 Added address scope information
|
||||
target = oslo_messaging.Target(version='1.8')
|
||||
|
||||
@property
|
||||
def plugin(self):
|
||||
|
@ -12,6 +12,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import itertools
|
||||
import netaddr
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import uuidutils
|
||||
@ -1182,11 +1183,52 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
|
||||
if r.get('gw_port'))
|
||||
return self._build_routers_list(context, router_dicts, gw_ports)
|
||||
|
||||
@staticmethod
|
||||
def _unique_floatingip_iterator(query):
|
||||
"""Iterates over only one row per floating ip. Ignores others."""
|
||||
# Group rows by fip id. They must be sorted by same.
|
||||
q = query.order_by(FloatingIP.id)
|
||||
keyfunc = lambda row: row[0]['id']
|
||||
group_iterator = itertools.groupby(q, keyfunc)
|
||||
|
||||
# Just hit the first row of each group
|
||||
for key, value in group_iterator:
|
||||
yield six.next(value)
|
||||
|
||||
def _make_floatingip_dict_with_scope(self, floatingip_db, scope_id):
|
||||
d = self._make_floatingip_dict(floatingip_db)
|
||||
d['fixed_ip_address_scope'] = scope_id
|
||||
return d
|
||||
|
||||
def _get_sync_floating_ips(self, context, router_ids):
|
||||
"""Query floating_ips that relate to list of router_ids."""
|
||||
"""Query floating_ips that relate to list of router_ids with scope.
|
||||
|
||||
This is different than the regular get_floatingips in that it finds the
|
||||
address scope of the fixed IP. The router needs to know this to
|
||||
distinguish it from other scopes.
|
||||
|
||||
There are a few redirections to go through to discover the address
|
||||
scope from the floating ip.
|
||||
"""
|
||||
if not router_ids:
|
||||
return []
|
||||
return self.get_floatingips(context, {'router_id': router_ids})
|
||||
|
||||
query = context.session.query(FloatingIP,
|
||||
models_v2.SubnetPool.address_scope_id)
|
||||
query = query.join(models_v2.Port,
|
||||
FloatingIP.fixed_port_id == models_v2.Port.id)
|
||||
# Outer join of Subnet can cause each ip to have more than one row.
|
||||
query = query.outerjoin(models_v2.Subnet,
|
||||
models_v2.Subnet.network_id == models_v2.Port.network_id)
|
||||
query = query.filter(models_v2.Subnet.ip_version == 4)
|
||||
query = query.outerjoin(models_v2.SubnetPool,
|
||||
models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id)
|
||||
|
||||
# Filter out on router_ids
|
||||
query = query.filter(FloatingIP.router_id.in_(router_ids))
|
||||
|
||||
return [self._make_floatingip_dict_with_scope(*row)
|
||||
for row in self._unique_floatingip_iterator(query)]
|
||||
|
||||
def _get_sync_interfaces(self, context, router_ids, device_owners=None):
|
||||
"""Query router interfaces that relate to list of router_ids."""
|
||||
|
@ -96,3 +96,37 @@ class TestL3_NAT_dbonly_mixin(base.BaseTestCase):
|
||||
'network_id': 'net_id',
|
||||
'subnets': [{k: subnet[k] for k in keys}],
|
||||
'address_scopes': address_scopes}], ports)
|
||||
|
||||
def test__get_sync_floating_ips_no_query(self):
|
||||
"""Basic test that no query is performed if no router ids are passed"""
|
||||
db = l3_db.L3_NAT_dbonly_mixin()
|
||||
context = mock.Mock()
|
||||
db._get_sync_floating_ips(context, [])
|
||||
self.assertFalse(context.session.query.called)
|
||||
|
||||
@mock.patch.object(l3_db.L3_NAT_dbonly_mixin, '_make_floatingip_dict')
|
||||
def test__make_floatingip_dict_with_scope(self, make_fip_dict):
|
||||
db = l3_db.L3_NAT_dbonly_mixin()
|
||||
make_fip_dict.return_value = {'id': mock.sentinel.fip_ip}
|
||||
result = db._make_floatingip_dict_with_scope(
|
||||
mock.sentinel.floating_ip_db, mock.sentinel.address_scope_id)
|
||||
self.assertEqual({
|
||||
'fixed_ip_address_scope': mock.sentinel.address_scope_id,
|
||||
'id': mock.sentinel.fip_ip}, result)
|
||||
|
||||
def test__unique_floatingip_iterator(self):
|
||||
query = mock.MagicMock()
|
||||
query.order_by().__iter__.return_value = [
|
||||
({'id': 'id1'}, 'scope1'),
|
||||
({'id': 'id1'}, 'scope1'),
|
||||
({'id': 'id2'}, 'scope2'),
|
||||
({'id': 'id2'}, 'scope2'),
|
||||
({'id': 'id2'}, 'scope2'),
|
||||
({'id': 'id3'}, 'scope3')]
|
||||
query.reset_mock()
|
||||
result = list(
|
||||
l3_db.L3_NAT_dbonly_mixin._unique_floatingip_iterator(query))
|
||||
query.order_by.assert_called_once_with(l3_db.FloatingIP.id)
|
||||
self.assertEqual([({'id': 'id1'}, 'scope1'),
|
||||
({'id': 'id2'}, 'scope2'),
|
||||
({'id': 'id3'}, 'scope3')], result)
|
||||
|
Loading…
x
Reference in New Issue
Block a user