Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WDC Redfish support for setting the power mode. #5145

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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).
78 changes: 72 additions & 6 deletions plugins/module_utils/wdc_redfish_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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_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,
creds,
root_uris,
Expand Down Expand Up @@ -409,17 +420,32 @@ def _get_installed_firmware_version_of_multi_tenant_system(self,
@staticmethod
def _get_led_locate_uri(data):
"""Get the LED locate URI given a resource body."""
if "Actions" not in data:
if WdcRedfishUtils.ACTIONS not in data:
return None
if "Oem" not in data["Actions"]:
if WdcRedfishUtils.OEM not in data[WdcRedfishUtils.ACTIONS]:
return None
if "WDC" not in data["Actions"]["Oem"]:
if WdcRedfishUtils.WDC not in data[WdcRedfishUtils.ACTIONS][WdcRedfishUtils.OEM]:
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
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 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):
key = 'IndicatorLED'
Expand Down Expand Up @@ -452,3 +478,43 @@ def manage_indicator_led(self, command, resource_uri):
return {'ret': False, 'msg': 'Invalid command'}

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
18 changes: 17 additions & 1 deletion plugins/modules/remote_management/redfish/wdc_redfish_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,18 @@
username: "{{ username }}"
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 = '''
Expand All @@ -191,7 +203,9 @@
],
"Chassis": [
"IndicatorLedOn",
"IndicatorLedOff"
"IndicatorLedOff",
"PowerModeLow",
"PowerModeNormal",
]
}

Expand Down Expand Up @@ -304,6 +318,8 @@ def main():
for command in command_list:
if command.startswith("IndicatorLed"):
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:
module.fail_json(msg=to_native(result['msg']))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,17 @@
"WDC": {
"#Chassis.Locate": {
"target": "/Chassis.Locate"
},
"#Chassis.PowerMode": {
"target": "/redfish/v1/Chassis/Enclosure/Actions/Chassis.PowerMode",
}
}
}
},
"Oem": {
"WDC": {
"PowerMode": "Normal"
}
}
}
}
Expand Down Expand Up @@ -237,8 +245,8 @@ def mock_get_request_enclosure_multi_tenant(*args, **kwargs):
raise RuntimeError("Illegal call to get_request in test: " + args[1])


def mock_get_request_led_indicator(*args, **kwargs):
"""Mock for get_request for LED indicator tests."""
def mock_get_request(*args, **kwargs):
"""Mock for get_request for simple resource tests."""
if args[1].endswith("/redfish/v1") or args[1].endswith("/redfish/v1/"):
return MOCK_SUCCESSFUL_RESPONSE_WITH_UPDATE_SERVICE_RESOURCE
elif args[1].endswith("/Chassis"):
Expand All @@ -253,7 +261,8 @@ def mock_post_request(*args, **kwargs):
"""Mock post_request with successful response."""
valid_endpoints = [
"/UpdateService.FWActivate",
"/Chassis.Locate"
"/Chassis.Locate",
"/Chassis.PowerMode",
]
for endpoint in valid_endpoints:
if args[1].endswith(endpoint):
Expand Down Expand Up @@ -325,6 +334,64 @@ def test_module_fail_when_unknown_command(self):
})
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):
"""Test turning on a valid LED indicator (in this case we use the Enclosure resource)."""
module_args = {
Expand All @@ -338,7 +405,7 @@ def test_module_enclosure_led_indicator_on(self):
set_module_args(module_args)

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):
with self.assertRaises(AnsibleExitJson) as ansible_exit_json:
module.main()
Expand All @@ -359,7 +426,7 @@ def test_module_invalid_resource_led_indicator_on(self):
set_module_args(module_args)

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):
with self.assertRaises(AnsibleFailJson) as ansible_fail_json:
module.main()
Expand All @@ -380,7 +447,7 @@ def test_module_enclosure_led_off_already_off(self):
set_module_args(module_args)

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:
module.main()
self.assertEqual(ACTION_WAS_SUCCESSFUL_MESSAGE,
Expand Down