diff --git a/api-ref/source/baremetal-api-v1-nodes.inc b/api-ref/source/baremetal-api-v1-nodes.inc index 6a6eaf1d14..13b57cad1d 100644 --- a/api-ref/source/baremetal-api-v1-nodes.inc +++ b/api-ref/source/baremetal-api-v1-nodes.inc @@ -86,6 +86,9 @@ supplied when the Node is created, or the resource may be updated later. Introduced the ``rescue_interface`` field. If this field is not supplied when creating the Node, the default value will be used. +.. versionadded:: 1.44 + Introduced the ``deploy_step`` field. + Normal response codes: 201 Error codes: 400,403,406 @@ -132,7 +135,7 @@ and any defaults added for non-specified fields. Most fields default to "null" or "". The list and example below are representative of the response as of API -microversion 1.43. +microversion 1.44. .. rest_parameters:: parameters.yaml @@ -159,6 +162,7 @@ microversion 1.43. - raid_config: raid_config - target_raid_config: target_raid_config - clean_step: clean_step + - deploy_step: deploy_step - links: links - ports: n_ports - portgroups: n_portgroups @@ -333,6 +337,7 @@ Response - raid_config: raid_config - target_raid_config: target_raid_config - clean_step: clean_step + - deploy_step: deploy_step - links: links - ports: n_ports - portgroups: n_portgroups @@ -416,6 +421,7 @@ Response - raid_config: raid_config - target_raid_config: target_raid_config - clean_step: clean_step + - deploy_step: deploy_step - links: links - ports: n_ports - portgroups: n_portgroups @@ -500,6 +506,7 @@ Response - raid_config: raid_config - target_raid_config: target_raid_config - clean_step: clean_step + - deploy_step: deploy_step - links: links - ports: n_ports - portgroups: n_portgroups diff --git a/api-ref/source/parameters.yaml b/api-ref/source/parameters.yaml index 240ce9ab3e..f3f2eb6dc2 100644 --- a/api-ref/source/parameters.yaml +++ b/api-ref/source/parameters.yaml @@ -492,6 +492,12 @@ deploy_interface: in: body required: true type: string +deploy_step: + description: | + The current deploy step. + in: body + required: false + type: string description: description: | Descriptive text about the Ironic service. diff --git a/api-ref/source/samples/node-create-response.json b/api-ref/source/samples/node-create-response.json index 64605e6677..d9e66049e0 100644 --- a/api-ref/source/samples/node-create-response.json +++ b/api-ref/source/samples/node-create-response.json @@ -6,6 +6,7 @@ "console_interface": null, "created_at": "2016-08-18T22:28:48.643434+11:11", "deploy_interface": null, + "deploy_step": {}, "driver": "agent_ipmitool", "driver_info": { "ipmi_password": "******", diff --git a/api-ref/source/samples/node-show-response.json b/api-ref/source/samples/node-show-response.json index 95e27c4fff..bea794cd10 100644 --- a/api-ref/source/samples/node-show-response.json +++ b/api-ref/source/samples/node-show-response.json @@ -6,6 +6,7 @@ "console_interface": null, "created_at": "2016-08-18T22:28:48.643434+11:11", "deploy_interface": null, + "deploy_step": {}, "driver": "fake", "driver_info": { "ipmi_password": "******", diff --git a/api-ref/source/samples/node-update-driver-info-response.json b/api-ref/source/samples/node-update-driver-info-response.json index 36dadef978..6723b2878b 100644 --- a/api-ref/source/samples/node-update-driver-info-response.json +++ b/api-ref/source/samples/node-update-driver-info-response.json @@ -6,6 +6,7 @@ "console_interface": null, "created_at": "2016-08-18T22:28:48.643434+11:11", "deploy_interface": null, + "deploy_step": {}, "driver": "fake", "driver_info": { "deploy_kernel": "http://127.0.0.1/images/kernel", diff --git a/api-ref/source/samples/nodes-list-details-response.json b/api-ref/source/samples/nodes-list-details-response.json index 7d17ce415e..96e181b02b 100644 --- a/api-ref/source/samples/nodes-list-details-response.json +++ b/api-ref/source/samples/nodes-list-details-response.json @@ -8,6 +8,7 @@ "console_interface": null, "created_at": "2016-08-18T22:28:48.643434+11:11", "deploy_interface": null, + "deploy_step": {}, "driver": "fake", "driver_info": { "ipmi_password": "******", @@ -105,6 +106,7 @@ "console_interface": "no-console", "created_at": "2016-08-18T22:28:48.643434+11:11", "deploy_interface": "iscsi", + "deploy_step": {}, "driver": "ipmi", "driver_info": { "ipmi_password": "******", diff --git a/doc/source/admin/notifications.rst b/doc/source/admin/notifications.rst index 04a87db18a..4d4bffe1d6 100644 --- a/doc/source/admin/notifications.rst +++ b/doc/source/admin/notifications.rst @@ -132,12 +132,13 @@ Example of node CRUD notification:: "payload":{ "ironic_object.namespace":"ironic", "ironic_object.name":"NodeCRUDPayload", - "ironic_object.version":"1.5", + "ironic_object.version":"1.6", "ironic_object.data":{ "chassis_uuid": "db0eef9d-45b2-4dc0-94a8-fc283c01171f", "clean_step": None, "console_enabled": False, "created_at": "2016-01-26T20:41:03+00:00", + "deploy_step": None, "driver": "ipmi", "driver_info": { "ipmi_address": "192.168.0.111", @@ -359,7 +360,7 @@ node maintenance notification:: "payload":{ "ironic_object.namespace":"ironic", "ironic_object.name":"NodePayload", - "ironic_object.version":"1.8", + "ironic_object.version":"1.9", "ironic_object.data":{ "clean_step": None, "console_enabled": False, @@ -440,7 +441,7 @@ level, "error" has ERROR. Example of node console notification:: "payload":{ "ironic_object.namespace":"ironic", "ironic_object.name":"NodePayload", - "ironic_object.version":"1.8", + "ironic_object.version":"1.9", "ironic_object.data":{ "clean_step": None, "console_enabled": True, @@ -515,11 +516,12 @@ ironic-conductor is attempting to change the node:: "payload":{ "ironic_object.namespace":"ironic", "ironic_object.name":"NodeSetPowerStatePayload", - "ironic_object.version":"1.7", + "ironic_object.version":"1.9", "ironic_object.data":{ "clean_step": None, "console_enabled": False, "created_at": "2016-01-26T20:41:03+00:00", + "deploy_step": None, "driver": "ipmi", "extra": {}, "inspection_finished_at": None, @@ -583,11 +585,12 @@ prior to the correction:: "payload":{ "ironic_object.namespace":"ironic", "ironic_object.name":"NodeCorrectedPowerStatePayload", - "ironic_object.version":"1.7", + "ironic_object.version":"1.9", "ironic_object.data":{ "clean_step": None, "console_enabled": False, "created_at": "2016-01-26T20:41:03+00:00", + "deploy_step": None, "driver": "ipmi", "extra": {}, "inspection_finished_at": None, @@ -662,11 +665,12 @@ indicate a node's provision states before state change, "event" is the FSM "payload":{ "ironic_object.namespace":"ironic", "ironic_object.name":"NodeSetProvisionStatePayload", - "ironic_object.version":"1.7", + "ironic_object.version":"1.9", "ironic_object.data":{ "clean_step": None, "console_enabled": False, "created_at": "2016-01-26T20:41:03+00:00", + "deploy_step": None, "driver": "ipmi", "extra": {}, "inspection_finished_at": None, diff --git a/doc/source/contributor/webapi-version-history.rst b/doc/source/contributor/webapi-version-history.rst index 64c3d58323..d37cdb3a67 100644 --- a/doc/source/contributor/webapi-version-history.rst +++ b/doc/source/contributor/webapi-version-history.rst @@ -2,6 +2,12 @@ REST API Version History ======================== +1.44 (Rocky, master) +-------------------- + +Added ``deploy_step`` to the node object, to indicate the current deploy +step (if any) being performed on the node. + 1.43 (Rocky, 11.0.0) -------------------- diff --git a/ironic/api/controllers/v1/node.py b/ironic/api/controllers/v1/node.py index 597c8ce4e0..7b3fac63a9 100644 --- a/ironic/api/controllers/v1/node.py +++ b/ironic/api/controllers/v1/node.py @@ -150,6 +150,9 @@ def hide_fields_in_newer_versions(obj): if pecan.request.version.minor < versions.MINOR_42_FAULT: obj.fault = wsme.Unset + if pecan.request.version.minor < versions.MINOR_44_NODE_DEPLOY_STEP: + obj.deploy_step = wsme.Unset + if not api_utils.allow_resource_class(): obj.resource_class = wsme.Unset @@ -990,6 +993,9 @@ class Node(base.APIBase): clean_step = wsme.wsattr({wtypes.text: types.jsontype}, readonly=True) """The current clean step""" + deploy_step = wsme.wsattr({wtypes.text: types.jsontype}, readonly=True) + """The current deploy step""" + raid_config = wsme.wsattr({wtypes.text: types.jsontype}, readonly=True) """Represents the current RAID configuration of the node """ @@ -1215,7 +1221,7 @@ class Node(base.APIBase): provision_updated_at=time, instance_info={}, maintenance=False, maintenance_reason=None, fault=None, inspection_finished_at=None, inspection_started_at=time, - console_enabled=False, clean_step={}, + console_enabled=False, clean_step={}, deploy_step={}, raid_config=None, target_raid_config=None, network_interface='flat', resource_class='baremetal-gold', boot_interface=None, console_interface=None, @@ -1247,6 +1253,7 @@ class NodePatchType(types.JsonPatchType): '/provision_updated_at', '/maintenance_reason', '/driver_internal_info', '/inspection_finished_at', '/inspection_started_at', '/clean_step', + '/deploy_step', '/raid_config', '/target_raid_config', '/fault'] @@ -1484,7 +1491,8 @@ class NodesController(rest.RestController): invalid_sort_key_list = ['properties', 'driver_info', 'extra', 'instance_info', 'driver_internal_info', - 'clean_step', 'raid_config', 'target_raid_config', + 'clean_step', 'deploy_step', + 'raid_config', 'target_raid_config', 'traits'] _subcontroller_map = { diff --git a/ironic/api/controllers/v1/versions.py b/ironic/api/controllers/v1/versions.py index f1f61b0cbc..965062f864 100644 --- a/ironic/api/controllers/v1/versions.py +++ b/ironic/api/controllers/v1/versions.py @@ -81,6 +81,7 @@ BASE_VERSION = 1 # v1.41: Add inspection abort support. # v1.42: Expose fault field to node. # v1.43: Add detail=True flag to all API endpoints +# v1.44: Add node deploy_step field MINOR_0_JUNO = 0 MINOR_1_INITIAL_VERSION = 1 @@ -126,6 +127,7 @@ MINOR_40_BIOS_INTERFACE = 40 MINOR_41_INSPECTION_ABORT = 41 MINOR_42_FAULT = 42 MINOR_43_ENABLE_DETAIL_QUERY = 43 +MINOR_44_NODE_DEPLOY_STEP = 44 # When adding another version, update: # - MINOR_MAX_VERSION @@ -133,7 +135,7 @@ MINOR_43_ENABLE_DETAIL_QUERY = 43 # explanation of what changed in the new version # - common/release_mappings.py, RELEASE_MAPPING['master']['api'] -MINOR_MAX_VERSION = MINOR_43_ENABLE_DETAIL_QUERY +MINOR_MAX_VERSION = MINOR_44_NODE_DEPLOY_STEP # String representations of the minor and maximum versions _MIN_VERSION_STRING = '{}.{}'.format(BASE_VERSION, MINOR_1_INITIAL_VERSION) diff --git a/ironic/common/release_mappings.py b/ironic/common/release_mappings.py index fd21b24566..01a626e3fd 100644 --- a/ironic/common/release_mappings.py +++ b/ironic/common/release_mappings.py @@ -115,7 +115,7 @@ RELEASE_MAPPING = { } }, 'master': { - 'api': '1.43', + 'api': '1.44', 'rpc': '1.45', 'objects': { 'Node': ['1.26'], diff --git a/ironic/objects/node.py b/ironic/objects/node.py index fc01ad0d28..8afc57e8d5 100644 --- a/ironic/objects/node.py +++ b/ironic/objects/node.py @@ -633,11 +633,13 @@ class NodePayload(notification.NotificationPayloadBase): # Version 1.6: Add traits field exposed via API. # Version 1.7: Add fault field exposed via API. # Version 1.8: Add bios interface field exposed via API. - VERSION = '1.8' + # Version 1.9: Add deploy_step field exposed via API. + VERSION = '1.9' fields = { 'clean_step': object_fields.FlexibleDictField(nullable=True), 'console_enabled': object_fields.BooleanField(nullable=True), 'created_at': object_fields.DateTimeField(nullable=True), + 'deploy_step': object_fields.FlexibleDictField(nullable=True), 'driver': object_fields.StringField(nullable=True), 'extra': object_fields.FlexibleDictField(nullable=True), 'inspection_finished_at': object_fields.DateTimeField(nullable=True), @@ -706,7 +708,8 @@ class NodeSetPowerStatePayload(NodePayload): # Version 1.6: Parent NodePayload version 1.6 # Version 1.7: Parent NodePayload version 1.7 # Version 1.8: Parent NodePayload version 1.8 - VERSION = '1.8' + # Version 1.9: Parent NodePayload version 1.9 + VERSION = '1.9' fields = { # "to_power" indicates the future target_power_state of the node. A @@ -755,7 +758,8 @@ class NodeCorrectedPowerStatePayload(NodePayload): # Version 1.6: Parent NodePayload version 1.6 # Version 1.7: Parent NodePayload version 1.7 # Version 1.8: Parent NodePayload version 1.8 - VERSION = '1.8' + # Version 1.9: Parent NodePayload version 1.9 + VERSION = '1.9' fields = { 'from_power': object_fields.StringField(nullable=True) @@ -788,7 +792,8 @@ class NodeSetProvisionStatePayload(NodePayload): # Version 1.6: Parent NodePayload version 1.6 # Version 1.7: Parent NodePayload version 1.7 # Version 1.8: Parent NodePayload version 1.8 - VERSION = '1.8' + # Version 1.9: Parent NodePayload version 1.9 + VERSION = '1.9' SCHEMA = dict(NodePayload.SCHEMA, **{'instance_info': ('node', 'instance_info')}) @@ -828,7 +833,8 @@ class NodeCRUDPayload(NodePayload): # Version 1.4: Parent NodePayload version 1.6 # Version 1.5: Parent NodePayload version 1.7 # Version 1.6: Parent NodePayload version 1.8 - VERSION = '1.6' + # Version 1.7: Parent NodePayload version 1.9 + VERSION = '1.7' SCHEMA = dict(NodePayload.SCHEMA, **{'instance_info': ('node', 'instance_info'), diff --git a/ironic/tests/unit/api/controllers/v1/test_node.py b/ironic/tests/unit/api/controllers/v1/test_node.py index ea4f8599df..ae10389033 100644 --- a/ironic/tests/unit/api/controllers/v1/test_node.py +++ b/ironic/tests/unit/api/controllers/v1/test_node.py @@ -122,6 +122,7 @@ class TestListNodes(test_api_base.BaseApiTest): # never expose the chassis_id self.assertNotIn('chassis_id', data['nodes'][0]) self.assertNotIn('bios_interface', data['nodes'][0]) + self.assertNotIn('deploy_step', data['nodes'][0]) def test_get_one(self): node = obj_utils.create_test_node(self.context, @@ -158,6 +159,7 @@ class TestListNodes(test_api_base.BaseApiTest): # never expose the chassis_id self.assertNotIn('chassis_id', data) self.assertIn('bios_interface', data) + self.assertIn('deploy_step', data) def test_get_one_with_json(self): # Test backward compatibility with guess_content_type_from_ext @@ -250,6 +252,10 @@ class TestListNodes(test_api_base.BaseApiTest): self._test_node_field_hidden_in_lower_version('fault', '1.41', '1.42') + def test_node_deploy_step_hidden_in_lower_version(self): + self._test_node_field_hidden_in_lower_version('deploy_step', + '1.43', '1.44') + def test_get_one_custom_fields(self): node = obj_utils.create_test_node(self.context, chassis_id=self.chassis.id) @@ -2419,6 +2425,19 @@ class TestPatch(test_api_base.BaseApiTest): self.assertEqual(http_client.BAD_REQUEST, response.status_code) self.assertTrue(response.json['error_message']) + def test_patch_deploy_step_forbidden(self): + node = obj_utils.create_test_node(self.context, + uuid=uuidutils.generate_uuid()) + response = self.patch_json('/nodes/%s' % node.uuid, + [{'path': '/deploy_step', + 'op': 'replace', + 'value': 'deploy this'}], + headers={api_base.Version.string: "1.43"}, + expect_errors=True) + self.assertEqual('application/json', response.content_type) + self.assertEqual(http_client.BAD_REQUEST, response.status_code) + self.assertTrue(response.json['error_message']) + def _create_node_locally(node): driver_factory.check_and_update_node_interfaces(node) diff --git a/ironic/tests/unit/api/utils.py b/ironic/tests/unit/api/utils.py index 0b4fab7ef4..233bea1a5d 100644 --- a/ironic/tests/unit/api/utils.py +++ b/ironic/tests/unit/api/utils.py @@ -100,9 +100,6 @@ def node_post_data(**kw): node.pop('chassis_id') node.pop('tags') node.pop('traits') - # TODO(mgoddard): Remove this once the deploy_step field is supported in - # the API. - node.pop('deploy_step') # NOTE(jroll): pop out fields that were introduced in later API versions, # unless explicitly requested. Otherwise, these will cause tests using diff --git a/ironic/tests/unit/objects/test_objects.py b/ironic/tests/unit/objects/test_objects.py index 9a72d68fd0..ee87a94e68 100644 --- a/ironic/tests/unit/objects/test_objects.py +++ b/ironic/tests/unit/objects/test_objects.py @@ -672,21 +672,21 @@ expected_object_fingerprints = { 'Conductor': '1.2-5091f249719d4a465062a1b3dc7f860d', 'EventType': '1.1-aa2ba1afd38553e3880c267404e8d370', 'NotificationPublisher': '1.0-51a09397d6c0687771fb5be9a999605d', - 'NodePayload': '1.8-2c327dfc77ce8c7a28b02eafaed509f0', + 'NodePayload': '1.9-c0aa5dd602adca3a28f091ca7848a41b', 'NodeSetPowerStateNotification': '1.0-59acc533c11d306f149846f922739c15', - 'NodeSetPowerStatePayload': '1.8-916c39939609254de1f8050a1c8da7d1', + 'NodeSetPowerStatePayload': '1.9-07cd9053faa76199ffeaa45db78728a2', 'NodeCorrectedPowerStateNotification': '1.0-59acc533c11d306f149846f922739c15', - 'NodeCorrectedPowerStatePayload': '1.8-1afef2b5c0538941defa00232302cd89', + 'NodeCorrectedPowerStatePayload': '1.9-321b389cd4aa2552da25a5001f7fcb71', 'NodeSetProvisionStateNotification': '1.0-59acc533c11d306f149846f922739c15', - 'NodeSetProvisionStatePayload': '1.8-d4aa7eb5a449e9f8271b3127a2d2b6ea', + 'NodeSetProvisionStatePayload': '1.9-995f311c21e5e54bf2183db4056542a5', 'VolumeConnector': '1.0-3e0252c0ab6e6b9d158d09238a577d97', 'VolumeTarget': '1.0-0b10d663d8dae675900b2c7548f76f5e', 'ChassisCRUDNotification': '1.0-59acc533c11d306f149846f922739c15', 'ChassisCRUDPayload': '1.0-dce63895d8186279a7dd577cffccb202', 'NodeCRUDNotification': '1.0-59acc533c11d306f149846f922739c15', - 'NodeCRUDPayload': '1.6-3ab7db0fc4574999fca91a1f04b4998f', + 'NodeCRUDPayload': '1.7-af856321c0531f52be431217214d203a', 'PortCRUDNotification': '1.0-59acc533c11d306f149846f922739c15', 'PortCRUDPayload': '1.2-233d259df442eb15cc584fae1fe81504', 'NodeMaintenanceNotification': '1.0-59acc533c11d306f149846f922739c15',