multi-ext-gw: api-def and api-ref

New router attribute:
    external_gateways

New router actions:
    PUT add_external_gateways
    PUT update_external_gateways
    PUT remove_external_gateways

Change-Id: I46381b70f770ccd32943644341388b09f7e14556
Partial-Bug: #1905295
Related-Change (spec): https://review.opendev.org/c/openstack/neutron-specs/+/779511
This commit is contained in:
Bence Romsics 2021-01-14 15:12:13 +01:00
parent 60046e4f2c
commit 75057767eb
16 changed files with 597 additions and 16 deletions

View File

@ -5399,6 +5399,12 @@ router-external_gateway_ports:
in: body
required: true
type: string
router-external_gateways:
description: |
The list of external gateways of the router.
in: body
required: true
type: array
router-flavor_id:
description: |
The ID of the flavor associated with the router.

View File

@ -68,6 +68,21 @@ modes, adds the ``external_gateway_info`` attribute to ``routers``
and allows definitions for ``network_id``, ``enable_snat`` and
``external_fixed_ips``.
L3 multiple external gateways extension (``multiple-external-gateways``)
========================================================================
The ``multiple-external-gateways`` extension allows a router to have
multiple external gateways, that is multiple legs towards the outside
world.
.. warning::
This API extension was merged as experimental to enable parallel
development of multiple backends. At the moment this API does not have
a reference implementation and should not be considered final. The
removal of this warning will mark when the reference implementation
gets merged and the feauture is ready to be consumed.
L3 flavors extension (``l3-flavors``)
=====================================
@ -178,6 +193,7 @@ Response Parameters
- admin_state_up: admin_state_up
- status: router-status
- external_gateway_info: router-external_gateway_info
- external_gateways: router-external_gateways
- revision_number: revision_number
- routes: router-routes
- destination: router-destination
@ -258,6 +274,7 @@ Response Parameters
- admin_state_up: admin_state_up
- status: router-status
- external_gateway_info: router-external_gateway_info
- external_gateways: router-external_gateways
- revision_number: revision_number
- routes: router-routes
- destination: router-destination
@ -318,6 +335,7 @@ Response Parameters
- admin_state_up: admin_state_up
- status: router-status
- external_gateway_info: router-external_gateway_info
- external_gateways: router-external_gateways
- revision_number: revision_number
- routes: router-routes
- destination: router-destination
@ -389,6 +407,7 @@ Response Parameters
- admin_state_up: admin_state_up
- status: router-status
- external_gateway_info: router-external_gateway_info
- external_gateways: router-external_gateways
- revision_number: revision_number
- routes: router-routes
- destination: router-destination
@ -748,3 +767,220 @@ Response Example
.. literalinclude:: samples/routers/router-remove-extraroutes-response.json
:language: javascript
Add external gateways to router
===============================
.. rest_method:: PUT /v2.0/routers/{router_id}/add_external_gateways
Add external gateways to router, beyond the external gateways the router
already has.
Adding an external gateway to a network that already has
one raises an error.
The add/update/remove external gateways operations extend the use of
``router.external_gateway_info`` to manage multiple external gateways.
The full set of external gateways is exposed in the read-only
``router.external_gateways`` parameter. ``router.external_gateways``
contains a list of ``external_gateway_info`` structures like:
::
[
{"network_id": ...,
"external_fixed_ips": [{"ip_address": ..., "subnet_id": ...}, ...],
"enable_snat": ...},
...
]
The first item (index 0) of the ``external_gateways`` list is special:
* It is always a duplicate of ``router.external_gateway_info``.
* This first item sets a router's default route. The other items have
no effect on it.
The order of the the rest of the list (indexes 1, 2, ...) is irrelevant
and ignored.
The first external gateway can be managed in two
ways: via ``router.external_gateway_info`` or via
``add/update/remove_external_gateways``. The other external gateways
can only be managed via ``add/update/remove_external_gateways``.
The format of the request body is the same as the format of the read-only
``router.external_gateways`` parameter, but wrapped as follows:
::
{"router": {"external_gateways": EXTERNAL-GATEWAY-LIST}}
The response codes and response body are the same as to the update of
the router. That is the whole router object is returned including the
``external_gateway_info`` and ``external_gateways`` parameters which
represents the result of the operation.
Changes in ``router.external_gateway_info`` are reflected
in ``router.external_gateways`` and vice versa. Updating
``external_gateway_info`` also updates the first element of
``external_gateways`` and it leaves the rest of ``external_gateways``
unchanged. Setting ``external_gateway_info`` to an empty value also
resets ``external_gateways`` to the empty list.
Normal response codes: 200
Error response codes: 400, 401, 404, 412
Request Parameters
------------------
.. rest_parameters:: parameters.yaml
- router_id: router_id
- external_gateways: router-external_gateways
Request Example
---------------
.. literalinclude:: samples/routers/router-add-external-gateways-request.json
:language: javascript
Response Parameters
-------------------
.. rest_parameters:: parameters.yaml
- id: router-id-body
- name: router_name
- external_gateways: router-external_gateways
Response Example
----------------
.. literalinclude:: samples/routers/router-add-external-gateways-response.json
:language: javascript
Update external gateways of router
==================================
.. rest_method:: PUT /v2.0/routers/{router_id}/update_external_gateways
Update some external gateways of router.
For general information on the add/update/remove external gateways
operations see ``add_external_gateways`` above.
The external gateways to be updated are identified by the ``network_ids``
found in the PUT request. The ``external_fixed_ips`` and ``enable_snat``
fields can be updated. The ``network_id`` field cannot be updated.
The format of the request body is the same as the format of the read-only
``router.external_gateways`` parameter, but wrapped as follows:
::
{"router": {"external_gateways": EXTERNAL-GATEWAY-LIST}}
If the whole ``external_fixed_ips`` or ``enable_snat`` fields are meant
to be left unchanged they can be omitted from the request.
The response codes and response body are the same as to the update of
the router. That is the whole router object is returned including the
``external_gateway_info`` and ``external_gateways`` parameters which
represents the result of the operation.
Please note that updating ``external_gateway_info`` also updates
the first element of ``external_gateways`` and it leaves the rest of
``external_gateways`` unchanged.
Normal response codes: 200
Error response codes: 400, 401, 404, 412
Request Parameters
------------------
.. rest_parameters:: parameters.yaml
- router_id: router_id
- external_gateways: router-external_gateways
Request Example
---------------
.. literalinclude:: samples/routers/router-update-external-gateways-request.json
:language: javascript
Response Parameters
-------------------
.. rest_parameters:: parameters.yaml
- id: router-id-body
- name: router_name
- external_gateways: router-external_gateways
Response Example
----------------
.. literalinclude:: samples/routers/router-update-external-gateways-response.json
:language: javascript
Remove external gateways from router
====================================
.. rest_method:: PUT /v2.0/routers/{router_id}/remove_external_gateways
Remove some external gateways from router.
For general information on the add/update/remove external gateways
operations see ``add_external_gateways`` above.
The format of the request body is the same as the format of the read-only
``router.external_gateways`` parameter, but wrapped as follows:
::
{"router": {"external_gateways": EXTERNAL-GATEWAY-LIST}}
However the request body can be partial. Only the ``network_id``
field from ``external_gateway_info`` structure is used. The
``external_fixed_ips`` and ``enable_snat`` keys can be present but their
values are ignored.
Please note that setting ``external_gateway_info`` to an empty value
also resets ``external_gateways`` to the empty list.
Normal response codes: 200
Error response codes: 400, 401, 404, 412
Request Parameters
------------------
.. rest_parameters:: parameters.yaml
- router_id: router_id
- external_gateways: router-external_gateways
Request Example
---------------
.. literalinclude:: samples/routers/router-remove-external-gateways-request.json
:language: javascript
Response Parameters
-------------------
.. rest_parameters:: parameters.yaml
- id: router-id-body
- name: router_name
- external_gateways: router-external_gateways
Response Example
----------------
.. literalinclude:: samples/routers/router-remove-external-gateways-response.json
:language: javascript

View File

@ -0,0 +1,16 @@
{
"router" : {
"external_gateways" : [
{
"enable_snat" : false,
"external_fixed_ips" : [
{
"ip_address" : "10.0.0.2",
"subnet_id" : "b189c314-ebb9-11eb-939c-9bde3f3867cb"
}
],
"network_id" : "8edec774-ebb9-11eb-9b09-371108ef5905"
}
]
}
}

View File

@ -0,0 +1,71 @@
{
"router" : {
"admin_state_up" : true,
"availability_zone_hints" : [],
"availability_zones" : [
"nova"
],
"created_at" : "2021-06-29T13:33:40Z",
"description" : "",
"distributed" : false,
"external_gateway_info" : {
"enable_snat" : false,
"external_fixed_ips" : [
{
"ip_address" : "172.24.4.144",
"subnet_id" : "1ed1c499-a45d-48d0-a567-e83a2364a40e"
}
],
"network_id" : "52700ca1-1647-46ad-8f86-b9e64eaed820"
},
"external_gateways" : [
{
"enable_snat" : false,
"external_fixed_ips" : [
{
"ip_address" : "172.24.4.144",
"subnet_id" : "1ed1c499-a45d-48d0-a567-e83a2364a40e"
}
],
"network_id" : "52700ca1-1647-46ad-8f86-b9e64eaed820"
},
{
"enable_snat" : false,
"external_fixed_ips" : [
{
"ip_address" : "10.0.0.2",
"subnet_id" : "b189c314-ebb9-11eb-939c-9bde3f3867cb"
}
],
"network_id" : "8edec774-ebb9-11eb-9b09-371108ef5905"
}
],
"flavor_id" : null,
"ha" : false,
"id" : "47c32c39-1c09-47de-8d50-ec57a96db5e7",
"interfaces_info" : [
{
"ip_address" : "fd26:d08e:af31::1",
"port_id" : "20683e3d-b041-4977-9686-b97db622c76a",
"subnet_id" : "2921b809-b60a-4799-ac99-59dacbeb7c3a"
},
{
"ip_address" : "10.0.0.1",
"port_id" : "89ab7084-7883-48e6-8281-d498a0cf4c92",
"subnet_id" : "3a5bec20-2df1-4d11-b0d5-5481969b91ac"
},
{
"ip_address" : "10.0.5.1",
"port_id" : "b04c6f4e-5bc7-43a2-85e9-c28452368532",
"subnet_id" : "f96970c4-026a-46f3-9852-f512a56688fe"
}
],
"name" : "router1",
"project_id" : "b66a1cea961f49738fff1210733ec440",
"revision_number" : 7,
"routes" : [],
"status" : "ACTIVE",
"tags" : [],
"updated_at" : "2021-06-29T13:37:07Z"
}
}

View File

@ -0,0 +1,9 @@
{
"router" : {
"external_gateways" : [
{
"network_id" : "8edec774-ebb9-11eb-9b09-371108ef5905"
}
]
}
}

View File

@ -0,0 +1,61 @@
{
"router" : {
"admin_state_up" : true,
"availability_zone_hints" : [],
"availability_zones" : [
"nova"
],
"created_at" : "2021-06-29T13:33:40Z",
"description" : "",
"distributed" : false,
"external_gateway_info" : {
"enable_snat" : false,
"external_fixed_ips" : [
{
"ip_address" : "172.24.4.144",
"subnet_id" : "1ed1c499-a45d-48d0-a567-e83a2364a40e"
}
],
"network_id" : "52700ca1-1647-46ad-8f86-b9e64eaed820"
},
"external_gateways" : [
{
"enable_snat" : false,
"external_fixed_ips" : [
{
"ip_address" : "172.24.4.144",
"subnet_id" : "1ed1c499-a45d-48d0-a567-e83a2364a40e"
}
],
"network_id" : "52700ca1-1647-46ad-8f86-b9e64eaed820"
}
],
"flavor_id" : null,
"ha" : false,
"id" : "47c32c39-1c09-47de-8d50-ec57a96db5e7",
"interfaces_info" : [
{
"ip_address" : "fd26:d08e:af31::1",
"port_id" : "20683e3d-b041-4977-9686-b97db622c76a",
"subnet_id" : "2921b809-b60a-4799-ac99-59dacbeb7c3a"
},
{
"ip_address" : "10.0.0.1",
"port_id" : "89ab7084-7883-48e6-8281-d498a0cf4c92",
"subnet_id" : "3a5bec20-2df1-4d11-b0d5-5481969b91ac"
},
{
"ip_address" : "10.0.5.1",
"port_id" : "b04c6f4e-5bc7-43a2-85e9-c28452368532",
"subnet_id" : "f96970c4-026a-46f3-9852-f512a56688fe"
}
],
"name" : "router1",
"project_id" : "b66a1cea961f49738fff1210733ec440",
"revision_number" : 7,
"routes" : [],
"status" : "ACTIVE",
"tags" : [],
"updated_at" : "2021-06-29T13:37:07Z"
}
}

View File

@ -0,0 +1,10 @@
{
"router" : {
"external_gateways" : [
{
"enable_snat" : true,
"network_id" : "8edec774-ebb9-11eb-9b09-371108ef5905"
}
]
}
}

View File

@ -0,0 +1,71 @@
{
"router" : {
"admin_state_up" : true,
"availability_zone_hints" : [],
"availability_zones" : [
"nova"
],
"created_at" : "2021-06-29T13:33:40Z",
"description" : "",
"distributed" : false,
"external_gateway_info" : {
"enable_snat" : false,
"external_fixed_ips" : [
{
"ip_address" : "172.24.4.144",
"subnet_id" : "1ed1c499-a45d-48d0-a567-e83a2364a40e"
}
],
"network_id" : "52700ca1-1647-46ad-8f86-b9e64eaed820"
},
"external_gateways" : [
{
"enable_snat" : false,
"external_fixed_ips" : [
{
"ip_address" : "172.24.4.144",
"subnet_id" : "1ed1c499-a45d-48d0-a567-e83a2364a40e"
}
],
"network_id" : "52700ca1-1647-46ad-8f86-b9e64eaed820"
},
{
"enable_snat" : true,
"external_fixed_ips" : [
{
"ip_address" : "10.0.0.2",
"subnet_id" : "b189c314-ebb9-11eb-939c-9bde3f3867cb"
}
],
"network_id" : "8edec774-ebb9-11eb-9b09-371108ef5905"
}
],
"flavor_id" : null,
"ha" : false,
"id" : "47c32c39-1c09-47de-8d50-ec57a96db5e7",
"interfaces_info" : [
{
"ip_address" : "fd26:d08e:af31::1",
"port_id" : "20683e3d-b041-4977-9686-b97db622c76a",
"subnet_id" : "2921b809-b60a-4799-ac99-59dacbeb7c3a"
},
{
"ip_address" : "10.0.0.1",
"port_id" : "89ab7084-7883-48e6-8281-d498a0cf4c92",
"subnet_id" : "3a5bec20-2df1-4d11-b0d5-5481969b91ac"
},
{
"ip_address" : "10.0.5.1",
"port_id" : "b04c6f4e-5bc7-43a2-85e9-c28452368532",
"subnet_id" : "f96970c4-026a-46f3-9852-f512a56688fe"
}
],
"name" : "router1",
"project_id" : "b66a1cea961f49738fff1210733ec440",
"revision_number" : 7,
"routes" : [],
"status" : "ACTIVE",
"tags" : [],
"updated_at" : "2021-06-29T13:37:07Z"
}
}

View File

@ -15,7 +15,6 @@ from oslo_config import cfg
from oslo_utils import strutils
from neutron_lib._i18n import _
from neutron_lib.api import validators
from neutron_lib import constants
from neutron_lib import exceptions as n_exc
from neutron_lib.utils import net as net_utils
@ -256,7 +255,7 @@ def convert_to_protocol(data):
"names and their integer representation (0 to "
"255) are supported") % data
try:
if validators.validate_range(convert_to_int(data), [0, 255]) is None:
if 0 <= convert_to_int(data) <= 255:
return data
else:
raise n_exc.InvalidInput(error_message=error_message)

View File

@ -64,6 +64,7 @@ from neutron_lib.api.definitions import l3_ext_gw_mode
from neutron_lib.api.definitions import l3_ext_ha_mode
from neutron_lib.api.definitions import l3_ext_ndp_proxy
from neutron_lib.api.definitions import l3_flavors
from neutron_lib.api.definitions import l3_multi_ext_gw
from neutron_lib.api.definitions import l3_ndp_proxy
from neutron_lib.api.definitions import l3_port_ip_change_not_allowed
from neutron_lib.api.definitions import logging
@ -192,6 +193,7 @@ _ALL_API_DEFINITIONS = {
l3_ext_ha_mode,
l3_ext_ndp_proxy,
l3_flavors,
l3_multi_ext_gw,
l3_ndp_proxy,
l3_port_ip_change_not_allowed,
logging,

View File

@ -25,6 +25,7 @@ KNOWN_ATTRIBUTES = (
'dns_nameservers',
'enable_dhcp',
'enable_ndp_proxy',
'external_gateways',
'fixed_ips',
'gateway_ip',
'host_routes',
@ -118,6 +119,7 @@ KNOWN_EXTENSIONS = (
'metering',
'metering_source_and_destination_filters',
'multi-provider',
'multiple-external-gateways',
'net-mtu',
'network-ip-availability',
'network-segment-range',

View File

@ -13,7 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
from neutron_lib.api import converters
from neutron_lib.api.definitions import l3
@ -35,18 +34,7 @@ RESOURCE_ATTRIBUTE_MAP = {
'is_visible': True,
'default': None,
'enforce_policy': True,
'validate': {
'type:dict_or_nodata': {
'network_id': {'type:uuid': None, 'required': True},
'enable_snat': {'type:boolean': None, 'required': False,
'convert_to':
converters.convert_to_boolean},
'external_fixed_ips': {
'type:fixed_ips': None,
'required': False
}
}
}
'validate': {'type:external_gw_info': None},
}
}
}

View File

@ -0,0 +1,48 @@
# Copyright 2021 Ericsson Software Technology
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from neutron_lib.api.definitions import l3
from neutron_lib.api.definitions import l3_ext_gw_mode
ALIAS = 'multiple-external-gateways'
IS_SHIM_EXTENSION = False
IS_STANDARD_ATTR_EXTENSION = False
NAME = 'Neutron L3 Multiple External Gateways'
API_PREFIX = ''
DESCRIPTION = 'Allow more than one External Gateways'
UPDATED_TIMESTAMP = '2021-04-01T00:00:00-00:00'
RESOURCE_NAME = l3.ROUTER
COLLECTION_NAME = l3.ROUTERS
RESOURCE_ATTRIBUTE_MAP = {
COLLECTION_NAME: {
'external_gateways': {
'allow_post': False,
'allow_put': False,
'is_visible': True,
'default': None,
'validate': {'type:external_gw_info_list': None},
},
},
}
SUB_RESOURCE_ATTRIBUTE_MAP = {}
ACTION_MAP = l3.ACTION_MAP
ACTION_MAP[l3.ROUTER].update({
'add_external_gateways': 'PUT',
'update_external_gateways': 'PUT',
'remove_external_gateways': 'PUT',
})
REQUIRED_EXTENSIONS = [l3.ALIAS, l3_ext_gw_mode.ALIAS]
OPTIONAL_EXTENSIONS = []
ACTION_STATUS = {}

View File

@ -25,6 +25,7 @@ from oslo_utils import uuidutils
from webob import exc
from neutron_lib._i18n import _
from neutron_lib.api import converters
from neutron_lib import constants
from neutron_lib import exceptions as n_exc
from neutron_lib.plugins import directory
@ -1169,6 +1170,35 @@ def validate_ethertype(ethertype, valid_values=None):
return msg
def validate_external_gw_info(data, valid_values=None):
"""Validate data is an external_gateway_info.
:param data: The data to validate.
:param valid_values: Not used!
:returns: None if valid, error string otherwise.
"""
return validate_dict_or_nodata(
data,
key_specs={
'network_id': {'type:uuid': None, 'required': True},
'external_fixed_ips': {'type:fixed_ips': None, 'required': False},
'enable_snat': {'type:boolean': None, 'required': False,
'convert_to': converters.convert_to_boolean},
}
)
def validate_external_gw_info_list(data, valid_values=None):
"""Validate data is a list of external_gateway_info.
:param data: The data to validate.
:param valid_values: Not used!
:returns: None if valid, error string otherwise.
"""
if data is not None:
return _validate_list_of_items(validate_external_gw_info, data)
# Dictionary that maintains a list of validation functions
validators = {'type:dict': validate_dict,
'type:dict_or_none': validate_dict_or_none,
@ -1215,7 +1245,9 @@ validators = {'type:dict': validate_dict,
'type:service_plugin_type': validate_service_plugin_type,
'type:list_of_subnets_or_none': validate_subnet_list_or_none,
'type:list_of_subnet_service_types':
validate_subnet_service_types
validate_subnet_service_types,
'type:external_gw_info': validate_external_gw_info,
'type:external_gw_info_list': validate_external_gw_info_list,
}

View File

@ -0,0 +1,21 @@
# Copyright 2021 Ericsson Software Technology
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from neutron_lib.api.definitions import l3_multi_ext_gw
from neutron_lib.tests.unit.api.definitions import base
class L3MultipleExternalGatewaysDefinitionTestCase(
base.DefinitionBaseTestCase):
extension_module = l3_multi_ext_gw

View File

@ -0,0 +1,9 @@
---
features:
- |
Definition for API extension: ``multiple-external-gateways``. This API
definition was merged as experimental to enable parallel development
of multiple backends. At the moment this API does not have a reference
implementation and should not be considered final. A later release
note will mark when the reference implementation gets merged and
the feauture is ready to be consumed.