From 75057767eb98c302524e566c79e251ab334af8a1 Mon Sep 17 00:00:00 2001 From: Bence Romsics Date: Thu, 14 Jan 2021 15:12:13 +0100 Subject: [PATCH] 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 --- api-ref/source/v2/parameters.yaml | 6 + api-ref/source/v2/routers.inc | 236 ++++++++++++++++++ .../router-add-external-gateways-request.json | 16 ++ ...router-add-external-gateways-response.json | 71 ++++++ ...uter-remove-external-gateways-request.json | 9 + ...ter-remove-external-gateways-response.json | 61 +++++ ...uter-update-external-gateways-request.json | 10 + ...ter-update-external-gateways-response.json | 71 ++++++ neutron_lib/api/converters.py | 3 +- neutron_lib/api/definitions/__init__.py | 2 + neutron_lib/api/definitions/base.py | 2 + neutron_lib/api/definitions/l3_ext_gw_mode.py | 14 +- .../api/definitions/l3_multi_ext_gw.py | 48 ++++ neutron_lib/api/validators/__init__.py | 34 ++- .../api/definitions/test_l3_multi_ext_gw.py | 21 ++ .../l3-multi-ext-gw-5a55c7afb7114db7.yaml | 9 + 16 files changed, 597 insertions(+), 16 deletions(-) create mode 100644 api-ref/source/v2/samples/routers/router-add-external-gateways-request.json create mode 100644 api-ref/source/v2/samples/routers/router-add-external-gateways-response.json create mode 100644 api-ref/source/v2/samples/routers/router-remove-external-gateways-request.json create mode 100644 api-ref/source/v2/samples/routers/router-remove-external-gateways-response.json create mode 100644 api-ref/source/v2/samples/routers/router-update-external-gateways-request.json create mode 100644 api-ref/source/v2/samples/routers/router-update-external-gateways-response.json create mode 100644 neutron_lib/api/definitions/l3_multi_ext_gw.py create mode 100644 neutron_lib/tests/unit/api/definitions/test_l3_multi_ext_gw.py create mode 100644 releasenotes/notes/l3-multi-ext-gw-5a55c7afb7114db7.yaml diff --git a/api-ref/source/v2/parameters.yaml b/api-ref/source/v2/parameters.yaml index c8bf76494..bb9636b05 100644 --- a/api-ref/source/v2/parameters.yaml +++ b/api-ref/source/v2/parameters.yaml @@ -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. diff --git a/api-ref/source/v2/routers.inc b/api-ref/source/v2/routers.inc index e38b56b63..cd8727919 100644 --- a/api-ref/source/v2/routers.inc +++ b/api-ref/source/v2/routers.inc @@ -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 diff --git a/api-ref/source/v2/samples/routers/router-add-external-gateways-request.json b/api-ref/source/v2/samples/routers/router-add-external-gateways-request.json new file mode 100644 index 000000000..a1e229922 --- /dev/null +++ b/api-ref/source/v2/samples/routers/router-add-external-gateways-request.json @@ -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" + } + ] + } +} diff --git a/api-ref/source/v2/samples/routers/router-add-external-gateways-response.json b/api-ref/source/v2/samples/routers/router-add-external-gateways-response.json new file mode 100644 index 000000000..6b91b6bea --- /dev/null +++ b/api-ref/source/v2/samples/routers/router-add-external-gateways-response.json @@ -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" + } +} diff --git a/api-ref/source/v2/samples/routers/router-remove-external-gateways-request.json b/api-ref/source/v2/samples/routers/router-remove-external-gateways-request.json new file mode 100644 index 000000000..71b33f7a9 --- /dev/null +++ b/api-ref/source/v2/samples/routers/router-remove-external-gateways-request.json @@ -0,0 +1,9 @@ +{ + "router" : { + "external_gateways" : [ + { + "network_id" : "8edec774-ebb9-11eb-9b09-371108ef5905" + } + ] + } +} diff --git a/api-ref/source/v2/samples/routers/router-remove-external-gateways-response.json b/api-ref/source/v2/samples/routers/router-remove-external-gateways-response.json new file mode 100644 index 000000000..502a33967 --- /dev/null +++ b/api-ref/source/v2/samples/routers/router-remove-external-gateways-response.json @@ -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" + } +} diff --git a/api-ref/source/v2/samples/routers/router-update-external-gateways-request.json b/api-ref/source/v2/samples/routers/router-update-external-gateways-request.json new file mode 100644 index 000000000..d61966a9f --- /dev/null +++ b/api-ref/source/v2/samples/routers/router-update-external-gateways-request.json @@ -0,0 +1,10 @@ +{ + "router" : { + "external_gateways" : [ + { + "enable_snat" : true, + "network_id" : "8edec774-ebb9-11eb-9b09-371108ef5905" + } + ] + } +} diff --git a/api-ref/source/v2/samples/routers/router-update-external-gateways-response.json b/api-ref/source/v2/samples/routers/router-update-external-gateways-response.json new file mode 100644 index 000000000..c00f0695d --- /dev/null +++ b/api-ref/source/v2/samples/routers/router-update-external-gateways-response.json @@ -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" + } +} diff --git a/neutron_lib/api/converters.py b/neutron_lib/api/converters.py index b97bb5453..d18052673 100644 --- a/neutron_lib/api/converters.py +++ b/neutron_lib/api/converters.py @@ -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) diff --git a/neutron_lib/api/definitions/__init__.py b/neutron_lib/api/definitions/__init__.py index 347d4fb2e..ca04c337f 100644 --- a/neutron_lib/api/definitions/__init__.py +++ b/neutron_lib/api/definitions/__init__.py @@ -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, diff --git a/neutron_lib/api/definitions/base.py b/neutron_lib/api/definitions/base.py index 53f1dee89..46ef1093e 100644 --- a/neutron_lib/api/definitions/base.py +++ b/neutron_lib/api/definitions/base.py @@ -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', diff --git a/neutron_lib/api/definitions/l3_ext_gw_mode.py b/neutron_lib/api/definitions/l3_ext_gw_mode.py index 7d3ca8fa6..e533b64d7 100644 --- a/neutron_lib/api/definitions/l3_ext_gw_mode.py +++ b/neutron_lib/api/definitions/l3_ext_gw_mode.py @@ -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}, } } } diff --git a/neutron_lib/api/definitions/l3_multi_ext_gw.py b/neutron_lib/api/definitions/l3_multi_ext_gw.py new file mode 100644 index 000000000..c9ca864fe --- /dev/null +++ b/neutron_lib/api/definitions/l3_multi_ext_gw.py @@ -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 = {} diff --git a/neutron_lib/api/validators/__init__.py b/neutron_lib/api/validators/__init__.py index 6f67dfc68..2432c7b0c 100644 --- a/neutron_lib/api/validators/__init__.py +++ b/neutron_lib/api/validators/__init__.py @@ -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, } diff --git a/neutron_lib/tests/unit/api/definitions/test_l3_multi_ext_gw.py b/neutron_lib/tests/unit/api/definitions/test_l3_multi_ext_gw.py new file mode 100644 index 000000000..6c2f28ba4 --- /dev/null +++ b/neutron_lib/tests/unit/api/definitions/test_l3_multi_ext_gw.py @@ -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 diff --git a/releasenotes/notes/l3-multi-ext-gw-5a55c7afb7114db7.yaml b/releasenotes/notes/l3-multi-ext-gw-5a55c7afb7114db7.yaml new file mode 100644 index 000000000..c9519fbab --- /dev/null +++ b/releasenotes/notes/l3-multi-ext-gw-5a55c7afb7114db7.yaml @@ -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.