* WDC Redfish support for setting the power mode.
* Apply suggestions from code review
Co-authored-by: Felix Fontein <felix@fontein.de>
* Add change fragment.
* Add extension to changelog fragment.
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 2a449eb163
)
Co-authored-by: Mike Moerk <michael.moerk@wdc.com>
pull/5230/head
parent
73ee9702db
commit
b32b69742b
|
@ -0,0 +1,2 @@
|
||||||
|
minor_changes:
|
||||||
|
- wdc_redfish_command - add ``PowerModeLow`` and ``PowerModeNormal`` commands for ``Chassis`` category (https://github.com/ansible-collections/community.general/pull/5145).
|
|
@ -32,6 +32,17 @@ class WdcRedfishUtils(RedfishUtils):
|
||||||
UPDATE_STATUS_MESSAGE_FW_UPDATE_COMPLETED_WAITING_FOR_ACTIVATION = "FW update completed. Waiting for activation."
|
UPDATE_STATUS_MESSAGE_FW_UPDATE_COMPLETED_WAITING_FOR_ACTIVATION = "FW update completed. Waiting for activation."
|
||||||
UPDATE_STATUS_MESSAGE_FW_UPDATE_FAILED = "FW update failed."
|
UPDATE_STATUS_MESSAGE_FW_UPDATE_FAILED = "FW update failed."
|
||||||
|
|
||||||
|
# Dict keys for resource bodies
|
||||||
|
# Standard keys
|
||||||
|
ACTIONS = "Actions"
|
||||||
|
OEM = "Oem"
|
||||||
|
WDC = "WDC"
|
||||||
|
TARGET = "target"
|
||||||
|
|
||||||
|
# Keys for specific operations
|
||||||
|
CHASSIS_LOCATE = "#Chassis.Locate"
|
||||||
|
CHASSIS_POWER_MODE = "#Chassis.PowerMode"
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
creds,
|
creds,
|
||||||
root_uris,
|
root_uris,
|
||||||
|
@ -409,17 +420,32 @@ class WdcRedfishUtils(RedfishUtils):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_led_locate_uri(data):
|
def _get_led_locate_uri(data):
|
||||||
"""Get the LED locate URI given a resource body."""
|
"""Get the LED locate URI given a resource body."""
|
||||||
if "Actions" not in data:
|
if WdcRedfishUtils.ACTIONS not in data:
|
||||||
return None
|
return None
|
||||||
if "Oem" not in data["Actions"]:
|
if WdcRedfishUtils.OEM not in data[WdcRedfishUtils.ACTIONS]:
|
||||||
return None
|
return None
|
||||||
if "WDC" not in data["Actions"]["Oem"]:
|
if WdcRedfishUtils.WDC not in data[WdcRedfishUtils.ACTIONS][WdcRedfishUtils.OEM]:
|
||||||
return None
|
return None
|
||||||
if "#Chassis.Locate" not in data["Actions"]["Oem"]["WDC"]:
|
if WdcRedfishUtils.CHASSIS_LOCATE not in data[WdcRedfishUtils.ACTIONS][WdcRedfishUtils.OEM][WdcRedfishUtils.WDC]:
|
||||||
return None
|
return None
|
||||||
if "target" not in data["Actions"]["Oem"]["WDC"]["#Chassis.Locate"]:
|
if WdcRedfishUtils.TARGET not in data[WdcRedfishUtils.ACTIONS][WdcRedfishUtils.OEM][WdcRedfishUtils.WDC][WdcRedfishUtils.CHASSIS_LOCATE]:
|
||||||
return None
|
return None
|
||||||
return data["Actions"]["Oem"]["WDC"]["#Chassis.Locate"]["target"]
|
return data[WdcRedfishUtils.ACTIONS][WdcRedfishUtils.OEM][WdcRedfishUtils.WDC][WdcRedfishUtils.CHASSIS_LOCATE][WdcRedfishUtils.TARGET]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_power_mode_uri(data):
|
||||||
|
"""Get the Power Mode URI given a resource body."""
|
||||||
|
if WdcRedfishUtils.ACTIONS not in data:
|
||||||
|
return None
|
||||||
|
if WdcRedfishUtils.OEM not in data[WdcRedfishUtils.ACTIONS]:
|
||||||
|
return None
|
||||||
|
if WdcRedfishUtils.WDC not in data[WdcRedfishUtils.ACTIONS][WdcRedfishUtils.OEM]:
|
||||||
|
return None
|
||||||
|
if WdcRedfishUtils.CHASSIS_POWER_MODE not in data[WdcRedfishUtils.ACTIONS][WdcRedfishUtils.OEM][WdcRedfishUtils.WDC]:
|
||||||
|
return None
|
||||||
|
if WdcRedfishUtils.TARGET not in data[WdcRedfishUtils.ACTIONS][WdcRedfishUtils.OEM][WdcRedfishUtils.WDC][WdcRedfishUtils.CHASSIS_POWER_MODE]:
|
||||||
|
return None
|
||||||
|
return data[WdcRedfishUtils.ACTIONS][WdcRedfishUtils.OEM][WdcRedfishUtils.WDC][WdcRedfishUtils.CHASSIS_POWER_MODE][WdcRedfishUtils.TARGET]
|
||||||
|
|
||||||
def manage_indicator_led(self, command, resource_uri):
|
def manage_indicator_led(self, command, resource_uri):
|
||||||
key = 'IndicatorLED'
|
key = 'IndicatorLED'
|
||||||
|
@ -452,3 +478,43 @@ class WdcRedfishUtils(RedfishUtils):
|
||||||
return {'ret': False, 'msg': 'Invalid command'}
|
return {'ret': False, 'msg': 'Invalid command'}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def manage_chassis_power_mode(self, command):
|
||||||
|
return self.manage_power_mode(command, self.chassis_uri)
|
||||||
|
|
||||||
|
def manage_power_mode(self, command, resource_uri=None):
|
||||||
|
if resource_uri is None:
|
||||||
|
resource_uri = self.chassis_uri
|
||||||
|
|
||||||
|
payloads = {'PowerModeNormal': 'Normal', 'PowerModeLow': 'Low'}
|
||||||
|
requested_power_mode = payloads[command]
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
response = self.get_request(self.root_uri + resource_uri)
|
||||||
|
if response['ret'] is False:
|
||||||
|
return response
|
||||||
|
result['ret'] = True
|
||||||
|
data = response['data']
|
||||||
|
|
||||||
|
# Make sure the response includes Oem.WDC.PowerMode, and get current power mode
|
||||||
|
power_mode = 'PowerMode'
|
||||||
|
if WdcRedfishUtils.OEM not in data or WdcRedfishUtils.WDC not in data[WdcRedfishUtils.OEM] or\
|
||||||
|
power_mode not in data[WdcRedfishUtils.OEM][WdcRedfishUtils.WDC]:
|
||||||
|
return {'ret': False, 'msg': 'Resource does not support Oem.WDC.PowerMode'}
|
||||||
|
current_power_mode = data[WdcRedfishUtils.OEM][WdcRedfishUtils.WDC][power_mode]
|
||||||
|
if current_power_mode == requested_power_mode:
|
||||||
|
return {'ret': True, 'changed': False}
|
||||||
|
|
||||||
|
power_mode_uri = self._get_power_mode_uri(data)
|
||||||
|
if power_mode_uri is None:
|
||||||
|
return {'ret': False, 'msg': 'Power Mode URI not found.'}
|
||||||
|
|
||||||
|
if command in payloads.keys():
|
||||||
|
payload = {'PowerMode': payloads[command]}
|
||||||
|
response = self.post_request(self.root_uri + power_mode_uri, payload)
|
||||||
|
if response['ret'] is False:
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
return {'ret': False, 'msg': 'Invalid command'}
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
|
@ -170,6 +170,18 @@ EXAMPLES = '''
|
||||||
username: "{{ username }}"
|
username: "{{ username }}"
|
||||||
password: "{{ password }}"
|
password: "{{ password }}"
|
||||||
|
|
||||||
|
- name: Set chassis to Low Power Mode
|
||||||
|
community.general.wdc_redfish_command:
|
||||||
|
category: Chassis
|
||||||
|
resource_id: Enclosure
|
||||||
|
command: PowerModeLow
|
||||||
|
|
||||||
|
- name: Set chassis to Normal Power Mode
|
||||||
|
community.general.wdc_redfish_command:
|
||||||
|
category: Chassis
|
||||||
|
resource_id: Enclosure
|
||||||
|
command: PowerModeNormal
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
RETURN = '''
|
RETURN = '''
|
||||||
|
@ -191,7 +203,9 @@ CATEGORY_COMMANDS_ALL = {
|
||||||
],
|
],
|
||||||
"Chassis": [
|
"Chassis": [
|
||||||
"IndicatorLedOn",
|
"IndicatorLedOn",
|
||||||
"IndicatorLedOff"
|
"IndicatorLedOff",
|
||||||
|
"PowerModeLow",
|
||||||
|
"PowerModeNormal",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,6 +318,8 @@ def main():
|
||||||
for command in command_list:
|
for command in command_list:
|
||||||
if command.startswith("IndicatorLed"):
|
if command.startswith("IndicatorLed"):
|
||||||
result = rf_utils.manage_chassis_indicator_led(command)
|
result = rf_utils.manage_chassis_indicator_led(command)
|
||||||
|
elif command.startswith("PowerMode"):
|
||||||
|
result = rf_utils.manage_chassis_power_mode(command)
|
||||||
|
|
||||||
if result['ret'] is False:
|
if result['ret'] is False:
|
||||||
module.fail_json(msg=to_native(result['msg']))
|
module.fail_json(msg=to_native(result['msg']))
|
||||||
|
|
|
@ -78,9 +78,17 @@ MOCK_SUCCESSFUL_RESPONSE_CHASSIS_ENCLOSURE = {
|
||||||
"WDC": {
|
"WDC": {
|
||||||
"#Chassis.Locate": {
|
"#Chassis.Locate": {
|
||||||
"target": "/Chassis.Locate"
|
"target": "/Chassis.Locate"
|
||||||
|
},
|
||||||
|
"#Chassis.PowerMode": {
|
||||||
|
"target": "/redfish/v1/Chassis/Enclosure/Actions/Chassis.PowerMode",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Oem": {
|
||||||
|
"WDC": {
|
||||||
|
"PowerMode": "Normal"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -237,8 +245,8 @@ def mock_get_request_enclosure_multi_tenant(*args, **kwargs):
|
||||||
raise RuntimeError("Illegal call to get_request in test: " + args[1])
|
raise RuntimeError("Illegal call to get_request in test: " + args[1])
|
||||||
|
|
||||||
|
|
||||||
def mock_get_request_led_indicator(*args, **kwargs):
|
def mock_get_request(*args, **kwargs):
|
||||||
"""Mock for get_request for LED indicator tests."""
|
"""Mock for get_request for simple resource tests."""
|
||||||
if args[1].endswith("/redfish/v1") or args[1].endswith("/redfish/v1/"):
|
if args[1].endswith("/redfish/v1") or args[1].endswith("/redfish/v1/"):
|
||||||
return MOCK_SUCCESSFUL_RESPONSE_WITH_UPDATE_SERVICE_RESOURCE
|
return MOCK_SUCCESSFUL_RESPONSE_WITH_UPDATE_SERVICE_RESOURCE
|
||||||
elif args[1].endswith("/Chassis"):
|
elif args[1].endswith("/Chassis"):
|
||||||
|
@ -253,7 +261,8 @@ def mock_post_request(*args, **kwargs):
|
||||||
"""Mock post_request with successful response."""
|
"""Mock post_request with successful response."""
|
||||||
valid_endpoints = [
|
valid_endpoints = [
|
||||||
"/UpdateService.FWActivate",
|
"/UpdateService.FWActivate",
|
||||||
"/Chassis.Locate"
|
"/Chassis.Locate",
|
||||||
|
"/Chassis.PowerMode",
|
||||||
]
|
]
|
||||||
for endpoint in valid_endpoints:
|
for endpoint in valid_endpoints:
|
||||||
if args[1].endswith(endpoint):
|
if args[1].endswith(endpoint):
|
||||||
|
@ -325,6 +334,64 @@ class TestWdcRedfishCommand(unittest.TestCase):
|
||||||
})
|
})
|
||||||
module.main()
|
module.main()
|
||||||
|
|
||||||
|
def test_module_chassis_power_mode_low(self):
|
||||||
|
"""Test setting chassis power mode to low (happy path)."""
|
||||||
|
module_args = {
|
||||||
|
'category': 'Chassis',
|
||||||
|
'command': 'PowerModeLow',
|
||||||
|
'username': 'USERID',
|
||||||
|
'password': 'PASSW0RD=21',
|
||||||
|
'resource_id': 'Enclosure',
|
||||||
|
'baseuri': 'example.com'
|
||||||
|
}
|
||||||
|
set_module_args(module_args)
|
||||||
|
with patch.multiple("ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.WdcRedfishUtils",
|
||||||
|
get_request=mock_get_request,
|
||||||
|
post_request=mock_post_request):
|
||||||
|
with self.assertRaises(AnsibleExitJson) as ansible_exit_json:
|
||||||
|
module.main()
|
||||||
|
self.assertEqual(ACTION_WAS_SUCCESSFUL_MESSAGE,
|
||||||
|
get_exception_message(ansible_exit_json))
|
||||||
|
self.assertTrue(is_changed(ansible_exit_json))
|
||||||
|
|
||||||
|
def test_module_chassis_power_mode_normal_when_already_normal(self):
|
||||||
|
"""Test setting chassis power mode to normal when it already is. Verify we get changed=False."""
|
||||||
|
module_args = {
|
||||||
|
'category': 'Chassis',
|
||||||
|
'command': 'PowerModeNormal',
|
||||||
|
'username': 'USERID',
|
||||||
|
'password': 'PASSW0RD=21',
|
||||||
|
'resource_id': 'Enclosure',
|
||||||
|
'baseuri': 'example.com'
|
||||||
|
}
|
||||||
|
set_module_args(module_args)
|
||||||
|
with patch.multiple("ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.WdcRedfishUtils",
|
||||||
|
get_request=mock_get_request):
|
||||||
|
with self.assertRaises(AnsibleExitJson) as ansible_exit_json:
|
||||||
|
module.main()
|
||||||
|
self.assertEqual(ACTION_WAS_SUCCESSFUL_MESSAGE,
|
||||||
|
get_exception_message(ansible_exit_json))
|
||||||
|
self.assertFalse(is_changed(ansible_exit_json))
|
||||||
|
|
||||||
|
def test_module_chassis_power_mode_invalid_command(self):
|
||||||
|
"""Test that we get an error when issuing an invalid PowerMode command."""
|
||||||
|
module_args = {
|
||||||
|
'category': 'Chassis',
|
||||||
|
'command': 'PowerModeExtraHigh',
|
||||||
|
'username': 'USERID',
|
||||||
|
'password': 'PASSW0RD=21',
|
||||||
|
'resource_id': 'Enclosure',
|
||||||
|
'baseuri': 'example.com'
|
||||||
|
}
|
||||||
|
set_module_args(module_args)
|
||||||
|
with patch.multiple("ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.WdcRedfishUtils",
|
||||||
|
get_request=mock_get_request):
|
||||||
|
with self.assertRaises(AnsibleFailJson) as ansible_fail_json:
|
||||||
|
module.main()
|
||||||
|
expected_error_message = "Invalid Command 'PowerModeExtraHigh'"
|
||||||
|
self.assertIn(expected_error_message,
|
||||||
|
get_exception_message(ansible_fail_json))
|
||||||
|
|
||||||
def test_module_enclosure_led_indicator_on(self):
|
def test_module_enclosure_led_indicator_on(self):
|
||||||
"""Test turning on a valid LED indicator (in this case we use the Enclosure resource)."""
|
"""Test turning on a valid LED indicator (in this case we use the Enclosure resource)."""
|
||||||
module_args = {
|
module_args = {
|
||||||
|
@ -338,7 +405,7 @@ class TestWdcRedfishCommand(unittest.TestCase):
|
||||||
set_module_args(module_args)
|
set_module_args(module_args)
|
||||||
|
|
||||||
with patch.multiple("ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.WdcRedfishUtils",
|
with patch.multiple("ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.WdcRedfishUtils",
|
||||||
get_request=mock_get_request_led_indicator,
|
get_request=mock_get_request,
|
||||||
post_request=mock_post_request):
|
post_request=mock_post_request):
|
||||||
with self.assertRaises(AnsibleExitJson) as ansible_exit_json:
|
with self.assertRaises(AnsibleExitJson) as ansible_exit_json:
|
||||||
module.main()
|
module.main()
|
||||||
|
@ -359,7 +426,7 @@ class TestWdcRedfishCommand(unittest.TestCase):
|
||||||
set_module_args(module_args)
|
set_module_args(module_args)
|
||||||
|
|
||||||
with patch.multiple("ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.WdcRedfishUtils",
|
with patch.multiple("ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.WdcRedfishUtils",
|
||||||
get_request=mock_get_request_led_indicator,
|
get_request=mock_get_request,
|
||||||
post_request=mock_post_request):
|
post_request=mock_post_request):
|
||||||
with self.assertRaises(AnsibleFailJson) as ansible_fail_json:
|
with self.assertRaises(AnsibleFailJson) as ansible_fail_json:
|
||||||
module.main()
|
module.main()
|
||||||
|
@ -380,7 +447,7 @@ class TestWdcRedfishCommand(unittest.TestCase):
|
||||||
set_module_args(module_args)
|
set_module_args(module_args)
|
||||||
|
|
||||||
with patch.multiple("ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.WdcRedfishUtils",
|
with patch.multiple("ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.WdcRedfishUtils",
|
||||||
get_request=mock_get_request_led_indicator):
|
get_request=mock_get_request):
|
||||||
with self.assertRaises(AnsibleExitJson) as ansible_exit_json:
|
with self.assertRaises(AnsibleExitJson) as ansible_exit_json:
|
||||||
module.main()
|
module.main()
|
||||||
self.assertEqual(ACTION_WAS_SUCCESSFUL_MESSAGE,
|
self.assertEqual(ACTION_WAS_SUCCESSFUL_MESSAGE,
|
||||||
|
|
Loading…
Reference in New Issue