Merge "api: Add schema for allocations API (requests)"
This commit is contained in:
@@ -24,6 +24,7 @@ from ironic.api.controllers.v1 import notification_utils as notify
|
||||
from ironic.api.controllers.v1 import utils as api_utils
|
||||
from ironic.api.controllers.v1 import versions
|
||||
from ironic.api import method
|
||||
from ironic.api.schemas.v1 import allocation as schema
|
||||
from ironic.api import validation
|
||||
from ironic.common import args
|
||||
from ironic.common import exception
|
||||
@@ -237,19 +238,16 @@ class AllocationsController(pecan.rest.RestController):
|
||||
|
||||
@METRICS.timer('AllocationsController.get_all')
|
||||
@method.expose()
|
||||
# TODO(stephenfin): We are currently using this for side-effects to e.g.
|
||||
# convert a CSV string to an array or a string to an integer. We should
|
||||
# probably rename this decorator or provide a separate, simpler decorator.
|
||||
@args.validate(limit=args.integer, fields=args.string_list)
|
||||
@validation.api_version(
|
||||
min_version=versions.MINOR_52_ALLOCATION,
|
||||
message=_('The API version does not allow allocations'),
|
||||
)
|
||||
@args.validate(node=args.uuid_or_name,
|
||||
resource_class=args.string,
|
||||
state=args.string,
|
||||
marker=args.uuid,
|
||||
limit=args.integer,
|
||||
sort_key=args.string,
|
||||
sort_dir=args.string,
|
||||
fields=args.string_list,
|
||||
owner=args.string)
|
||||
@validation.request_query_schema(schema.index_request_query, None, 59)
|
||||
@validation.request_query_schema(schema.index_request_query_v60, 60)
|
||||
def get_all(self, node=None, resource_class=None, state=None, marker=None,
|
||||
limit=None, sort_key='id', sort_dir='asc', fields=None,
|
||||
owner=None):
|
||||
@@ -295,7 +293,13 @@ class AllocationsController(pecan.rest.RestController):
|
||||
min_version=versions.MINOR_52_ALLOCATION,
|
||||
message=_('The API version does not allow allocations'),
|
||||
)
|
||||
@args.validate(allocation_ident=args.uuid_or_name, fields=args.string_list)
|
||||
# TODO(stephenfin): We are currently using this for side-effects to e.g.
|
||||
# convert a CSV string to an array or a string to an integer. We should
|
||||
# probably rename this decorator or provide a separate, simpler decorator.
|
||||
@args.validate(fields=args.string_list)
|
||||
@validation.request_parameter_schema(schema.show_request_parameter)
|
||||
@validation.request_query_schema(schema.show_request_query, None, 59)
|
||||
@validation.request_query_schema(schema.show_request_query_v60, 60)
|
||||
def get_one(self, allocation_ident, fields=None):
|
||||
"""Retrieve information about the given allocation.
|
||||
|
||||
@@ -311,7 +315,6 @@ class AllocationsController(pecan.rest.RestController):
|
||||
return convert_with_links(rpc_allocation, fields=fields)
|
||||
|
||||
def _authorize_create_allocation(self, allocation):
|
||||
|
||||
try:
|
||||
# PRE-RBAC this rule was logically restricted, it is more-unlocked
|
||||
# post RBAC, but we need to ensure it is not abused.
|
||||
@@ -351,7 +354,8 @@ class AllocationsController(pecan.rest.RestController):
|
||||
message=_('The API version does not allow allocations'),
|
||||
exception_class=webob_exc.HTTPMethodNotAllowed,
|
||||
)
|
||||
@args.validate(allocation=ALLOCATION_VALIDATOR)
|
||||
@validation.request_body_schema(schema.create_request_body, None, 57)
|
||||
@validation.request_body_schema(schema.create_request_body_v58, 58)
|
||||
def post(self, allocation):
|
||||
"""Create a new allocation.
|
||||
|
||||
@@ -489,7 +493,9 @@ class AllocationsController(pecan.rest.RestController):
|
||||
message=_('The API version does not allow updating allocations'),
|
||||
exception_class=webob_exc.HTTPMethodNotAllowed,
|
||||
)
|
||||
@args.validate(allocation_ident=args.string, patch=args.patch)
|
||||
@validation.request_parameter_schema(schema.update_request_parameter)
|
||||
@validation.request_body_schema(schema.update_request_body, None, 59)
|
||||
@validation.request_body_schema(schema.update_request_body_v60, 60)
|
||||
def patch(self, allocation_ident, patch):
|
||||
"""Update an existing allocation.
|
||||
|
||||
@@ -533,7 +539,7 @@ class AllocationsController(pecan.rest.RestController):
|
||||
message=_('The API version does not allow allocations'),
|
||||
exception_class=webob_exc.HTTPMethodNotAllowed,
|
||||
)
|
||||
@args.validate(allocation_ident=args.uuid_or_name)
|
||||
@validation.request_parameter_schema(schema.delete_request_parameter)
|
||||
def delete(self, allocation_ident):
|
||||
"""Delete an allocation.
|
||||
|
||||
|
26
ironic/api/schemas/common/request_types.py
Normal file
26
ironic/api/schemas/common/request_types.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# 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.
|
||||
|
||||
|
||||
uuid_or_name = {
|
||||
'anyOf': [
|
||||
{'type': 'string', 'format': 'uuid'},
|
||||
{
|
||||
'type': 'string',
|
||||
'minLength': 1,
|
||||
'maxLength': 255,
|
||||
'pattern': r'^[a-zA-Z0-9-._~]+$',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
sort_dir = {'type': 'string', 'enum': ['asc', 'desc'], 'default': 'asc'}
|
27
ironic/api/schemas/common/response_types.py
Normal file
27
ironic/api/schemas/common/response_types.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# 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 os_traits
|
||||
|
||||
|
||||
STANDARD_TRAITS = os_traits.get_traits()
|
||||
CUSTOM_TRAIT_PATTERN = "^%s[A-Z0-9_]+$" % os_traits.CUSTOM_NAMESPACE
|
||||
|
||||
traits = {
|
||||
'type': 'string',
|
||||
'minLength': 1,
|
||||
'maxLength': 255,
|
||||
'anyOf': [
|
||||
{'pattern': CUSTOM_TRAIT_PATTERN},
|
||||
{'enum': STANDARD_TRAITS},
|
||||
]
|
||||
}
|
173
ironic/api/schemas/v1/allocation.py
Normal file
173
ironic/api/schemas/v1/allocation.py
Normal file
@@ -0,0 +1,173 @@
|
||||
# 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 ironic.api.schemas.common import request_types
|
||||
from ironic.api.schemas.common import response_types
|
||||
|
||||
|
||||
# request parameter schemas
|
||||
|
||||
_allocation_request_parameter = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'allocation_ident': {'type': 'string'},
|
||||
},
|
||||
'required': ['allocation_ident'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
show_request_parameter = copy.deepcopy(_allocation_request_parameter)
|
||||
update_request_parameter = copy.deepcopy(_allocation_request_parameter)
|
||||
delete_request_parameter = copy.deepcopy(_allocation_request_parameter)
|
||||
|
||||
# request query string schemas
|
||||
|
||||
index_request_query = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'fields': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'enum': [
|
||||
'candidate_nodes',
|
||||
'created_at',
|
||||
'extra',
|
||||
'last_error',
|
||||
'links',
|
||||
'name',
|
||||
'node_uuid',
|
||||
'resource_class',
|
||||
'state',
|
||||
'traits',
|
||||
'updated_at',
|
||||
'uuid',
|
||||
],
|
||||
},
|
||||
# OpenAPI-specific properties
|
||||
# https://swagger.io/docs/specification/v3_0/serialization/#query-parameters
|
||||
'style': 'form',
|
||||
'explode': False,
|
||||
},
|
||||
'limit': {'type': 'integer'},
|
||||
'marker': {'type': 'string', 'format': 'uuid'},
|
||||
'node': request_types.uuid_or_name,
|
||||
'owner': {'type': 'string'},
|
||||
'resource_class': {'type': 'string'},
|
||||
'sort_dir': request_types.sort_dir,
|
||||
# TODO(stephenfin): This could probably be narrower but we need to be
|
||||
# careful not to change the response type. If we do, a new microversion
|
||||
# will be needed.
|
||||
'sort_key': {'type': 'string'},
|
||||
'state': {'type': 'string'},
|
||||
},
|
||||
'required': [],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
||||
index_request_query_v60 = copy.deepcopy(index_request_query)
|
||||
index_request_query_v60['properties']['fields']['items']['enum'].append(
|
||||
'owner'
|
||||
)
|
||||
|
||||
show_request_query = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'fields': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'enum': [
|
||||
'candidate_nodes',
|
||||
'created_at',
|
||||
'extra',
|
||||
'last_error',
|
||||
'links',
|
||||
'name',
|
||||
'node_uuid',
|
||||
'resource_class',
|
||||
'state',
|
||||
'traits',
|
||||
'updated_at',
|
||||
'uuid',
|
||||
],
|
||||
},
|
||||
# OpenAPI-specific properties
|
||||
# https://swagger.io/docs/specification/v3_0/serialization/#query-parameters
|
||||
'style': 'form',
|
||||
'explode': False,
|
||||
},
|
||||
},
|
||||
'required': [],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
||||
show_request_query_v60 = copy.deepcopy(show_request_query)
|
||||
show_request_query_v60['properties']['fields']['items']['enum'].append(
|
||||
'owner'
|
||||
)
|
||||
|
||||
# request body schemas
|
||||
|
||||
create_request_body = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'candidate_nodes': {
|
||||
'type': ['array', 'null'],
|
||||
'items': request_types.uuid_or_name,
|
||||
},
|
||||
'extra': {'type': ['object', 'null']},
|
||||
# TODO(stephenfin): We'd like to use request_types.uuid_or_name here
|
||||
# but doing so will change the error response
|
||||
'name': {'type': ['string', 'null']},
|
||||
# TODO(stephenfin): The docs say that owner is only present in v1.60+,
|
||||
# but I can't see anything in the code to prevent this in the POST
|
||||
# request, only in the GET request and all responses
|
||||
'owner': {'type': ['string', 'null']},
|
||||
'resource_class': {'type': ['string', 'null'], 'maxLength': 80},
|
||||
'traits': {
|
||||
'type': ['array', 'null'],
|
||||
'items': response_types.traits,
|
||||
},
|
||||
'uuid': {'type': ['string', 'null']},
|
||||
},
|
||||
# TODO(stephenfin): The resource_class field is required when node is not
|
||||
# provided. We'd like to express this here, but doing so will change the
|
||||
# error response.
|
||||
'required': [],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
||||
create_request_body_v58 = copy.deepcopy(create_request_body)
|
||||
create_request_body_v58['properties'].update({
|
||||
'node': {'type': ['string', 'null']},
|
||||
})
|
||||
|
||||
# TODO(stephenfin): This needs to be completed. We probably want a helper to
|
||||
# generate these since they are superficially identical, with only the allowed
|
||||
# patch fields changing
|
||||
update_request_body = {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'op': {'enum': ['add', 'replace', 'remove']},
|
||||
'path': {'type': 'string'},
|
||||
'value': {'type': ['string', 'object', 'null']},
|
||||
},
|
||||
'required': ['op', 'path'],
|
||||
'additionalProperties': False,
|
||||
},
|
||||
}
|
||||
|
||||
# TODO(stephenfin): The code suggests that we should be allowing 'owner' here,
|
||||
# but it's not included in PATCH_ALLOWED_FIELDS so I have ignored it for now
|
||||
update_request_body_v60 = copy.deepcopy(update_request_body)
|
Reference in New Issue
Block a user