Merge "Allow setting of disable_power_off via API"
This commit is contained in:
commit
3421bb614a
@ -460,6 +460,9 @@ detailed documentation of the Ironic State Machine is available
|
||||
approved for the given node, as an alternative to providing ``clean_steps``
|
||||
or ``service_steps`` dictionary.
|
||||
|
||||
.. versionadded:: 1.95
|
||||
Added the ability to set/unset ``disable_power_off`` on a node.
|
||||
|
||||
Normal response code: 202
|
||||
|
||||
Error codes:
|
||||
|
@ -119,6 +119,9 @@ supplied when the Node is created, or the resource may be updated later.
|
||||
.. versionadded: 1.83
|
||||
Introduced the ``parent_node`` field.
|
||||
|
||||
.. versionadded: 1.95
|
||||
Introduced the ``disable_power_off`` field.
|
||||
|
||||
Normal response codes: 201
|
||||
|
||||
Error codes: 400,403,406
|
||||
@ -132,6 +135,7 @@ Request
|
||||
- conductor_group: req_conductor_group
|
||||
- console_interface: req_console_interface
|
||||
- deploy_interface: req_deploy_interface
|
||||
- disable_power_off: req_disable_power_off
|
||||
- driver_info: req_driver_info
|
||||
- driver: req_driver_name
|
||||
- extra: req_extra
|
||||
@ -178,7 +182,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.81.
|
||||
microversion 1.95.
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
@ -239,6 +243,7 @@ microversion 1.81.
|
||||
- network_data: network_data
|
||||
- retired: retired
|
||||
- retired_reason: retired_reason
|
||||
- disable_power_off: disable_power_off
|
||||
|
||||
**Example JSON representation of a Node:**
|
||||
|
||||
@ -504,6 +509,7 @@ Response
|
||||
- inspection_finished_at: inspection_finished_at
|
||||
- created_at: created_at
|
||||
- updated_at: updated_at
|
||||
- disable_power_off: disable_power_off
|
||||
|
||||
**Example detailed list of Nodes:**
|
||||
|
||||
@ -562,6 +568,9 @@ only the specified set.
|
||||
.. versionadded:: 1.83
|
||||
Introduced the ``parent_node`` field.
|
||||
|
||||
.. versionadded:: 1.95
|
||||
Introduced the ``disable_power_off`` field.
|
||||
|
||||
Normal response codes: 200
|
||||
|
||||
Error codes: 400,403,404,406
|
||||
@ -632,6 +641,7 @@ Response
|
||||
- conductor: conductor
|
||||
- allocation_uuid: allocation_uuid
|
||||
- network_data: network_data
|
||||
- disable_power_off: disable_power_off
|
||||
|
||||
**Example JSON representation of a Node:**
|
||||
|
||||
@ -733,6 +743,7 @@ Response
|
||||
- conductor: conductor
|
||||
- allocation_uuid: allocation_uuid
|
||||
- network_data: network_data
|
||||
- disable_power_off: disable_power_off
|
||||
|
||||
**Example JSON representation of a Node:**
|
||||
|
||||
|
@ -894,6 +894,15 @@ description:
|
||||
in: body
|
||||
required: true
|
||||
type: string
|
||||
disable_power_off:
|
||||
description: |
|
||||
If set to true, power off for the node is explicitly disabled, instead, a
|
||||
reboot will be used in place of power on/off. Additionally, when possible,
|
||||
the node will be disabled (i.e., its API agent will be rendered unusable
|
||||
and network configuration will be removed) instead of being powered off.
|
||||
in: body
|
||||
required: false
|
||||
type: boolean
|
||||
disable_ramdisk:
|
||||
description: |
|
||||
If set to ``true``, the ironic-python-agent ramdisk will not be booted for
|
||||
@ -1683,6 +1692,15 @@ req_description:
|
||||
in: body
|
||||
required: false
|
||||
type: string
|
||||
req_disable_power_off:
|
||||
description: |
|
||||
If set to ``true``, power off for the node is explicitly disabled, instead, a
|
||||
reboot will be used in place of power on/off. Additionally, when possible,
|
||||
the node will be disabled (i.e., its API agent will be rendered unusable
|
||||
and network configuration will be removed) instead of being powered off.
|
||||
in: body
|
||||
required: false
|
||||
type: boolean
|
||||
req_disable_ramdisk:
|
||||
description: |
|
||||
Whether to boot ramdisk while using a runbook for cleaning or servicing
|
||||
|
@ -2,6 +2,11 @@
|
||||
REST API Version History
|
||||
========================
|
||||
|
||||
1.95 (Epoxy)
|
||||
-----------------------
|
||||
|
||||
Add support to set/unset disable_power_off on nodes.
|
||||
|
||||
1.94 (Epoxy)
|
||||
-----------------------
|
||||
|
||||
|
@ -177,6 +177,7 @@ def node_schema():
|
||||
'deploy_interface': {'type': ['string', 'null']},
|
||||
'description': {'type': ['string', 'null'],
|
||||
'maxLength': _NODE_DESCRIPTION_MAX_LENGTH},
|
||||
'disable_power_off': {'type': ['string', 'boolean', 'null']},
|
||||
'driver': {'type': 'string'},
|
||||
'driver_info': {'type': ['object', 'null']},
|
||||
'extra': {'type': ['object', 'null']},
|
||||
@ -229,6 +230,7 @@ NODE_VALIDATE_EXTRA = args.dict_valid(
|
||||
automated_clean=args.boolean,
|
||||
chassis_uuid=args.uuid,
|
||||
console_enabled=args.boolean,
|
||||
disable_power_off=args.boolean,
|
||||
instance_uuid=args.uuid,
|
||||
protected=args.boolean,
|
||||
maintenance=args.boolean,
|
||||
@ -270,6 +272,7 @@ PATCH_ALLOWED_FIELDS = [
|
||||
'console_interface',
|
||||
'deploy_interface',
|
||||
'description',
|
||||
'disable_power_off',
|
||||
'driver',
|
||||
'driver_info',
|
||||
'extra',
|
||||
@ -1565,6 +1568,7 @@ def _get_fields_for_node_query(fields=None):
|
||||
'conductor_group',
|
||||
'console_enabled',
|
||||
'console_interface',
|
||||
'disable_power_off',
|
||||
'deploy_interface',
|
||||
'deploy_step',
|
||||
'description',
|
||||
@ -3050,7 +3054,8 @@ class NodesController(rest.RestController):
|
||||
('/name', 'baremetal:node:update:name'),
|
||||
('/retired', 'baremetal:node:update:retired'),
|
||||
('/shard', 'baremetal:node:update:shard'),
|
||||
('/parent_node', 'baremetal:node:update:parent_node')
|
||||
('/parent_node', 'baremetal:node:update:parent_node'),
|
||||
('/disable_power_off', 'baremetal:node:update:disable_power_off')
|
||||
)
|
||||
for p in patch:
|
||||
# Process general direct path to policy map
|
||||
|
@ -877,7 +877,8 @@ VERSIONED_FIELDS = {
|
||||
'shard': versions.MINOR_82_NODE_SHARD,
|
||||
'parent_node': versions.MINOR_83_PARENT_CHILD_NODES,
|
||||
'firmware_interface': versions.MINOR_86_FIRMWARE_INTERFACE,
|
||||
'service_step': versions.MINOR_87_SERVICE
|
||||
'service_step': versions.MINOR_87_SERVICE,
|
||||
'disable_power_off': versions.MINOR_95_DISABLE_POWER_OFF,
|
||||
}
|
||||
|
||||
for field in V31_FIELDS:
|
||||
|
@ -132,6 +132,7 @@ BASE_VERSION = 1
|
||||
# v1.92: Add runbooks API
|
||||
# v1.93: Add GET API for virtual media
|
||||
# v1.94: Add node name support for port creation
|
||||
# v1.95: Add node support for disable_power_off
|
||||
|
||||
MINOR_0_JUNO = 0
|
||||
MINOR_1_INITIAL_VERSION = 1
|
||||
@ -228,6 +229,7 @@ MINOR_91_DOT_JSON = 91
|
||||
MINOR_92_RUNBOOKS = 92
|
||||
MINOR_93_GET_VMEDIA = 93
|
||||
MINOR_94_PORT_NODENAME = 94
|
||||
MINOR_95_DISABLE_POWER_OFF = 95
|
||||
|
||||
# When adding another version, update:
|
||||
# - MINOR_MAX_VERSION
|
||||
@ -235,7 +237,7 @@ MINOR_94_PORT_NODENAME = 94
|
||||
# explanation of what changed in the new version
|
||||
# - common/release_mappings.py, RELEASE_MAPPING['master']['api']
|
||||
|
||||
MINOR_MAX_VERSION = MINOR_94_PORT_NODENAME
|
||||
MINOR_MAX_VERSION = MINOR_95_DISABLE_POWER_OFF
|
||||
|
||||
# String representations of the minor and maximum versions
|
||||
_MIN_VERSION_STRING = '{}.{}'.format(BASE_VERSION, MINOR_1_INITIAL_VERSION)
|
||||
|
@ -1052,6 +1052,16 @@ node_policies = [
|
||||
'the API clients.',
|
||||
operations=[{'path': '/nodes/{node_ident}', 'method': 'PATCH'}],
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name='baremetal:node:update:disable_power_off',
|
||||
check_str=SYSTEM_ADMIN,
|
||||
scope_types=['system', 'project'],
|
||||
description='Governs if power off can be disabled via the API '
|
||||
'clients.',
|
||||
operations=[
|
||||
{'path': '/nodes/{node_ident}', 'method': 'PATCH'}
|
||||
],
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name='baremetal:node:firmware:get',
|
||||
check_str=SYSTEM_OR_PROJECT_READER,
|
||||
|
@ -776,7 +776,7 @@ RELEASE_MAPPING = {
|
||||
# make it below. To release, we will preserve a version matching
|
||||
# the release as a separate block of text, like above.
|
||||
'master': {
|
||||
'api': '1.94',
|
||||
'api': '1.95',
|
||||
'rpc': '1.61',
|
||||
'objects': {
|
||||
'Allocation': ['1.1'],
|
||||
|
@ -145,6 +145,7 @@ class TestListNodes(test_api_base.BaseApiTest):
|
||||
self.assertNotIn('lessee', data['nodes'][0])
|
||||
self.assertNotIn('network_data', data['nodes'][0])
|
||||
self.assertNotIn('service_steps', data['nodes'][0])
|
||||
self.assertNotIn('disable_power_off', data['nodes'][0])
|
||||
|
||||
@mock.patch.object(policy, 'check', autospec=True)
|
||||
@mock.patch.object(policy, 'check_policy', autospec=True)
|
||||
@ -220,6 +221,7 @@ class TestListNodes(test_api_base.BaseApiTest):
|
||||
self.assertNotIn('allocation_id', data)
|
||||
self.assertIn('allocation_uuid', data)
|
||||
self.assertIn('service_step', data)
|
||||
self.assertIn('disable_power_off', data)
|
||||
|
||||
def test_get_one_configdrive_dict(self):
|
||||
fake_instance_info = {
|
||||
@ -518,6 +520,30 @@ class TestListNodes(test_api_base.BaseApiTest):
|
||||
self.assertEqual(data['boot_mode'], 'uefi')
|
||||
self.assertEqual(data['secure_boot'], value)
|
||||
|
||||
def test_node_disable_power_off_hidden_in_lower_version(self):
|
||||
self._test_node_field_hidden_in_lower_version('disable_power_off',
|
||||
'1.94', '1.95')
|
||||
|
||||
def test_node_disable_power_off_null_field(self):
|
||||
node = obj_utils.create_test_node(self.context, disable_power_off=None)
|
||||
data = self.get_json('/nodes/%s' % node.uuid,
|
||||
headers={api_base.Version.string: '1.95'})
|
||||
# Default for disable_power_off is False (so not Null)
|
||||
self.assertIs(data['disable_power_off'], False)
|
||||
|
||||
def test_node_disable_power_off_true_field(self):
|
||||
node = obj_utils.create_test_node(self.context, disable_power_off=True)
|
||||
data = self.get_json('/nodes/%s' % node.uuid,
|
||||
headers={api_base.Version.string: '1.95'})
|
||||
self.assertEqual(data['disable_power_off'], True)
|
||||
|
||||
def test_node_disable_power_off_false_field(self):
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
disable_power_off=False)
|
||||
data = self.get_json('/nodes/%s' % node.uuid,
|
||||
headers={api_base.Version.string: '1.95'})
|
||||
self.assertEqual(data['disable_power_off'], False)
|
||||
|
||||
def test_get_one_custom_fields(self):
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
chassis_id=self.chassis.id)
|
||||
@ -831,6 +857,14 @@ class TestListNodes(test_api_base.BaseApiTest):
|
||||
token_value = response['driver_internal_info']['agent_secret_token']
|
||||
self.assertEqual('******', token_value)
|
||||
|
||||
def test_get_disable_power_off_fields(self):
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
disable_power_off=True)
|
||||
fields = 'disable_power_off'
|
||||
response = self.get_json('/nodes/%s?fields=%s' % (node.uuid, fields),
|
||||
headers={api_base.Version.string: '1.95'})
|
||||
self.assertIn('disable_power_off', response)
|
||||
|
||||
def test_detail(self):
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
chassis_id=self.chassis.id)
|
||||
@ -873,6 +907,7 @@ class TestListNodes(test_api_base.BaseApiTest):
|
||||
self.assertIn('retired', data['nodes'][0])
|
||||
self.assertIn('retired_reason', data['nodes'][0])
|
||||
self.assertIn('network_data', data['nodes'][0])
|
||||
self.assertIn('disable_power_off', data['nodes'][0])
|
||||
|
||||
def test_detail_snmpv3(self):
|
||||
driver_info = {
|
||||
@ -931,6 +966,7 @@ class TestListNodes(test_api_base.BaseApiTest):
|
||||
self.assertIn('retired', data['nodes'][0])
|
||||
self.assertIn('retired_reason', data['nodes'][0])
|
||||
self.assertIn('network_data', data['nodes'][0])
|
||||
self.assertIn('disable_power_off', data['nodes'][0])
|
||||
|
||||
def test_detail_instance_uuid(self):
|
||||
instance_uuid = '6eccd391-961c-4da5-b3c5-e2fa5cfbbd9d'
|
||||
@ -951,7 +987,8 @@ class TestListNodes(test_api_base.BaseApiTest):
|
||||
'network_interface', 'resource_class', 'owner', 'lessee',
|
||||
'storage_interface', 'traits', 'automated_clean',
|
||||
'conductor_group', 'protected', 'protected_reason',
|
||||
'retired', 'retired_reason', 'allocation_uuid', 'network_data'
|
||||
'retired', 'retired_reason', 'allocation_uuid', 'network_data',
|
||||
'disable_power_off'
|
||||
]
|
||||
|
||||
for field in expected_fields:
|
||||
@ -1045,6 +1082,7 @@ class TestListNodes(test_api_base.BaseApiTest):
|
||||
self.assertIn('retired', data['nodes'][0])
|
||||
self.assertIn('retired_reason', data['nodes'][0])
|
||||
self.assertIn('network_data', data['nodes'][0])
|
||||
self.assertIn('disable_power_off', data['nodes'][0])
|
||||
|
||||
def test_detail_query_false(self):
|
||||
obj_utils.create_test_node(self.context)
|
||||
@ -5207,6 +5245,26 @@ class TestPost(test_api_base.BaseApiTest):
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(http_client.NOT_ACCEPTABLE, response.status_int)
|
||||
|
||||
def test_create_node_disable_power_off(self):
|
||||
ndict = test_api_utils.post_get_test_node(
|
||||
disable_power_off=True)
|
||||
response = self.post_json('/nodes', ndict,
|
||||
headers={api_base.Version.string:
|
||||
str(api_v1.max_version())})
|
||||
self.assertEqual(http_client.CREATED, response.status_int)
|
||||
result = self.get_json('/nodes/%s' % ndict['uuid'],
|
||||
headers={api_base.Version.string:
|
||||
str(api_v1.max_version())})
|
||||
self.assertEqual(True, result['disable_power_off'])
|
||||
|
||||
def test_create_node_disable_power_off_old_api_version(self):
|
||||
headers = {api_base.Version.string: '1.94'}
|
||||
ndict = test_api_utils.post_get_test_node(disable_power_off=True)
|
||||
response = self.post_json('/nodes', ndict, headers=headers,
|
||||
expect_errors=True)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(http_client.NOT_ACCEPTABLE, response.status_int)
|
||||
|
||||
|
||||
class TestDelete(test_api_base.BaseApiTest):
|
||||
|
||||
|
@ -0,0 +1,9 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Adds support for setting ``disable_power_off`` on node creation along with
|
||||
set/unset ``disable_power_off`` on existing nodes.
|
||||
If set to ``true``, power off for the node is explicitly disabled, instead, a
|
||||
reboot will be used in place of power on/off. Additionally, when possible,
|
||||
the node will be disabled (i.e., its API agent will be rendered unusable
|
||||
and network configurationwill be removed) instead of being powered off.
|
Loading…
Reference in New Issue
Block a user