Files
offline_kubespray/collection/dellemc/openmanage/plugins/modules/redfish_powerstate.py
ByeonJungHun 360c6eef4a offline 작업
2024-02-19 16:02:29 +09:00

262 lines
11 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Dell EMC OpenManage Ansible Modules
# Version 3.0.0
# Copyright (C) 2020-2021 Dell Inc. or its subsidiaries. All Rights Reserved.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r'''
---
module: redfish_powerstate
short_description: Manage device power state
version_added: "2.1.0"
description:
- This module allows to manage the different power states of the specified device.
extends_documentation_fragment:
- dellemc.openmanage.redfish_auth_options
options:
resource_id:
description:
- The unique identifier of the device being managed.
For example- U(https://<I(baseuri)>/redfish/v1/Systems/<I(resource_id)>).
- This option is mandatory for I(base_uri) with multiple devices.
- To get the device details, use the API U(https://<I(baseuri)>/redfish/v1/Systems).
required: False
type: str
reset_type:
description:
- This option resets the device.
- If C(ForceOff), Turns off the device immediately.
- If C(ForceOn), Turns on the device immediately.
- If C(ForceRestart), Turns off the device immediately, and then restarts the device.
- If C(GracefulRestart), Performs graceful shutdown of the device, and then restarts the device.
- If C(GracefulShutdown), Performs a graceful shutdown of the device, and the turns off the device.
- If C(Nmi), Sends a diagnostic interrupt to the device. This is usually a non-maskable interrupt
(NMI) on x86 device.
- If C(On), Turns on the device.
- If C(PowerCycle), Performs power cycle on the device.
- If C(PushPowerButton), Simulates the pressing of a physical power button on the device.
- When a power control operation is performed, which is not supported on the device, an error message is displayed
with the list of operations that can be performed.
required: True
type: str
choices: ["ForceOff", "ForceOn", "ForceRestart", "GracefulRestart", "GracefulShutdown",
"Nmi", "On", "PowerCycle", "PushPowerButton"]
requirements:
- "python >= 2.7.5"
author:
- "Sajna Shetty(@Sajna-Shetty)"
notes:
- Run this module from a system that has direct access to Redfish APIs.
- This module supports C(check_mode).
'''
EXAMPLES = r'''
---
- name: Manage power state of the first device
dellemc.openmanage.redfish_powerstate:
baseuri: "192.168.0.1"
username: "username"
password: "password"
reset_type: "On"
- name: Manage power state of a specified device
dellemc.openmanage.redfish_powerstate:
baseuri: "192.168.0.1"
username: "username"
password: "password"
reset_type: "ForceOff"
resource_id: "System.Embedded.1"
'''
RETURN = r'''
---
msg:
description: Overall status of the reset operation.
returned: always
type: str
sample: "Successfully performed the reset type operation 'On'."
error_info:
type: dict
description: Details of the HTTP error.
returned: on http error
sample: {
"error": {
"@Message.ExtendedInfo": [
{
"Message": "Unable to complete the operation because the resource
/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset entered in not found.",
"MessageArgs": [
"/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset"
],
"MessageArgs@odata.count": 1,
"MessageId": "IDRAC.2.1.SYS403",
"RelatedProperties": [],
"RelatedProperties@odata.count": 0,
"Resolution": "Enter the correct resource and retry the operation.
For information about valid resource,
see the Redfish Users Guide available on the support site.",
"Severity": "Critical"
},
],
"code": "Base.1.5.GeneralError",
"message": "A general error has occurred. See ExtendedInfo for more information"
}
}
'''
import json
import re
from ssl import SSLError
from ansible_collections.dellemc.openmanage.plugins.module_utils.redfish import Redfish
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.six.moves.urllib.error import URLError, HTTPError
from ansible.module_utils.urls import ConnectionError, SSLValidationError
powerstate_map = {}
def fetch_power_uri_resource(module, session_obj):
try:
resource_id = module.params.get("resource_id")
static_resource_id_resource = None
if resource_id:
static_resource_id_resource = "{0}{1}{2}".format(session_obj.root_uri, "Systems/", resource_id)
error_message1 = "The target device does not support the system reset feature" \
" using Redfish API."
system_uri = "{0}{1}".format(session_obj.root_uri, "Systems")
system_resp = session_obj.invoke_request("GET", system_uri)
system_members = system_resp.json_data.get("Members")
if len(system_members) > 1 and static_resource_id_resource is None:
module.fail_json(msg="Multiple devices exists in the system, but option 'resource_id' is not specified.")
if system_members:
resource_id_list = [system_id["@odata.id"] for system_id in system_members if "@odata.id" in system_id]
system_id_res = static_resource_id_resource or resource_id_list[0]
if system_id_res in resource_id_list:
system_id_res_resp = session_obj.invoke_request("GET", system_id_res)
system_id_res_data = system_id_res_resp.json_data
action_id_res = system_id_res_data.get("Actions")
if action_id_res:
current_state = system_id_res_data["PowerState"]
power_uri = action_id_res['#ComputerSystem.Reset']['target']
allowable_enums = action_id_res['#ComputerSystem.Reset']['ResetType@Redfish.AllowableValues']
powerstate_map.update(
{'power_uri': power_uri, 'allowable_enums': allowable_enums, 'current_state': current_state})
else:
module.fail_json(msg=error_message1)
else:
error_message2 = "Invalid device Id '{0}' is provided".format(resource_id)
module.fail_json(msg=error_message2)
else:
module.fail_json(msg=error_message1)
except HTTPError as err:
if err.code in [404, 405]:
module.fail_json(msg=error_message1,
error_info=json.load(err))
raise err
def is_change_applicable_for_power_state(current_power_state, apply_power_state):
""" checks if changes are applicable or not for current system state
:param current_power_state: Current power state
:type current_power_state: str
:param apply_power_state: Required power state
:type apply_power_state: str
:return: boolean True if changes is applicable
"""
on_states = ["On", "PoweringOn"]
off_states = ["Off", "PoweringOff"]
reset_map_apply = {
("On", "ForceOn",): off_states,
("PushPowerButton",): on_states + off_states,
("ForceOff", "ForceRestart", "GracefulRestart", "GracefulShutdown", "Nmi", "PowerCycle",): on_states
}
is_reset_applicable = False
for apply_states, applicable_states in reset_map_apply.items():
if apply_power_state in apply_states:
if current_power_state in applicable_states:
is_reset_applicable = True
break
break
return is_reset_applicable
def is_valid_reset_type(reset_type, allowable_enum, module):
if reset_type not in allowable_enum:
res_list = re.findall('[A-Z][^A-Z]*', reset_type)
lw_reset_type = " ".join([word.lower() for word in res_list])
error_msg = "The target device does not support a" \
" {0} operation.The acceptable values for device reset types" \
" are {1}.".format(lw_reset_type, ", ".join(allowable_enum))
module.fail_json(msg=error_msg)
def run_change_power_state(redfish_session_obj, module):
"""
Apply reset type to system
Keyword arguments:
redfish_session_obj -- session handle
module -- Ansible module obj
"""
apply_reset_type = module.params["reset_type"]
fetch_power_uri_resource(module, redfish_session_obj)
is_valid_reset_type(apply_reset_type, powerstate_map["allowable_enums"], module)
current_power_state = powerstate_map["current_state"]
reset_flag = is_change_applicable_for_power_state(current_power_state, apply_reset_type)
if module.check_mode is True:
if reset_flag is True:
module.exit_json(msg="Changes found to be applied.", changed=True)
else:
module.exit_json(msg="No Changes found to be applied.", changed=False)
if reset_flag is True:
payload = {"ResetType": apply_reset_type}
power_uri = powerstate_map["power_uri"]
reset_resp = redfish_session_obj.invoke_request("POST", power_uri, data=payload)
if reset_resp.success:
module.exit_json(msg="Successfully performed the reset type operation"
" '{0}'.".format(apply_reset_type), changed=True)
else:
module.exit_json(msg="Unable to perform the reset type operation '{0}'.".format(apply_reset_type),
changed=False)
else:
module.exit_json(msg="The device is already powered {0}.".format(current_power_state.lower()), changed=False)
def main():
module = AnsibleModule(
argument_spec={
"baseuri": {"required": True, "type": "str"},
"username": {"required": True, "type": "str"},
"password": {"required": True, "type": "str", "no_log": True},
"resource_id": {"required": False, "type": "str"},
"reset_type": {"required": True, "type": "str",
"choices": ['ForceOff', 'ForceOn', 'ForceRestart', 'GracefulRestart',
'GracefulShutdown', 'Nmi', 'On', 'PowerCycle', 'PushPowerButton']},
},
supports_check_mode=True)
try:
with Redfish(module.params) as redfish_obj:
run_change_power_state(redfish_obj, module)
except HTTPError as err:
module.fail_json(msg=str(err), error_info=json.load(err))
except URLError as err:
module.exit_json(msg=str(err), unreachable=True)
except (IOError, ValueError, SSLError, TypeError, ConnectionError) as err:
module.fail_json(msg=str(err))
except Exception as err:
module.fail_json(msg=str(err))
if __name__ == '__main__':
main()