Merge "api: Add schema for node firmware API"

This commit is contained in:
Zuul
2025-06-04 17:30:44 +00:00
committed by Gerrit Code Review
3 changed files with 109 additions and 3 deletions

View File

@ -17,8 +17,12 @@ from pecan import rest
from ironic import api
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 firmware as schema
from ironic.api import validation
from ironic.common import args
from ironic.common.i18n import _
from ironic.common import metrics_utils
from ironic import objects
@ -59,7 +63,17 @@ class NodeFirmwareController(rest.RestController):
@METRICS.timer('NodeFirmwareController.get_all')
@method.expose()
@args.validate(fields=args.string_list, detail=args.boolean)
# TODO(adamcarthur): 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.api_version(
min_version=versions.MINOR_86_FIRMWARE_INTERFACE,
message=_('The API version does not allow node firmware'),
)
@validation.request_query_schema(schema.index_request_query, 86)
@validation.response_body_schema(schema.index_response_body, 86)
def get_all(self, detail=None, fields=None):
"""List node firmware components."""
node = api_utils.check_node_policy_and_retrieve(

View File

@ -0,0 +1,69 @@
# 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.
index_request_query = {
'type': 'object',
'properties': {
'fields': {
'type': 'array',
'items': {
'enum': [
'created_at',
'updated_at',
'component',
'initial_version',
'current_version',
'last_version_flashed',
],
},
# OpenAPI-specific properties
# https://swagger.io/docs/specification/v3_0/serialization/#query-parameters
'style': 'form',
'explode': False,
},
'detail': {'type': 'boolean'},
},
'required': [],
'additionalProperties': False,
}
_firmware_response_body = {
'type': 'object',
'properties': {
'component': {'type': 'string'},
'initial_version': {'type': ['string', 'null']},
'current_version': {'type': ['string', 'null']},
'last_version_flashed': {'type': ['string', 'null']},
'created_at': {'type': 'string', 'format': 'date-time'},
'updated_at': {'type': ['string', 'null'], 'format': 'date-time'},
},
# NOTE(adamcarthur, stephenfin): The 'fields' parameter
# means nothing is required
'required': [],
'additionalProperties': False,
}
index_response_body = {
'type': 'object',
'properties': {
'firmware': {
'type': 'array',
'items': _firmware_response_body,
},
},
'required': ['firmware'],
'additionalProperties': False,
}
index_response_body['properties'].update({
'next': {'type': 'string'},
})

View File

@ -9008,8 +9008,9 @@ class TestNodeFirmwareComponent(test_api_base.BaseApiTest):
self.context, node_id=self.node.id, component='BIOS')
def test_get_all_firmware_components(self):
ret = self.get_json('/nodes/%s/firmware' % self.node.uuid,
headers={api_base.Version.string: self.version})
ret = self.get_json(
'/nodes/%s/firmware' % self.node.uuid,
headers={api_base.Version.string: self.version})
expected_components = [
{'created_at': ret['firmware'][0]['created_at'],
'updated_at': ret['firmware'][0]['updated_at'],
@ -9023,6 +9024,28 @@ class TestNodeFirmwareComponent(test_api_base.BaseApiTest):
'last_version_flashed': None}]
self.assertEqual({'firmware': expected_components}, ret)
def test_get_all_custom_fields(self):
fields = 'component,last_version_flashed'
ret = self.get_json(
'/nodes/%s/firmware?fields=%s' % (self.node.uuid, fields),
headers={api_base.Version.string: self.version})
expected_components = [
# We always append "created_at", "updated_at"
{
'created_at': ret['firmware'][0]['created_at'],
'updated_at': ret['firmware'][0]['updated_at'],
'component': 'BIOS',
'last_version_flashed': None,
},
{
'created_at': ret['firmware'][1]['created_at'],
'updated_at': ret['firmware'][1]['updated_at'],
'component': 'bmc',
'last_version_flashed': None,
}
]
self.assertEqual({'firmware': expected_components}, ret)
def test_wrong_version_get_all_firmware_components_old_version(self):
ret = self.get_json('/nodes/%s/firmware' % self.node.uuid,
headers={api_base.Version.string: "1.81"},