api: Add schema for allocations API (requests)
Change-Id: Ida8bd1d098246f6401605b1bd33ef47f82f4154b Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
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 utils as api_utils
|
||||||
from ironic.api.controllers.v1 import versions
|
from ironic.api.controllers.v1 import versions
|
||||||
from ironic.api import method
|
from ironic.api import method
|
||||||
|
from ironic.api.schemas.v1 import allocation as schema
|
||||||
from ironic.api import validation
|
from ironic.api import validation
|
||||||
from ironic.common import args
|
from ironic.common import args
|
||||||
from ironic.common import exception
|
from ironic.common import exception
|
||||||
@@ -237,19 +238,16 @@ class AllocationsController(pecan.rest.RestController):
|
|||||||
|
|
||||||
@METRICS.timer('AllocationsController.get_all')
|
@METRICS.timer('AllocationsController.get_all')
|
||||||
@method.expose()
|
@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(
|
@validation.api_version(
|
||||||
min_version=versions.MINOR_52_ALLOCATION,
|
min_version=versions.MINOR_52_ALLOCATION,
|
||||||
message=_('The API version does not allow allocations'),
|
message=_('The API version does not allow allocations'),
|
||||||
)
|
)
|
||||||
@args.validate(node=args.uuid_or_name,
|
@validation.request_query_schema(schema.index_request_query, None, 59)
|
||||||
resource_class=args.string,
|
@validation.request_query_schema(schema.index_request_query_v60, 60)
|
||||||
state=args.string,
|
|
||||||
marker=args.uuid,
|
|
||||||
limit=args.integer,
|
|
||||||
sort_key=args.string,
|
|
||||||
sort_dir=args.string,
|
|
||||||
fields=args.string_list,
|
|
||||||
owner=args.string)
|
|
||||||
def get_all(self, node=None, resource_class=None, state=None, marker=None,
|
def get_all(self, node=None, resource_class=None, state=None, marker=None,
|
||||||
limit=None, sort_key='id', sort_dir='asc', fields=None,
|
limit=None, sort_key='id', sort_dir='asc', fields=None,
|
||||||
owner=None):
|
owner=None):
|
||||||
@@ -295,7 +293,13 @@ class AllocationsController(pecan.rest.RestController):
|
|||||||
min_version=versions.MINOR_52_ALLOCATION,
|
min_version=versions.MINOR_52_ALLOCATION,
|
||||||
message=_('The API version does not allow allocations'),
|
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):
|
def get_one(self, allocation_ident, fields=None):
|
||||||
"""Retrieve information about the given allocation.
|
"""Retrieve information about the given allocation.
|
||||||
|
|
||||||
@@ -311,7 +315,6 @@ class AllocationsController(pecan.rest.RestController):
|
|||||||
return convert_with_links(rpc_allocation, fields=fields)
|
return convert_with_links(rpc_allocation, fields=fields)
|
||||||
|
|
||||||
def _authorize_create_allocation(self, allocation):
|
def _authorize_create_allocation(self, allocation):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# PRE-RBAC this rule was logically restricted, it is more-unlocked
|
# PRE-RBAC this rule was logically restricted, it is more-unlocked
|
||||||
# post RBAC, but we need to ensure it is not abused.
|
# 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'),
|
message=_('The API version does not allow allocations'),
|
||||||
exception_class=webob_exc.HTTPMethodNotAllowed,
|
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):
|
def post(self, allocation):
|
||||||
"""Create a new allocation.
|
"""Create a new allocation.
|
||||||
|
|
||||||
@@ -489,7 +493,9 @@ class AllocationsController(pecan.rest.RestController):
|
|||||||
message=_('The API version does not allow updating allocations'),
|
message=_('The API version does not allow updating allocations'),
|
||||||
exception_class=webob_exc.HTTPMethodNotAllowed,
|
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):
|
def patch(self, allocation_ident, patch):
|
||||||
"""Update an existing allocation.
|
"""Update an existing allocation.
|
||||||
|
|
||||||
@@ -533,7 +539,7 @@ class AllocationsController(pecan.rest.RestController):
|
|||||||
message=_('The API version does not allow allocations'),
|
message=_('The API version does not allow allocations'),
|
||||||
exception_class=webob_exc.HTTPMethodNotAllowed,
|
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):
|
def delete(self, allocation_ident):
|
||||||
"""Delete an allocation.
|
"""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