Support set boot source of composed node
This patch allow user to set boot source of composed node. Two parameters are needed in request body, "Target" - the boot source to be used at next boot "Enabled" - options "Once" and "Continuous" are supported, which indicate whether this action is only one time change Partially-Implements blueprint node-action Change-Id: I46342c884e3cb8f00069e7e2c10c878582a2b185
This commit is contained in:
parent
6bc8f689b0
commit
be919e10a5
6
api-ref/source/mockup/node-set-boot-soruce-request.json
Normal file
6
api-ref/source/mockup/node-set-boot-soruce-request.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"Boot": {
|
||||
"Enabled": "Once",
|
||||
"Target": "Pxe"
|
||||
}
|
||||
}
|
@ -111,10 +111,17 @@ node_attach_type:
|
||||
in: body
|
||||
required: true
|
||||
type: string
|
||||
node_boot_enabled:
|
||||
description: |
|
||||
The value of this describe the state of boot source override feature,
|
||||
including "Once", "Continuous".
|
||||
in: body
|
||||
required: true
|
||||
type: string
|
||||
node_boot_source:
|
||||
description: |
|
||||
The value of this contain the source to boot the composed node, including
|
||||
"pxe", "hdd", "none"
|
||||
"Pxe", "Hdd", "None".
|
||||
in: body
|
||||
required: true
|
||||
type: string
|
||||
@ -168,7 +175,7 @@ node_power_state:
|
||||
"On", "Off", "PoweringOn" ,"PoweringOff"
|
||||
in: body
|
||||
required: true
|
||||
type: string
|
||||
type: string
|
||||
node_processor_asset:
|
||||
description: |
|
||||
Processor asset (Object) info.
|
||||
@ -208,11 +215,11 @@ node_reset_type:
|
||||
type: string
|
||||
node_state:
|
||||
description: |
|
||||
The current composed node state including
|
||||
The current composed node state including
|
||||
"Assembling", "Allocating", "Assembling" ,"Failed", "Assembled"
|
||||
in: body
|
||||
required: true
|
||||
type: string
|
||||
type: string
|
||||
node_storage_asset:
|
||||
description: |
|
||||
Storage asset (Object) info.
|
||||
|
@ -212,12 +212,12 @@ Request
|
||||
- node_ident: node_ident
|
||||
|
||||
|
||||
Node reset
|
||||
Node action
|
||||
===========
|
||||
|
||||
.. rest_method:: POST /v1/nodes/{node_ident}/action
|
||||
|
||||
Send a POST (reset) cmd to a composed node.
|
||||
Send a POST cmd to a composed node, includes reset node state and set boot source.
|
||||
|
||||
|
||||
Normal response codes: 204
|
||||
@ -231,10 +231,17 @@ Request
|
||||
|
||||
- node_ident: node_ident
|
||||
- reset_type: node_reset_type
|
||||
- boot_enabled: node_boot_enabled
|
||||
- boot_source: node_boot_source
|
||||
|
||||
**Example POST action cmd for composed node :**
|
||||
**Example reset state for composed node :**
|
||||
|
||||
.. literalinclude:: mockup/node-post-action-request.json
|
||||
.. literalinclude:: mockup/node-reset-state-request.json
|
||||
:language: javascript
|
||||
|
||||
**Example set boot source for composed node :**
|
||||
|
||||
.. literalinclude:: mockup/node-set-boot-source-request.json
|
||||
:language: javascript
|
||||
|
||||
Response
|
||||
|
@ -575,6 +575,59 @@ def reset_node(nodeid, request):
|
||||
"successfully.".format(action_type))
|
||||
|
||||
|
||||
def set_boot_source(nodeid, request):
|
||||
nodes_url = get_base_resource_url("Nodes")
|
||||
node_url = os.path.normpath("/".join([nodes_url, nodeid]))
|
||||
resp = send_request(node_url)
|
||||
|
||||
if resp.status_code != http_client.OK:
|
||||
# Raise exception if don't find node
|
||||
raise exception.RedfishException(resp.json(),
|
||||
status_code=resp.status_code)
|
||||
|
||||
node = resp.json()
|
||||
|
||||
boot_enabled = request.get("Boot", {}).get("Enabled")
|
||||
boot_target = request.get("Boot", {}).get("Target")
|
||||
allowable_boot_target = \
|
||||
node["Boot"]["BootSourceOverrideTarget@Redfish.AllowableValues"]
|
||||
|
||||
if not boot_enabled or not boot_target:
|
||||
raise exception.BadRequest(
|
||||
detail="The content of set boot source request is malformed. "
|
||||
"Please refer to Valence api specification to correct it.")
|
||||
if boot_enabled not in ["Disabled", "Once", "Continuous"]:
|
||||
raise exception.BadRequest(
|
||||
detail="The parameter Enabled '{0}' is not in allowable list "
|
||||
"['Disabled', 'Once', 'Continuous'].".format(
|
||||
boot_enabled))
|
||||
if allowable_boot_target and \
|
||||
boot_target not in allowable_boot_target:
|
||||
raise exception.BadRequest(
|
||||
detail="The parameter Target '{0}' is not in allowable list "
|
||||
"{1}.".format(boot_target,
|
||||
allowable_boot_target))
|
||||
|
||||
action_resp = send_request(
|
||||
node_url, 'PATCH', headers={'Content-type': 'application/json'},
|
||||
json={"Boot": {"BootSourceOverrideEnabled": boot_enabled,
|
||||
"BootSourceOverrideTarget": boot_target}})
|
||||
|
||||
if action_resp.status_code != http_client.NO_CONTENT:
|
||||
raise exception.RedfishException(action_resp.json(),
|
||||
status_code=action_resp.status_code)
|
||||
else:
|
||||
# Set boot source successfully
|
||||
LOG.debug("Set boot source of composed node {0} to '{1}' with enabled "
|
||||
"state '{2}' successfully."
|
||||
.format(nodes_url, boot_target, boot_enabled))
|
||||
return exception.confirmation(
|
||||
confirm_code="Set Boot Source of Composed Node",
|
||||
confirm_detail="The boot source of composed node has been set to "
|
||||
"'{0}' with enabled state '{1}' successfully."
|
||||
.format(boot_target, boot_enabled))
|
||||
|
||||
|
||||
def node_action(nodeid, request):
|
||||
# Only support one action in single request
|
||||
if len(list(request.keys())) != 1:
|
||||
@ -589,7 +642,8 @@ def node_action(nodeid, request):
|
||||
# Because valence assemble node by default when compose node, so only need
|
||||
# to support "Reset" action here. In case podm new version support more
|
||||
# actions, use "functions" dict to drive the workflow.
|
||||
functions = {"Reset": reset_node}
|
||||
functions = {"Reset": reset_node,
|
||||
"Boot": set_boot_source}
|
||||
|
||||
if action not in functions:
|
||||
raise exception.BadRequest(
|
||||
|
@ -471,6 +471,80 @@ class TestRedfish(TestCase):
|
||||
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
@mock.patch('valence.redfish.redfish.send_request')
|
||||
@mock.patch('valence.redfish.redfish.get_base_resource_url')
|
||||
def test_set_boot_source_wrong_request(self, mock_get_url, mock_request):
|
||||
"""Test reset node with wrong action type"""
|
||||
mock_get_url.return_value = '/redfish/v1/Nodes'
|
||||
mock_request.return_value = fakes.mock_request_get(
|
||||
fakes.fake_node_detail(), http_client.OK)
|
||||
|
||||
# Test no "Target" parameter
|
||||
with self.assertRaises(exception.BadRequest) as context:
|
||||
redfish.set_boot_source("1", {"Boot": {"Enabled": "Once"}})
|
||||
|
||||
self.assertTrue("The content of set boot source request is malformed. "
|
||||
"Please refer to Valence api specification to correct "
|
||||
"it." in str(context.exception.detail))
|
||||
|
||||
# Test no "Enabled" parameter
|
||||
with self.assertRaises(exception.BadRequest) as context:
|
||||
redfish.set_boot_source("1", {"Boot": {"Target": "Hdd"}})
|
||||
|
||||
self.assertTrue("The content of set boot source request is malformed. "
|
||||
"Please refer to Valence api specification to correct "
|
||||
"it." in str(context.exception.detail))
|
||||
|
||||
# Test no "Enabled" either "Target" parameter
|
||||
with self.assertRaises(exception.BadRequest) as context:
|
||||
redfish.set_boot_source("1", {"Boot": {}})
|
||||
|
||||
self.assertTrue("The content of set boot source request is malformed. "
|
||||
"Please refer to Valence api specification to correct "
|
||||
"it." in str(context.exception.detail))
|
||||
|
||||
# Test wrong "Enabled" parameter
|
||||
with self.assertRaises(exception.BadRequest) as context:
|
||||
redfish.set_boot_source("1", {"Boot": {"Enabled": "wrong_input",
|
||||
"Target": "Hdd"}})
|
||||
|
||||
self.assertTrue("The parameter Enabled 'wrong_input' is not in "
|
||||
"allowable list ['Disabled', 'Once', 'Continuous']."
|
||||
in str(context.exception.detail))
|
||||
|
||||
# Test wrong "Enabled" parameter
|
||||
with self.assertRaises(exception.BadRequest) as context:
|
||||
redfish.set_boot_source("1", {"Boot": {"Enabled": "Once",
|
||||
"Target": "wrong_input"}})
|
||||
|
||||
allowable_boot_target = \
|
||||
(fakes.fake_node_detail()["Boot"]
|
||||
["BootSourceOverrideTarget@Redfish.AllowableValues"])
|
||||
self.assertTrue("The parameter Target 'wrong_input' is not in "
|
||||
"allowable list {0}.".format(allowable_boot_target)
|
||||
in str(context.exception.detail))
|
||||
|
||||
@mock.patch('valence.redfish.redfish.send_request')
|
||||
@mock.patch('valence.redfish.redfish.get_base_resource_url')
|
||||
def test_set_boot_source_success(self, mock_get_url, mock_request):
|
||||
"""Test successfully reset node status"""
|
||||
mock_get_url.return_value = '/redfish/v1/Nodes'
|
||||
fake_node_detail = fakes.mock_request_get(
|
||||
fakes.fake_node_detail(), http_client.OK)
|
||||
fake_node_action_resp = fakes.mock_request_get(
|
||||
{}, http_client.NO_CONTENT)
|
||||
mock_request.side_effect = [fake_node_detail, fake_node_action_resp]
|
||||
|
||||
result = redfish.set_boot_source(
|
||||
"1", {"Boot": {"Enabled": "Once", "Target": "Hdd"}})
|
||||
expected = exception.confirmation(
|
||||
confirm_code="Set Boot Source of Composed Node",
|
||||
confirm_detail="The boot source of composed node has been set to "
|
||||
"'{0}' with enabled state '{1}' successfully."
|
||||
.format("Hdd", "Once"))
|
||||
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
@mock.patch('valence.redfish.redfish.reset_node')
|
||||
def test_node_action_malformed_request(self, mock_reset_node):
|
||||
"""Test post node_action with malformed request"""
|
||||
|
Loading…
Reference in New Issue
Block a user