From 4da199808fdc3646fafab5d7daa9cd666c4590a5 Mon Sep 17 00:00:00 2001 From: Armando Migliaccio Date: Tue, 9 Aug 2016 15:06:24 -0700 Subject: [PATCH] Introduce API definition for trunk/trunk_details extensions This patch serves as a trail blazer for Neutron API consolidation. It introduces the following: * It provides the API definitions to be used by a Neutron Stadium project. * It introduces some testing scaffolding to validate the correctness of an API definition and coverage for the extension. * It provides the API reference documentation for the extension. More patches will follow up to consolidate the rest of the Neutron APIs and will use this as an example/template on how to approach the consolidation effort. Change-Id: Ie620a6fc21916ab34a19632c7fb9456b2c7c43b1 --- api-ref/source/v2/index.rst | 2 + api-ref/source/v2/intro.inc | 2 + api-ref/source/v2/parameters.yaml | 92 +++++ .../trunks/trunk-add-subports-request.json | 9 + .../trunks/trunk-add-subports-response.json | 20 + .../samples/trunks/trunk-create-request.json | 7 + .../trunks/trunk-details-show-response.json | 43 ++ .../trunks/trunk-list-subports-response.json | 9 + .../trunks/trunk-remove-subports-request.json | 7 + .../trunk-remove-subports-response.json | 14 + .../samples/trunks/trunk-show-response.json | 22 + .../samples/trunks/trunk-update-request.json | 6 + .../samples/trunks/trunk-update-response.json | 22 + .../trunks/trunks-create-response.json | 16 + .../samples/trunks/trunks-list-response.json | 18 + api-ref/source/v2/trunk-details.inc | 43 ++ api-ref/source/v2/trunk.inc | 386 ++++++++++++++++++ neutron_lib/api/definitions/__init__.py | 0 neutron_lib/api/definitions/base.py | 108 +++++ neutron_lib/api/definitions/trunk.py | 97 +++++ neutron_lib/api/definitions/trunk_details.py | 62 +++ .../tests/unit/api/definitions/__init__.py | 0 .../tests/unit/api/definitions/base.py | 140 +++++++ .../tests/unit/api/definitions/test_trunk.py | 20 + .../api/definitions/test_trunk_details.py | 19 + 25 files changed, 1164 insertions(+) create mode 100644 api-ref/source/v2/samples/trunks/trunk-add-subports-request.json create mode 100644 api-ref/source/v2/samples/trunks/trunk-add-subports-response.json create mode 100644 api-ref/source/v2/samples/trunks/trunk-create-request.json create mode 100644 api-ref/source/v2/samples/trunks/trunk-details-show-response.json create mode 100644 api-ref/source/v2/samples/trunks/trunk-list-subports-response.json create mode 100644 api-ref/source/v2/samples/trunks/trunk-remove-subports-request.json create mode 100644 api-ref/source/v2/samples/trunks/trunk-remove-subports-response.json create mode 100644 api-ref/source/v2/samples/trunks/trunk-show-response.json create mode 100644 api-ref/source/v2/samples/trunks/trunk-update-request.json create mode 100644 api-ref/source/v2/samples/trunks/trunk-update-response.json create mode 100644 api-ref/source/v2/samples/trunks/trunks-create-response.json create mode 100644 api-ref/source/v2/samples/trunks/trunks-list-response.json create mode 100644 api-ref/source/v2/trunk-details.inc create mode 100644 api-ref/source/v2/trunk.inc create mode 100644 neutron_lib/api/definitions/__init__.py create mode 100644 neutron_lib/api/definitions/base.py create mode 100644 neutron_lib/api/definitions/trunk.py create mode 100644 neutron_lib/api/definitions/trunk_details.py create mode 100644 neutron_lib/tests/unit/api/definitions/__init__.py create mode 100644 neutron_lib/tests/unit/api/definitions/base.py create mode 100644 neutron_lib/tests/unit/api/definitions/test_trunk.py create mode 100644 neutron_lib/tests/unit/api/definitions/test_trunk_details.py diff --git a/api-ref/source/v2/index.rst b/api-ref/source/v2/index.rst index ef3162169..bb78f423e 100644 --- a/api-ref/source/v2/index.rst +++ b/api-ref/source/v2/index.rst @@ -29,6 +29,8 @@ Networking API v2.0 .. include:: network-ip-availability.inc .. include:: qos.inc .. include:: metering.inc +.. include:: trunk.inc +.. include:: trunk-details.inc .. include:: lbaas-v2.inc .. include:: lbaas-v1.inc .. include:: fwaas.inc diff --git a/api-ref/source/v2/intro.inc b/api-ref/source/v2/intro.inc index 6abc65673..aa604d99e 100644 --- a/api-ref/source/v2/intro.inc +++ b/api-ref/source/v2/intro.inc @@ -111,6 +111,8 @@ attribute in responses, check that the ``project-id`` API extension is enabled (see Extensions_). +.. _filtering: + Filtering and column selection ============================== diff --git a/api-ref/source/v2/parameters.yaml b/api-ref/source/v2/parameters.yaml index aea07ad67..d344dd57a 100644 --- a/api-ref/source/v2/parameters.yaml +++ b/api-ref/source/v2/parameters.yaml @@ -187,6 +187,12 @@ tag: in: path required: false type: string +trunk_id: + description: | + The ID of the trunk. + in: path + required: true + type: string vip_id_1: description: | The UUID for the VIP. @@ -482,6 +488,13 @@ admin_state_up_9: in: body required: false type: boolean +admin_state_up_trunk: + description: | + The administrative state of the trunk, which + is up (``true``) or down (``false``). + in: body + required: false + type: boolean alias: description: | The alias for the extension. For example, @@ -642,6 +655,12 @@ created_at_1: in: body required: true type: string +created_at_resource: + description: | + Time at which the resource has been created (in UTC ISO8601 format). + in: body + required: true + type: string default: description: | Defines whether the provider is the default for @@ -983,6 +1002,12 @@ description_9: in: body required: false type: string +description_resource: + description: | + The description for the resource. + in: body + required: true + type: string destination_ip_address: description: | The destination IPv4 or IPv6 address or CIDR. No @@ -1700,6 +1725,12 @@ id_9: in: body required: true type: string +id_resource: + description: | + The ID for the resource. + in: body + required: true + type: string ike_version: description: | The IKE version. A valid value is ``v1`` or @@ -2438,6 +2469,12 @@ name_9: in: body required: true type: string +name_resource: + description: | + The name of the resource. + in: body + required: false + type: string network: description: | A ``network`` object. @@ -2715,6 +2752,12 @@ port_id_4: in: body required: true type: string +port_id_subport: + description: | + The ID of the subport. + in: body + required: true + type: string port_range_max: description: | The maximum port number in the range that is @@ -3116,6 +3159,12 @@ resources: in: body required: true type: array +revision_number: + description: | + The revision number of the resource. + in: body + required: true + type: integer route_mode: description: | The route mode. A valid value is ``static``, @@ -3447,6 +3496,18 @@ segment_id: in: body required: true type: string +segmentation_id: + description: | + The segmentation ID for the subport. + in: body + required: false + type: integer +segmentation_type: + description: | + The segmentation type for the subport. + in: body + required: false + type: string segments: description: | A list of provider ``segment`` objects. @@ -3754,6 +3815,12 @@ status_description: in: body required: true type: string +sub_ports: + description: | + A list of ports associated with the trunk. + in: body + required: true + type: array subnet_id: description: | If you specify only a subnet UUID, OpenStack @@ -3900,6 +3967,25 @@ transform_protocol_1: in: body required: true type: string +trunk_details: + description: | + The details about the trunk. + in: body + required: false + type: dict +trunk_port_id: + description: | + The ID of the parent port. + in: body + required: true + type: string +trunk_status: + description: | + The status for the trunk. Possible values are ``ACTIVE``, + ``DOWN``, ``BUILD``, ``DEGRADED``, and ``ERROR``. + in: body + required: true + type: string type: description: | The type of probe sent by the load balancer to @@ -3973,6 +4059,12 @@ updated_at_2: in: body required: true type: string +updated_at_resource: + description: | + Time at which the resource has been updated (in UTC ISO8601 format). + in: body + required: true + type: string url_path: description: | The HTTP path of the request sent by the monitor diff --git a/api-ref/source/v2/samples/trunks/trunk-add-subports-request.json b/api-ref/source/v2/samples/trunks/trunk-add-subports-request.json new file mode 100644 index 000000000..27bfcde22 --- /dev/null +++ b/api-ref/source/v2/samples/trunks/trunk-add-subports-request.json @@ -0,0 +1,9 @@ +{ + "sub_ports": [ + { + "segmentation_id": 44, + "port_id": "4b4c691b-086d-43d2-8a65-5487e9434155", + "segmentation_type": "vlan" + } + ] +} diff --git a/api-ref/source/v2/samples/trunks/trunk-add-subports-response.json b/api-ref/source/v2/samples/trunks/trunk-add-subports-response.json new file mode 100644 index 000000000..d90cec48d --- /dev/null +++ b/api-ref/source/v2/samples/trunks/trunk-add-subports-response.json @@ -0,0 +1,20 @@ +{ + "status": "DOWN", + "sub_ports": [ + { + "segmentation_type": "vlan", + "port_id": "4b4c691b-086d-43d2-8a65-5487e9434155", + "segmentation_id": 44 + } + ], + "name": "test", + "admin_state_up": true, + "project_id": "145a14e4a64b49bf98baad8945dbd4f1", + "tenant_id": "145a14e4a64b49bf98baad8945dbd4f1", + "created_at": "2016-10-05T22:31:37Z", + "updated_at": "2016-10-05T22:52:04Z", + "revision_number": 2, + "port_id": "8027c4da-772f-4e43-bfbf-023b4a4e63de", + "id": "114a26b1-d124-4835-bb4f-021d3d886023", + "description": "" +} diff --git a/api-ref/source/v2/samples/trunks/trunk-create-request.json b/api-ref/source/v2/samples/trunks/trunk-create-request.json new file mode 100644 index 000000000..c08688da7 --- /dev/null +++ b/api-ref/source/v2/samples/trunks/trunk-create-request.json @@ -0,0 +1,7 @@ +{ + "trunk": { + "port_id": "8027c4da-772f-4e43-bfbf-023b4a4e63de", + "name": "test", + "admin_state_up": true + } +} diff --git a/api-ref/source/v2/samples/trunks/trunk-details-show-response.json b/api-ref/source/v2/samples/trunks/trunk-details-show-response.json new file mode 100644 index 000000000..933d2ce21 --- /dev/null +++ b/api-ref/source/v2/samples/trunks/trunk-details-show-response.json @@ -0,0 +1,43 @@ +{ + "port": { + "status": "DOWN", + "created_at": "2016-10-05T20:05:14Z", + "description": "", + "admin_state_up": true, + "network_id": "1cf9e069-365f-4a78-8784-616bc12c4c5a", + "project_id": "313be01bd0744cea86643c711c57012b", + "tenant_id": "313be01bd0744cea86643c711c57012b", + "extra_dhcp_opts": [], + "updated_at": "2016-10-05T20:05:14Z", + "name": "test", + "device_owner": "", + "trunk_details": { + "trunk_id": "8905d084-010c-46e8-a863-f21cb4441ab1", + "sub_ports": [ + { + "segmentation_id": 33, + "port_id": "70df9f3e-b409-4761-8304-ce029b2079f5", + "segmentation_type": "vlan", + "mac_address": "fa:16:3e:86:9b:dc" + }, + { + "segmentation_id": 44, + "port_id": "4b4c691b-086d-43d2-8a65-5487e9434155", + "segmentation_type": "vlan", + "mac_address": "fa:16:3e:fe:29:97" + } + ] + }, + "revision_number": 5, + "mac_address": "fa:16:3e:5c:e9:a3", + "fixed_ips": [ + { + "subnet_id": "76a059c0-b189-479f-882c-5e8bd464ea49", + "ip_address": "40.0.0.3" + } + ], + "id": "8027c4da-772f-4e43-bfbf-023b4a4e63de", + "security_groups": ["da88a249-12ac-4221-9565-c406b6feeb48"], + "device_id": "" + } +} diff --git a/api-ref/source/v2/samples/trunks/trunk-list-subports-response.json b/api-ref/source/v2/samples/trunks/trunk-list-subports-response.json new file mode 100644 index 000000000..95c4c70ab --- /dev/null +++ b/api-ref/source/v2/samples/trunks/trunk-list-subports-response.json @@ -0,0 +1,9 @@ +{ + "sub_ports": [ + { + "segmentation_type": "vlan", + "port_id": "4b4c691b-086d-43d2-8a65-5487e9434155", + "segmentation_id": 44 + } + ] +} diff --git a/api-ref/source/v2/samples/trunks/trunk-remove-subports-request.json b/api-ref/source/v2/samples/trunks/trunk-remove-subports-request.json new file mode 100644 index 000000000..cf1ac11c4 --- /dev/null +++ b/api-ref/source/v2/samples/trunks/trunk-remove-subports-request.json @@ -0,0 +1,7 @@ +{ + "sub_ports": [ + { + "port_id": "4b4c691b-086d-43d2-8a65-5487e9434155" + } + ] +} diff --git a/api-ref/source/v2/samples/trunks/trunk-remove-subports-response.json b/api-ref/source/v2/samples/trunks/trunk-remove-subports-response.json new file mode 100644 index 000000000..7ed2ea07d --- /dev/null +++ b/api-ref/source/v2/samples/trunks/trunk-remove-subports-response.json @@ -0,0 +1,14 @@ +{ + "status": "DOWN", + "sub_ports": [], + "name": "test", + "admin_state_up": true, + "project_id": "145a14e4a64b49bf98baad8945dbd4f1", + "tenant_id": "145a14e4a64b49bf98baad8945dbd4f1", + "created_at": "2016-10-05T22:31:37Z", + "updated_at": "2016-10-05T22:57:44Z", + "revision_number": 3, + "port_id": "8027c4da-772f-4e43-bfbf-023b4a4e63de", + "id": "114a26b1-d124-4835-bb4f-021d3d886023", + "description": "" +} diff --git a/api-ref/source/v2/samples/trunks/trunk-show-response.json b/api-ref/source/v2/samples/trunks/trunk-show-response.json new file mode 100644 index 000000000..f9597baf9 --- /dev/null +++ b/api-ref/source/v2/samples/trunks/trunk-show-response.json @@ -0,0 +1,22 @@ +{ + "trunk": { + "status": "DOWN", + "sub_ports": [ + { + "segmentation_type": "vlan", + "port_id": "4b4c691b-086d-43d2-8a65-5487e9434155", + "segmentation_id": 44 + } + ], + "name": "foo", + "admin_state_up": true, + "project_id": "145a14e4a64b49bf98baad8945dbd4f1", + "tenant_id": "145a14e4a64b49bf98baad8945dbd4f1", + "created_at": "2016-10-05T22:31:37Z", + "updated_at": "2016-10-05T23:28:17Z", + "revision_number": 9, + "port_id": "8027c4da-772f-4e43-bfbf-023b4a4e63de", + "id": "114a26b1-d124-4835-bb4f-021d3d886023", + "description": "" + } +} diff --git a/api-ref/source/v2/samples/trunks/trunk-update-request.json b/api-ref/source/v2/samples/trunks/trunk-update-request.json new file mode 100644 index 000000000..2727237b4 --- /dev/null +++ b/api-ref/source/v2/samples/trunks/trunk-update-request.json @@ -0,0 +1,6 @@ +{ + "trunk": { + "name": "foo", + "admin_state_up": true + } +} diff --git a/api-ref/source/v2/samples/trunks/trunk-update-response.json b/api-ref/source/v2/samples/trunks/trunk-update-response.json new file mode 100644 index 000000000..77e103a87 --- /dev/null +++ b/api-ref/source/v2/samples/trunks/trunk-update-response.json @@ -0,0 +1,22 @@ +{ + "trunk": { + "status": "DOWN", + "sub_ports": [ + { + "segmentation_type": "vlan", + "port_id": "4b4c691b-086d-43d2-8a65-5487e9434155", + "segmentation_id": 44 + } + ], + "name": "foo", + "admin_state_up": true, + "project_id": "145a14e4a64b49bf98baad8945dbd4f1", + "tenant_id": "145a14e4a64b49bf98baad8945dbd4f1", + "created_at": "2016-10-05T22:31:37Z", + "updated_at": "2016-10-05T23:28:17Z", + "revision_number": 9, + "port_id": "8027c4da-772f-4e43-bfbf-023b4a4e63de", + "id": "114a26b1-d124-4835-bb4f-021d3d886023", + "description": "" + } +} diff --git a/api-ref/source/v2/samples/trunks/trunks-create-response.json b/api-ref/source/v2/samples/trunks/trunks-create-response.json new file mode 100644 index 000000000..ff831dc41 --- /dev/null +++ b/api-ref/source/v2/samples/trunks/trunks-create-response.json @@ -0,0 +1,16 @@ +{ + "trunk": { + "status": "DOWN", + "sub_ports": [], + "name": "test", + "admin_state_up": true, + "project_id": "145a14e4a64b49bf98baad8945dbd4f1", + "tenant_id": "145a14e4a64b49bf98baad8945dbd4f1", + "created_at": "2016-10-05T22:31:37Z", + "updated_at": "2016-10-05T22:31:37Z", + "revision_number": 1, + "port_id": "8027c4da-772f-4e43-bfbf-023b4a4e63de", + "id": "114a26b1-d124-4835-bb4f-021d3d886023", + "description": "" + } +} diff --git a/api-ref/source/v2/samples/trunks/trunks-list-response.json b/api-ref/source/v2/samples/trunks/trunks-list-response.json new file mode 100644 index 000000000..19bf2e71f --- /dev/null +++ b/api-ref/source/v2/samples/trunks/trunks-list-response.json @@ -0,0 +1,18 @@ +{ + "trunks": [ + { + "status": "DOWN", + "sub_ports": [], + "name": "test", + "admin_state_up": true, + "project_id": "313be01bd0744cea86643c711c57012b", + "tenant_id": "313be01bd0744cea86643c711c57012b", + "created_at": "2016-10-05T20:11:16Z", + "updated_at": "2016-10-05T20:11:16Z", + "revision_number": 1, + "port_id": "8027c4da-772f-4e43-bfbf-023b4a4e63de", + "id": "ee98bdb4-a817-43af-943f-4318bff98f51", + "description": "" + } + ] +} diff --git a/api-ref/source/v2/trunk-details.inc b/api-ref/source/v2/trunk-details.inc new file mode 100644 index 000000000..1a4439670 --- /dev/null +++ b/api-ref/source/v2/trunk-details.inc @@ -0,0 +1,43 @@ +.. -*- rst -*- + +========================================= +Trunk details extended attributes (ports) +========================================= + +The trunk_details extension attribute is available when showing a +port resource that participates in a trunk as parent. The extension +is useful for REST clients that may want to access trunk details +when getting the parent port, and it allows them to avoid extra +lookups. + +Show trunk details +================== + +.. rest_method:: GET /v2.0/ports/{port_id} + +Shows details for a port. The details available in the `trunk_details` +attribute contain the trunk ID and the array showing information +about the subports that belong to the trunk: the port UUID, the +segmentation type, the segmentation ID, and the MAC address. + +Normal response codes: 200 + +Request +------- + +.. rest_parameters:: parameters.yaml + + - port_id: port_id + +Response Parameters +------------------- + +.. rest_parameters:: parameters.yaml + + - trunk_details: trunk_details + +Response Example +---------------- + +.. literalinclude:: ../v2/samples/trunks/trunk-details-show-response.json + :language: javascript diff --git a/api-ref/source/v2/trunk.inc b/api-ref/source/v2/trunk.inc new file mode 100644 index 000000000..63bee0695 --- /dev/null +++ b/api-ref/source/v2/trunk.inc @@ -0,0 +1,386 @@ +.. -*- rst -*- + +================ +Trunk networking +================ + +The trunk extension can be used to multiplex packets coming from and going to +multiple neutron logical networks using a single neutron logical port. A trunk +is modeled in neutron as a collection of neutron logical ports. One port, +called parent port, must be associated to a trunk and it is *the* port to +be used to connect instances with neutron. A sequence of subports (or +sub_ports) each typically belonging to distinct neutron networks, is also +associated to a trunk, and each subport may have a segmentation type and ID +used to mux/demux the traffic coming in and out of the parent port. + +In more details, the extension introduces the following resources: + +- **trunk**. A top level logical entity to model the group of neutron + logical networks whose traffic flows through the trunk. + +- **sub_port**. An association to a neutron logical port with attributes + segmentation_id and segmentation_type. + + +List trunks +=========== + +.. rest_method:: GET /v2.0/trunks + +Lists trunks that are accessible to the user who submits the request. + +Default policy settings return only those trunks that are +owned by the user who submits the request, unless an admin user +submits the request. + +Use the ``fields`` query parameter to control which fields are +returned in the response body. Additionally, you can filter results +by using query string parameters. For information, see the Filtering_ +section for more details. + +Normal response codes: 200 + +Error response codes: 401,404 + +Response Parameters +------------------- + +.. rest_parameters:: parameters.yaml + + - admin_state_up: admin_state_up_trunk + - created_at: created_at_resource + - description: description_resource + - id: id_resource + - name: name_resource + - port_id: trunk_port_id + - revision_number: revision_number + - status: trunk_status + - tenant_id: project_id-request + - project_id: project_id-request + - sub_ports: sub_ports + - updated_at: updated_at_resource + +Response Example +---------------- + +.. literalinclude:: ../v2/samples/trunks/trunks-list-response.json + :language: javascript + + +Create trunk +============ + +.. rest_method:: POST /v2.0/trunks + +Error codes: + +- ``400`` The operation returns this error code if the request is malformed, + e.g. there are missing or invalid parameters in the request. + +- ``401`` The operation is not authorized. + +- ``404`` If the extension is not available or the port UUID of any of the + specified ports is not found. + +- ``409`` The operation returns this error code for one of these + reasons: + + - A port to be used as subport is in use by another trunk. + + - The segmentation type and segmentation ID are already in use in the trunk. + + - A port to be used as parent port is in use by another trunk or cannot be trunked. + + - A system configuration prevents the operation from succeeding. + + +Error response codes: 201,400,401,404,409 + +Request +------- + +.. rest_parameters:: parameters.yaml + + - tenant_id: project_id + - project_id: project_id + - port_id: trunk_port_id + - name: name_resource + - description: description_resource + - admin_state_up: admin_state_up_trunk + - sub_ports: sub_ports + +Request Example +--------------- + +.. literalinclude:: ../v2/samples/trunks/trunk-create-request.json + :language: javascript + +Response Parameters +------------------- + +.. rest_parameters:: parameters.yaml + + - admin_state_up: admin_state_up_trunk + - created_at: created_at_resource + - description: description_resource + - id: id_resource + - name: name_resource + - port_id: trunk_port_id + - revision_number: revision_number + - status: trunk_status + - tenant_id: project_id + - project_id: project_id + - sub_ports: sub_ports + - updated_at: updated_at_resource + + +Add subports to trunk +===================== + +.. rest_method:: PUT /v2.0/trunks/{trunk_id}/add_subports + +Normal response codes: 200 + +Error response codes: 400,401,404,409 + +Request +------- + +.. rest_parameters:: parameters.yaml + + - trunk_id: trunk_id + - segmentation_id: segmentation_id + - segmentation_type: segmentation_type + - port_id: port_id_subport + +Request Example +--------------- + +.. literalinclude:: ../v2/samples/trunks/trunk-add-subports-request.json + :language: javascript + +Response Parameters +------------------- + +.. rest_parameters:: parameters.yaml + + - admin_state_up: admin_state_up_trunk + - created_at: created_at_resource + - description: description_resource + - id: id_resource + - name: name_resource + - port_id: trunk_port_id + - revision_number: revision_number + - status: trunk_status + - tenant_id: project_id + - project_id: project_id + - sub_ports: sub_ports + - updated_at: updated_at_resource + +Response Example +---------------- + +.. literalinclude:: ../v2/samples/trunks/trunk-add-subports-response.json + :language: javascript + + +Delete subports from trunk +========================== + +.. rest_method:: PUT /v2.0/trunks/{trunk_id}/remove_subports + +Normal response codes: 200 + +Error response codes: 400,401,404,409 + +Request +------- + +.. rest_parameters:: parameters.yaml + + - trunk_id: trunk_id + - port_id: port_id + +Request Example +--------------- + +.. literalinclude:: ../v2/samples/trunks/trunk-remove-subports-request.json + :language: javascript + +Response Parameters +------------------- + +.. rest_parameters:: parameters.yaml + + - admin_state_up: admin_state_up_trunk + - created_at: created_at_resource + - description: description_resource + - id: id_resource + - name: name_resource + - port_id: trunk_port_id + - revision_number: revision_number + - status: trunk_status + - tenant_id: project_id + - project_id: project_id + - sub_ports: sub_ports + - updated_at: updated_at_resource + +Response Example +---------------- + +.. literalinclude:: ../v2/samples/trunks/trunk-remove-subports-response.json + :language: javascript + + +List subports for trunk +======================= + +.. rest_method:: GET /v2.0/trunks/{trunk_id}/get_subports + +Normal response codes: 200 + +Error response codes: 401,404 + +Request +------- + +.. rest_parameters:: parameters.yaml + + - trunk_id: trunk_id + +Response Parameters +------------------- + +.. rest_parameters:: parameters.yaml + + - port_id: port_id_subport + - segmentation_type: segmentation_type + - segmentation_id: segmentation_id + +Response Example +---------------- + +.. literalinclude:: ../v2/samples/trunks/trunk-list-subports-response.json + :language: javascript + + +Update trunk +============ + +.. rest_method:: PUT /v2.0/trunks/{trunk_id} + +The update request is only for changing fields like name, description or +admin_state_up. Setting the admin_state_up to False locks the trunk in +that it prevents operations such as as adding/removing subports. + +Normal response codes: 200 + +Error response codes: 400,401,404,409 + +Request +------- + +.. rest_parameters:: parameters.yaml + + - name_resource: name_resource + - admin_state_up_trunk: admin_state_up + - description_resource: description_resource + +Request Example +--------------- + +.. literalinclude:: ../v2/samples/trunks/trunk-update-request.json + :language: javascript + +Response Parameters +------------------- + +.. rest_parameters:: parameters.yaml + + + - admin_state_up: admin_state_up_trunk + - created_at: created_at_resource + - description: description_resource + - id: id_resource + - name: name_resource + - port_id: trunk_port_id + - revision_number: revision_number + - status: trunk_status + - tenant_id: project_id + - project_id: project_id + - sub_ports: sub_ports + - updated_at: updated_at_resource + +Response Example +---------------- + +.. literalinclude:: ../v2/samples/trunks/trunk-update-response.json + :language: javascript + + +Show trunk +========== + +.. rest_method:: GET /v2.0/trunks/{trunk_id} + +Shows details for a trunk. + +Use the ``fields`` query parameter to control which fields are +returned in the response body. For information, see `Filtering and +Column Selection `__. + +Normal response codes: 200 + +Error response codes: 404,401 + +Request +------- + +.. rest_parameters:: parameters.yaml + + - trunk_id: trunk_id + +Response Parameters +------------------- + +.. rest_parameters:: parameters.yaml + + - admin_state_up: admin_state_up_trunk + - created_at: created_at_resource + - description: description_resource + - id: id_resource + - name: name_resource + - port_id: trunk_port_id + - revision_number: revision_number + - status: trunk_status + - tenant_id: project_id + - project_id: project_id + - sub_ports: sub_ports + - updated_at: updated_at_resource + +Response Example +---------------- + +.. literalinclude:: ../v2/samples/trunks/trunk-show-response.json + :language: javascript + + +Delete trunk +============ + +.. rest_method:: DELETE /v2.0/trunks/{trunk_id} + +Deletes a trunk, if its state allows it. + +Normal response codes: 204 + +Error response codes: 401,404,409 + +Request +------- + +.. rest_parameters:: parameters.yaml + + - trunk_id: trunk_id diff --git a/neutron_lib/api/definitions/__init__.py b/neutron_lib/api/definitions/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutron_lib/api/definitions/base.py b/neutron_lib/api/definitions/base.py new file mode 100644 index 000000000..3734403ec --- /dev/null +++ b/neutron_lib/api/definitions/base.py @@ -0,0 +1,108 @@ +# 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. + +KNOWN_ATTRIBUTES = ( + 'admin_state_up', + 'id', + 'description', + 'name', + 'network_id', + 'port_id', + 'project_id', + 'shared', + 'status', + 'tenant_id', +) + +KNOWN_RESOURCES = ( + 'networks', + 'ports', + 'subnets', + 'routers', +) + +KNOWN_HTTP_ACTIONS = ( + 'DELETE', + 'GET', + 'POST', + 'PUT', +) + +KNOWN_EXTENSIONS = ( + 'address-scope', + 'agent', + 'allowed-address-pairs', + 'auto-allocated-topology', + 'availability_zone', + 'binding', + 'default-subnetpools', + 'dhcp_agent_scheduler', + 'dns-integration', + 'dvr', + 'ext-gw-mode', + 'external-net', + 'extra_dhcp_opt', + 'extraroute', + 'flavors', + 'l3-ha', + 'l3_agent_scheduler', + 'metering', + 'multi-provider', + 'net-mtu', + 'network-ip-availability', + 'network_availability_zone', + 'pagination', + 'port-security', + 'project-id', + 'provider', + 'qos', + 'quotas', + 'rbac-policies', + 'router', + 'router_availability_zone', + 'security-group', + 'service-type', + 'sorting', + 'standard-attr-description', + 'standard-attr-revisions', + 'standard-attr-timestamp', + 'subnet_allocation', + 'tag', + 'trunk', + 'trunk-details', +) + +# The following is a short reference for understanding attribute info: +# allow_post: the attribute can be used on POST requests. +# allow_put: the attribute can be used on PUT requests. +# convert_to: transformation to apply to the value before it is returned +# default: default value of the attribute (if missing, the attribute +# becomes mandatory. +# enforce_policy: the attribute is actively part of the policy enforcing +# mechanism, ie: there might be rules which refer to this attribute. +# is_visible: the attribute is returned in GET responses. +# required_by_policy: the attribute is required by the policy engine and +# should therefore be filled by the API layer even if not present in +# request body. +# validate: specifies rules for validating data in the attribute. +KNOWN_KEYWORDS = ( + 'allow_post', + 'allow_put', + 'convert_to', + 'convert_list_to', + 'default', + 'enforce_policy', + 'is_visible', + 'primary_key', + 'required_by_policy', + 'validate', +) diff --git a/neutron_lib/api/definitions/trunk.py b/neutron_lib/api/definitions/trunk.py new file mode 100644 index 000000000..4f488b9b8 --- /dev/null +++ b/neutron_lib/api/definitions/trunk.py @@ -0,0 +1,97 @@ +# All rights reserved. +# +# 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 import converters + + +# The alias of the extension. +ALIAS = 'trunk' + +# Whether or not this extension is simply signaling behavior to the user +# or it actively modifies the attribute map. +IS_SHIM_EXTENSION = False + +# Whether the extension is marking the adoption of standardattr model for +# legacy resources, or introducing new standardattr attributes. False or +# None if the standardattr model is adopted since the introduction of +# resource extension. +# If this is True, the alias for the extension should be prefixed with +# 'standard-attr-'. +IS_STANDARD_ATTR_EXTENSION = False + +# The name of the extension. +NAME = 'Trunk Extension' + +# The description of the extension. +DESCRIPTION = "Provides support for trunk ports" + +# A timestamp of when the extension was introduced. +UPDATED_TIMESTAMP = "2016-01-01T10:00:00-00:00" + +# The specific resources and/or attributes for the extension (optional). +TRUNK = 'trunk' +TRUNKS = 'trunks' +SUB_PORTS = 'sub_ports' + +# The resource attribute map for the extension. +RESOURCE_ATTRIBUTE_MAP = { + TRUNKS: { + 'admin_state_up': {'allow_post': True, 'allow_put': True, + 'default': True, + 'convert_to': converters.convert_to_boolean, + 'is_visible': True}, + '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:string': 255}, + 'default': '', 'is_visible': True}, + 'tenant_id': {'allow_post': True, 'allow_put': False, + 'required_by_policy': True, + 'validate': + {'type:string': 255}, + 'is_visible': True}, + 'port_id': {'allow_post': True, 'allow_put': False, + 'required_by_policy': True, + 'validate': {'type:uuid': None}, + 'is_visible': True}, + 'status': {'allow_post': False, 'allow_put': False, + 'is_visible': True}, + SUB_PORTS: {'allow_post': True, 'allow_put': False, + 'default': [], + 'convert_list_to': converters.convert_kvp_list_to_dict, + 'validate': {'type:subports': None}, + 'enforce_policy': True, + 'is_visible': True} + }, +} + +# The action map. +ACTION_MAP = { + TRUNK: { + 'add_subports': 'PUT', + 'remove_subports': 'PUT', + 'get_subports': 'GET' + } +} + +# The list of required extensions. +REQUIRED_EXTENSIONS = [ + "binding", +] + +# The list of optional extensions. +OPTIONAL_EXTENSIONS = None + +# TODO(armax): add support for modeling custom queries diff --git a/neutron_lib/api/definitions/trunk_details.py b/neutron_lib/api/definitions/trunk_details.py new file mode 100644 index 000000000..2d9b4f25f --- /dev/null +++ b/neutron_lib/api/definitions/trunk_details.py @@ -0,0 +1,62 @@ +# All rights reserved. +# +# 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 trunk +from neutron_lib import constants + + +# The alias of the extension. +ALIAS = 'trunk-details' + +# Whether or not this extension is simply signaling behavior to the user +# or it actively modifies the attribute map. +IS_SHIM_EXTENSION = False + +# Whether the extension is marking the adoption of standardattr model for +# legacy resources, or introducing new standardattr attributes. False or +# None if the standardattr model is adopted since the introduction of +# resource extension. +# If this is True, the alias for the extension should be prefixed with +# 'standard-attr-'. +IS_STANDARD_ATTR_EXTENSION = False + +# The name of the extension. +NAME = 'Trunk port details' + +# The description of the extension. +DESCRIPTION = "Expose trunk port details" + +# A timestamp of when the extension was introduced. +TIMESTAMP = "2016-01-01T10:00:00-00:00" + +# The specific resources and/or attributes for the extension (optional). +TRUNK_DETAILS = 'trunk_details' + +# The resource attribute map for the extension. +RESOURCE_ATTRIBUTE_MAP = { + 'ports': {TRUNK_DETAILS: {'allow_post': False, 'allow_put': False, + 'default': constants.ATTR_NOT_SPECIFIED, + 'is_visible': True, + 'enforce_policy': True, + 'required_by_policy': True}}, +} + +# The action map. +ACTION_MAP = None + +# The list of required extensions. +REQUIRED_EXTENSIONS = [trunk.ALIAS] + +# The list of optional extensions. +OPTIONAL_EXTENSIONS = None diff --git a/neutron_lib/tests/unit/api/definitions/__init__.py b/neutron_lib/tests/unit/api/definitions/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutron_lib/tests/unit/api/definitions/base.py b/neutron_lib/tests/unit/api/definitions/base.py new file mode 100644 index 000000000..5a0c4713c --- /dev/null +++ b/neutron_lib/tests/unit/api/definitions/base.py @@ -0,0 +1,140 @@ +# 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 base +from neutron_lib.api import validators +from neutron_lib.tests import _base as test_base + + +def assert_bool(tester, attribute, attribute_dict, keyword, value): + tester.assertIsInstance( + value, bool, + '%s must be a boolean for %s.' % (keyword, attribute)) + + +def assert_converter(tester, attribute, attribute_dict, keyword, value): + try: + attribute_dict['convert_to'](attribute_dict['default']) + except KeyError: + try: + attribute_dict['convert_list_to'](attribute_dict['default']) + except KeyError: + if validators.is_attr_set(value) and not isinstance(value, str): + tester.fail("Default value '%s' cannot be converted for " + "attribute %s." % (value, attribute)) + + +def assert_true(tester, attribute, attribute_dict, keyword, value): + tester.assertTrue( + value, '%s must be True for %s.' % (keyword, attribute)) + + +def assert_validator(tester, attribute, attribute_dict, keyword, value): + tester.assertIn(list(value)[0], validators.validators, + '%s is not a known validator for %s.' % (value, attribute)) + +ASSERT_FUNCTIONS = { + 'allow_post': assert_bool, + 'allow_put': assert_bool, + 'convert_to': assert_converter, + 'convert_list_to': assert_converter, + 'default': assert_converter, + 'enforce_policy': assert_bool, + 'is_visible': assert_bool, + 'primary_key': assert_true, + 'required_by_policy': assert_bool, + 'validate': assert_validator, +} + + +class DefinitionBaseTestCase(test_base.BaseTestCase): + + extension_module = None + extension_resources = () + extension_attributes = () + + def setUp(self): + super(DefinitionBaseTestCase, self).setUp() + if not self.extension_module: + self.fail("Missing extension module definition.") + self.alias = self.extension_module.ALIAS + self.is_shim_extension = self.extension_module.IS_SHIM_EXTENSION + self.is_standard_attr_extension = ( + self.extension_module.IS_STANDARD_ATTR_EXTENSION) + self.name = self.extension_module.NAME + self.description = self.extension_module.DESCRIPTION + self.resource_map = self.extension_module.RESOURCE_ATTRIBUTE_MAP + self.action_map = self.extension_module.ACTION_MAP + self.required_extensions = self.extension_module.REQUIRED_EXTENSIONS + self.optional_extensions = self.extension_module.OPTIONAL_EXTENSIONS + + def test_shim_extension(self): + if self.is_shim_extension is True: + self.assertFalse(self.extension_resources) + self.assertFalse(self.extension_attributes) + self.assertFalse(self.resource_map) + self.assertFalse(self.action_map) + + def test_is_standard_attr_extension(self): + if self.is_standard_attr_extension: + self.assertIn('standard-attr-', self.alias) + else: + self.skipTest('API definition is not related to standardattr.') + + def test_resource_map(self): + if not self.resource_map and not self.is_shim_extension: + self.fail('Missing resource map, what is this extension doing?') + elif self.is_shim_extension: + self.skipTest('Shim extension with no API changes.') + + for resource in self.resource_map: + self.assertIn( + resource, base.KNOWN_RESOURCES + self.extension_resources, + 'Resource is unknown, check for typos.') + for attribute in self.resource_map[resource].keys(): + self.assertIn( + attribute, + base.KNOWN_ATTRIBUTES + self.extension_attributes, + 'Attribute is unknown, check for typos.') + for keyword in self.resource_map[resource][attribute]: + self.assertIn(keyword, base.KNOWN_KEYWORDS, + 'Keyword is unknown, check for typos.') + value = self.resource_map[resource][attribute][keyword] + assert_f = ASSERT_FUNCTIONS[keyword] + assert_f(self, attribute, + self.resource_map[resource][attribute], + keyword, value) + + def test_action_map(self): + if not self.action_map: + self.skipTest('API definition has no action map.') + + for key in self.action_map: + for action in self.action_map[key].values(): + self.assertIn(action, base.KNOWN_HTTP_ACTIONS, + 'HTTP verb is unknown, check for typos.') + + def test_required_extensions(self): + if not self.required_extensions: + self.skipTest('API definition has no required extensions.') + + for ext in self.required_extensions: + self.assertIn(ext, base.KNOWN_EXTENSIONS, + 'Required extension is unknown, check for typos.') + + def test_optional_extensions(self): + if not self.optional_extensions: + self.skipTest('API definition has no optional extensions.') + + for ext in self.optional_extensions: + self.assertIn(ext, base.KNOWN_EXTENSIONS, + 'Optional extension is unknown, check for typos.') diff --git a/neutron_lib/tests/unit/api/definitions/test_trunk.py b/neutron_lib/tests/unit/api/definitions/test_trunk.py new file mode 100644 index 000000000..9da186a5c --- /dev/null +++ b/neutron_lib/tests/unit/api/definitions/test_trunk.py @@ -0,0 +1,20 @@ +# 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 trunk +from neutron_lib.tests.unit.api.definitions import base + + +class TrunkDefinitionTestCase(base.DefinitionBaseTestCase): + extension_module = trunk + extension_resources = (trunk.TRUNKS,) + extension_attributes = (trunk.SUB_PORTS,) diff --git a/neutron_lib/tests/unit/api/definitions/test_trunk_details.py b/neutron_lib/tests/unit/api/definitions/test_trunk_details.py new file mode 100644 index 000000000..f46273662 --- /dev/null +++ b/neutron_lib/tests/unit/api/definitions/test_trunk_details.py @@ -0,0 +1,19 @@ +# 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 trunk_details +from neutron_lib.tests.unit.api.definitions import base + + +class TrunkDetailsDefinitionTestCase(base.DefinitionBaseTestCase): + extension_module = trunk_details + extension_attributes = (trunk_details.TRUNK_DETAILS,)