From 6134fb3ffc44312abf348943d2569a2028fe861b Mon Sep 17 00:00:00 2001 From: Thomas Bachman Date: Thu, 20 Apr 2017 16:03:03 +0000 Subject: [PATCH] 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 --- .../address-scope-subnet-pool-mapping.rst | 227 ++++++++++++++---- 1 file changed, 184 insertions(+), 43 deletions(-) diff --git a/specs/newton/address-scope-subnet-pool-mapping.rst b/specs/newton/address-scope-subnet-pool-mapping.rst index 01bcf9e..2569eed 100644 --- a/specs/newton/address-scope-subnet-pool-mapping.rst +++ b/specs/newton/address-scope-subnet-pool-mapping.rst @@ -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/