Dual-stack L3-Policy mapping

The initial spec for address scope and subnetpool support mentioned
support for dual-stack. This commit extends the existing spec to
define that support.

Change-Id: I0966d200c75c1d571a15687062020528e97648da
Partially-implements: blueprint address-scope-mapping
This commit is contained in:
Thomas Bachman 2017-04-20 16:03:03 +00:00
parent 449643f905
commit 6134fb3ffc
1 changed files with 184 additions and 43 deletions

View File

@ -30,50 +30,104 @@ Proposed change
The current mapping of GBP l3_policy to a list of Neutron routers will be
augmented with the mapping to a Neutron address_scope (one each for IPv4 and
IPv6) and subnetpools. This spec attempts to lay the foundation for the use
of IPv6, and dual-stack (both IPv4 and IPv6) in the l3_policy. The API and DB
model constructs to support this are being addressed in this spec. The details
for IPv6 implementation are not addresed in this spec and will be laid out in
a forthcoming complementary spec.
of IPv6 and dual-stack (both IPv4 and IPv6) in the l3_policy. The API and DB
model constructs to support this are addressed in this spec.
For each address family (IPv4 and/or IPv6) the l3_policy supports, there will
be a 1-1 mapping between GBP l3_policy and the Neutron address_scope,
and 1-many mapping between GBP l3_policy and Neutron subnetpools. The 1-many
The ip_version attribute of GBP's l3_policy is extended to allow specifying
dual-stack support. Currently allowed values for ip_version are 4 for IPv4
and 6 for IPv6. The value 46 may now be used to enable dual-stack support.
For each address family (IPv4 and/or IPv6) indicated by an l3_policy, there
will be a 1-1 mapping between GBP l3_policy and a Neutron address_scope,
and a 1-many mapping between GBP l3_policy and Neutron subnetpools. The 1-many
mapping allows for use cases where the l3_policy (and address_scope) is shared
across tenants, while the subnetpool is created per tenant and not shared.
The subnetpools that are explicitly associated with the l3_policy, need to
belong to the same address_scope that is associated with that l3_policy. GBP
The ip_pool parameter now allows comma-delimited prefixes, as well just a
single prefix. CIDRs from different address families may be used together
in the same ip_pool parameter. The ip_pool parameter may also be None,
which is intended for use with the default subnetpool Neutron extension.
The default subnetpool Neutron extension is used to control the desired behavior
when an implicit workflow is used on L3 policies with mapping drivers [#]_.
Default subnetpools are only used when the ip_version requests a given address
family (IPv4 or IPv6) and the user hasn't explicitly provided any CIDRs for
that address family (e.g. via the ip_pool parameter or a subnetpool).
Within each indicated address family, the subnetpools that are explicitly
associated with the l3_policy must all belong to the same address_scope. GBP
will allocate subnets only from the subnetpools that are associated with the
corresponding l3_policy. The list of subnets associated with the l3_policy can
be updated by adding or removing subnetpools (subnetpools which are currently
being used cannot be removed).
being used cannot be removed), or by updating the prefixes attribute of
subnetpools already associated with the l3_policy
The definition of the subnet_prefix_length is modified to refine it's use.
The subnet_prefix_length now only applies to IPv4 prefixes. It also is
used as the default subnet prefix length for all subnets allocated from
IPv4 subnetpools for the given l3_policy. In order to help backward-
compatibilty, values longer than 30 will now be ignored. IPv6 subnets
that are allocated will always use a prefix length of 64.
Two properties of address scopes are address namespace and routability.
Address namespace simply means that addresses are guaranteed to be unique
within an address scope. Routability means that a neutron router may only
forward between interfaces that have subnets which were allocated from
subnetpools with the same address scope (note: forwarding is still possible
via NAT when they aren't in the same scope). It's worth pointing out that
since address scopes can span multiple l3_policies, the address uniqueness is
applied across the l3_policies. However, even though sharing a common address
scope implies routability across l3_policies, actual connectivity between
l3_policies is not implied because each l3_policy maps to a distinct neutron
router. In GBP, connectivity between l3_policies remains subject to a PTG
providing a PRS that is consumed by an external_policy and a PTG in the
other l3_policy consuming that external_policy.
Let's consider the various combinations that are possible based on the choice
of implicit and explicit arguments.
#. A l3_policy is created with an ip_pool, ip_version, and
subnet_prefix_length but no address_scope or subnetpool(s) are provided. In
this case an address_scope with the address family of the ip_version will be
created implicitly, and a subnetpool with the ip_pool CIDR will be created
in that address_scope. If the l3_policy is shared, the implcitily create
address_scope and subnetpool will also be shared. The ip_version of the
l3_policy will be used for the address_scope and the subnetpool. The
default_prefixlen, will be set to the subnet_prefix_length of the l3_policy.
(The min_prefixlen, and max_prefixlen of the subnetpool will default to 8
and 32 as defined in Neutron.)
this case an address_scope for each address family specified by ip_version
is created, and a subnetpool with prefixes for each CIDR for that address
family found in ip_pool is created in that address_scope. If the l3_policy
is shared, the implcitily created address_scope and subnetpool will also be
shared. The default_prefixlen for IPv4 subnetpools will be set to the
subnet_prefix_length of the l3_policy, and for IPv6 subnetpools it will be
set to 64 (the min_prefixlen and max_prefixlen of the IPv4 subnetpool will
default to 8 and 32, and to 64 and 128 for the IPv6 subnetpool, as defined
in Neutron).
#. A l3_policy is created with an ip_pool, ip_version, subnet_prefix_length,
and address_scope, but no subnetpools are provided. In this case the
ip_version of the l3_policy is ignored, and the subnetpool is created in
this address_scope as before. If both, v4 and v6, address_scopes are
provided, a subnetpool will be created for each family. If a subnetpool
already exists in this address_scope, and overlaps with the CIDR of the
ip_pool, an exception will be raised.
#. A l3_policy is created with an ip_version and subnet_prefix_length
(IPv4 only), but no address_scope, subnetpool(s), or prefix in ip_pool
are provided for that address family. There are two possibilities in this
scenario:
#. A l3_policy is created with an address_scope, and subnetpool(s). Since the
ip_version is already defined for the address_scope and the CIDR and prefix
lengths are defined for the subnetpool(s), the ip_version, ip_pool and
subnet_prefix_length of the l3_policy will be ignored if provided.
* A default subnetpool (defined in neutron) exists for an address family
indicated by ip_version. In this case the default subnetpool and its
address scope are associated with the l3_policy.
* A default subnetpool does not exist for an address family indicated
by ip_version. In this case, an exception is raised.
#. A l3_policy is created with an address_scope of the indicated address
family, but no subnetpools of that address family are provided. If there
are CIDRs in ip_pool from the same address family, then they are used as
prefixes to create a new subnetpool within the specified address scope.
If a subnetpool already exists in this address_scope, and overlaps with
the CIDR of the ip_pool, an exception will be raised. If no CIDRs from the
same address family are found, a check is made to see if a default
subnetpool sharing the same address scope exists. If it does, then that
subnetpool is associated with the l3_policy. If none exists, then an
exception is raised.
#. A l3_policy is created with an address_scope and subnetpool(s). Since the
supplied subnetpool(s) define how subnets will be allocated, any supplied
ip_pool for that address family is ignored. The ip_pool value returned
from any l3_policy API call will contain the set of prefixes used for
allocating subnets for all address families specified in ip_version. In
this case, that will be the aggregate prefixes from the explicitly
passed subnetpools.
#. A l3_policy is created with one or more subnetpools. The address_scope
associated with the subnetpool(s) is associated with the l3_policy. If
@ -87,9 +141,9 @@ In all of the above cases, if subnetpools are associated with an address_scope
that is associated with a l3_policy, they will be considered for subnet
allocation only if they are explicitly associated with the l3_policy.
A minor variation of all the above cases is the one where either or all of
ip_pool, ip_version, and subnet_prefix_length are not provided. In such cases
the GBP defaults for these attributes will be used (but overridden by the
A minor variation of all the above cases is the one where either or both of
the ip_pool and subnet_prefix_length are not provided. In such cases the GBP
defaults for these attributes will be used (but overridden by the
corresponding attributes in the address_scope and subnetpools if any of those
are explicitly provided).
@ -105,16 +159,8 @@ subnetpool was implicitly created by GBP.
It should be noted that the ip_pool, subnet_prefix_length, and ip_version
attributes of the l3_policy may only have effect at the l3_policy creation
time. In the body of response for GET l3_policy call, the ip_pool will be set
to a comma separated string consisting of a list of CIDRs corresponding to each
subnetpool currently present in the address_scope. To preserve API backward
compatibility (i.e. to not immediately break existing clients and integration
tests), the subnet_prefix_length and ip_version will be set to the
corresponding subnet_prefix_length and ip_version when only one CIDR is
present. If more than one CIDR is present, these will be set to null. In the
case where multiple CIDRs are present, more details like the prefix length of
the subnets that are drawn from the subnetpools corresponding to these CIDRs
can be obtained by navigating the resource relationship from l3_policy to
address_scope to subnetpool.
to a comma delimited string consisting of a list of CIDRs made up of the
prefixes attribute values of all subnetpools associated with the l3_policy.
Data model impact
@ -152,7 +198,7 @@ backward incompatible DB change and a script will be provided to migrate data
from existing deployments to this new structure. The script will essentially
create an address_scope and subnetpool for each existing l3_policy.
In addition, additional tables will be added to track the Neutron address_scope
Additional tables will be added to track the Neutron address_scope
and subnetpool resources created by GBP.
::
@ -177,6 +223,43 @@ and subnetpool resources created by GBP.
nullable=False, primary_key=True)
The size of the ip_pool column in the L3Policy table is increased from
64 to 256 in order to account for more than a single prefix. Once all
policy drivers use subnetpools, this column could be removed from
the DB.
::
class L3Policy(model_base.BASEV2, BaseSharedGbpResource):
"""Represents a L3 Policy with a non-overlapping IP address space."""
__tablename__ = 'gp_l3_policies'
type = sa.Column(sa.String(15))
__mapper_args__ = {
'polymorphic_on': type,
'polymorphic_identity': 'base'
}
ip_version = sa.Column(sa.Integer, nullable=False)
ip_pool = sa.Column(sa.String(256))
subnet_prefix_length = sa.Column(sa.Integer)
l2_policies = orm.relationship(L2Policy, backref='l3_policy')
external_segments = orm.relationship(
ESToL3PAssociation, backref='l3_policies',
cascade='all, delete-orphan')
A similar increase (64 to 256) is needed to the proxy_ip_pool parameter in the
DB for that extension, as well as the ability to make the field nullable:
::
class ProxyIPPoolMapping(model_base.BASEV2):
__tablename__ = 'gp_proxy_ip_pool_mapping'
l3_policy_id = sa.Column(
sa.String(36), sa.ForeignKey('gp_l3_policies.id', ondelete="CASCADE"),
primary_key=True)
proxy_ip_pool = sa.Column(sa.String(256), nullable=True)
proxy_subnet_prefix_length = sa.Column(sa.Integer, nullable=False)
REST API impact
---------------
@ -204,6 +287,63 @@ extension definition
'is_visible': True, 'default': None},
},
In addition, the l3_policy itself needs modifications to support:
* the new value 46 for ip_version:
* a type change for ip_pool (subnet to string)
the defaultt value for ip_pool is defined in a new config file
variable. The variable has the same value as the old default, which
ensures backwards-compatibility, but also allows for the default
value to be something else (e.g. dual-stack prefixes, empty prefix,
IPv6 prefix only, etc.).
::
L3_POLICIES: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None}, 'is_visible': True,
'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:gbp_resource_name': None},
'default': '', 'is_visible': True},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True, 'is_visible': True},
'status': {'allow_post': False, 'allow_put': False,
'is_visible': True},
'status_details': {'allow_post': False, 'allow_put': False,
'is_visible': True},
'ip_version': {'allow_post': True, 'allow_put': False,
'convert_to': conv.convert_to_int,
'validate': {'type:values': [4, 6, 46]},
'default': 4, 'is_visible': True},
'ip_pool': {'allow_post': True, 'allow_put': False,
'validate': {'type:string_or_none': None},
'default': GBP_CONF.default_ip_pool, 'is_visible': True},
'subnet_prefix_length': {'allow_post': True, 'allow_put': True,
'convert_to': conv.convert_to_int,
# This parameter only applies to ipv4
# prefixes. For IPv4 legal values are
# 2 to 30. For ipv6, this parameter
# is ignored
'default': 24, 'is_visible': True},
'l2_policies': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid_list': None},
'convert_to': conv.convert_none_to_empty_list,
'default': None, 'is_visible': True},
attr.SHARED: {'allow_post': True, 'allow_put': True,
'default': False, 'convert_to': conv.convert_to_boolean,
'is_visible': True, 'required_by_policy': True,
'enforce_policy': True},
'external_segments': {
'allow_post': True, 'allow_put': True, 'default': None,
'validate': {'type:external_dict': None},
'convert_to': conv.convert_none_to_empty_dict, 'is_visible': True},
},
Security impact
---------------
@ -320,3 +460,4 @@ References
==========
.. [#] http://docs.openstack.org/developer/neutron/devref/address_scopes.html
.. [#] https://review.openstack.org/#/c/282021/