From e07ea29033a0e42acd31f8d482b7a0c06105a934 Mon Sep 17 00:00:00 2001 From: Marco Wehrhahn Date: Sat, 18 Oct 2025 17:59:17 -0400 Subject: [PATCH] Add schema validation for Share Snapshot list API Introduces JSON schema validation for the share snapshot list (index) API across supported microversions. It defines request query and response body schemas for versions 2.0 through 2.79, covering pagination, sorting, filtering, and count parameters. Prepares the groundwork for full controller validation, which will include request body schemas for create and update operations in a follow-up Change-Id: I308d942ae460e881cf37f95f393c799c75d5bd62 Signed-off-by: Marco Wehrhahn --- manila/api/schemas/share_snapshots.py | 149 ++++++++++++++++++++++++++ manila/api/v2/share_snapshots.py | 9 ++ 2 files changed, 158 insertions(+) create mode 100644 manila/api/schemas/share_snapshots.py diff --git a/manila/api/schemas/share_snapshots.py b/manila/api/schemas/share_snapshots.py new file mode 100644 index 0000000000..b1bdce5dcf --- /dev/null +++ b/manila/api/schemas/share_snapshots.py @@ -0,0 +1,149 @@ +# 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. +import copy +from oslo_config import cfg + +from manila.api.validation import parameter_types +from manila.api.validation import response_types + +CONF = cfg.CONF + +# Base list snapshots query (v2.0+) +index_request_query = { + 'type': 'object', + 'properties': { + # Pagination + 'limit': parameter_types.single_param( + parameter_types.non_negative_integer + ), + 'offset': parameter_types.single_param( + parameter_types.non_negative_integer + ), + + # Sorting + 'sort_key': parameter_types.single_param({ + 'type': 'string', + 'minLength': 1, + 'maxLength': 255, + }), + 'sort_dir': parameter_types.single_param({ + 'type': 'string', + 'default': 'desc', + 'description': 'Sort direction', + }), + + # Project Scoping + 'project_id': parameter_types.single_param({ + 'type': 'string', + 'minLength': 1, + 'maxLength': 255, + }), + # Admin only + 'all_tenants': { + **parameter_types.boolean, + 'enum': [1, 0], + 'description': ( + "Set 1 to list resources for all projects;" + "set 0 to list resources only for the current project" + )}, + + # Basic filters + 'name': parameter_types.single_param({ + 'type': 'string', + 'minLength': 1, + 'maxLength': 255, + }), + 'description': parameter_types.single_param({ + 'type': 'string', + 'minLength': 1, + 'maxLength': 255, + }), + 'status': parameter_types.single_param({ + 'type': 'string', + 'minLength': 1, + 'maxLength': 255, + }), + 'share_id': parameter_types.single_param({ + 'type': 'string', + 'minLength': 1, + 'maxLength': 255, + }), + 'size': parameter_types.single_param( + parameter_types.non_negative_integer, + ), + }, + 'required': [], + 'additionalProperties': True, +} + +# >= v2.36: like filters for name~/description~ +index_request_query_v236 = copy.copy(index_request_query) +index_request_query_v236['properties'].update({ + 'name~': parameter_types.single_param({ + 'type': 'string', + 'minLength': 1, + 'maxLength': 255, + }), + 'description~': parameter_types.single_param({ + 'type': 'string', + 'minLength': 1, + 'maxLength': 255 + }), +}) + +# >= v2.73: metadata filter +index_request_query_v273 = copy.copy(index_request_query_v236) +index_request_query_v273['properties'].update({ + 'metadata': parameter_types.single_param({ + 'type': 'string', + 'minLength': 1, + 'maxLength': 4096, + }), +}) + +# >= v2.79: with_count flag added on top +index_request_query_v279 = copy.copy(index_request_query_v273) +index_request_query_v279['properties'].update({ + 'with_count': parameter_types.single_param({ + **parameter_types.boolean, + 'default': False, + 'description': "Show count in share snapshot list API response" + }), +}) + +_snapshot_response = { + 'type': 'object', + 'properties': { + 'id': {'type': ['string', 'integer'], + 'description': "The UUID of the snapshot."}, + 'links': response_types.links, + 'name': {'type': ['string', 'null'], + 'description': "The user-defined name of the snapshot."}, + }, + 'required': ['id', 'links', 'name'], + 'additionalProperties': False, +} + +index_response_body = { + 'type': 'object', + 'properties': { + 'snapshots': { + 'type': 'array', + 'items': _snapshot_response, + }, + 'share_snapshots_links': response_types.collection_links, + # >= v2.79 when with_count=True + 'count': {'type': 'integer', 'minimum': 0}, + }, + 'required': ['snapshots'], + 'additionalProperties': False, +} diff --git a/manila/api/v2/share_snapshots.py b/manila/api/v2/share_snapshots.py index e60c34251e..279080cc1a 100644 --- a/manila/api/v2/share_snapshots.py +++ b/manila/api/v2/share_snapshots.py @@ -25,8 +25,10 @@ from webob import exc from manila.api import common from manila.api.openstack import api_version_request as api_version from manila.api.openstack import wsgi +from manila.api.schemas import share_snapshots as schema from manila.api.v1 import share_snapshots from manila.api.v2 import metadata +from manila.api import validation from manila.api.views import share_snapshots as snapshot_views from manila.common import constants from manila.db import api as db_api @@ -333,6 +335,13 @@ class ShareSnapshotsController(share_snapshots.ShareSnapshotMixin, return self._access_list(req, snapshot_id) @wsgi.Controller.api_version("2.0") + @validation.request_query_schema(schema.index_request_query, "2.0", "2.35") + @validation.request_query_schema( + schema.index_request_query_v236, "2.36", "2.72") + @validation.request_query_schema( + schema.index_request_query_v273, "2.73", "2.78") + @validation.request_query_schema(schema.index_request_query_v279, "2.79") + @validation.response_body_schema(schema.index_response_body) def index(self, req): """Returns a summary list of shares.""" if req.api_version_request < api_version.APIVersionRequest("2.36"):