Deploy steps - API & notifications

Adds a 'deploy_step' field to the nodes API.

Co-Authored-By: Ruby Loo <rloo@oath.com>

Change-Id: I1baeeaaa6ed521e4189958fd7624cd6c5de96707
Story: #1753128
Task: #22592
This commit is contained in:
Mark Goddard 2018-07-03 19:37:39 +01:00 committed by Ruby Loo
parent 65a68e4e96
commit 5a14eed467
15 changed files with 84 additions and 24 deletions

View File

@ -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

View File

@ -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.

View File

@ -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": "******",

View File

@ -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": "******",

View File

@ -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",

View File

@ -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": "******",

View File

@ -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,

View File

@ -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)
--------------------

View File

@ -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 = {

View File

@ -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)

View File

@ -115,7 +115,7 @@ RELEASE_MAPPING = {
}
},
'master': {
'api': '1.43',
'api': '1.44',
'rpc': '1.45',
'objects': {
'Node': ['1.26'],

View File

@ -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'),

View File

@ -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)

View File

@ -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

View File

@ -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',