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

217 lines
8.7 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Dell EMC OpenManage Ansible Modules
# Version 4.0.0
# Copyright (C) 2019-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_firmware
short_description: To perform a component firmware update using the image file available on the local or remote system
version_added: "2.1.0"
description:
- This module allows the firmware update of only one component at a time.
If the module is run for more than one component, an error message is returned.
- Depending on the component, the firmware update is applied after an automatic or manual reboot.
extends_documentation_fragment:
- dellemc.openmanage.redfish_auth_options
options:
image_uri:
description:
- Firmware Image location URI or local path.
- For example- U(http://<web_address>/components.exe) or /home/firmware_repo/component.exe.
type: str
required: True
transfer_protocol:
description: Protocol used to transfer the firmware image file. Applicable for URI based update.
type: str
default: HTTP
choices: ["CIFS", "FTP", "HTTP", "HTTPS", "NSF", "OEM", "SCP", "SFTP", "TFTP"]
requirements:
- "python >= 2.7.5"
- "urllib3"
author:
- "Felix Stephen (@felixs88)"
notes:
- Run this module from a system that has direct access to Redfish APIs.
- This module does not support C(check_mode).
"""
EXAMPLES = """
---
- name: Update the firmware from a single executable file available in a HTTP protocol
dellemc.openmanage.redfish_firmware:
baseuri: "192.168.0.1"
username: "user_name"
password: "user_password"
image_uri: "http://192.168.0.2/firmware_repo/component.exe"
transfer_protocol: "HTTP"
- name: Update the firmware from a single executable file available in a local path
dellemc.openmanage.redfish_firmware:
baseuri: "192.168.0.1"
username: "user_name"
password: "user_password"
image_uri: "/home/firmware_repo/component.exe"
"""
RETURN = """
---
msg:
description: Overall status of the firmware update task.
returned: always
type: str
sample: Successfully submitted the firmware update task.
task:
description: Returns ID and URI of the created task.
returned: success
type: dict
sample: {
"id": "JID_XXXXXXXXXXXX",
"uri": "/redfish/v1/TaskService/Tasks/JID_XXXXXXXXXXXX"
}
error_info:
type: dict
description: Details of http error.
returned: on http error
sample: {
"error": {
"@Message.ExtendedInfo": [
{
"Message": "Unable to complete the operation because the JSON data format entered is invalid.",
"Resolution": "Do the following and the retry the operation:
1) Enter the correct JSON data format and retry the operation.
2) Make sure that no syntax error is present in JSON data format.
3) Make sure that a duplicate key is not present in JSON data format.",
"Severity": "Critical"
},
{
"Message": "The request body submitted was malformed JSON and
could not be parsed by the receiving service.",
"Resolution": "Ensure that the request body is valid JSON and resubmit the request.",
"Severity": "Critical"
}
],
"code": "Base.1.2.GeneralError",
"message": "A general error has occurred. See ExtendedInfo for more information."
}
}
"""
import json
import os
from ansible_collections.dellemc.openmanage.plugins.module_utils.redfish import Redfish
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.urls import ConnectionError, SSLValidationError
from ansible.module_utils.six.moves.urllib.error import URLError, HTTPError
try:
from urllib3.fields import RequestField
from urllib3.filepost import encode_multipart_formdata
HAS_LIB = True
except ImportError:
HAS_LIB = False
UPDATE_SERVICE = "UpdateService"
def _encode_form_data(payload_file):
"""Encode multipart/form-data for file upload."""
fields = []
f_name, f_data, f_type = payload_file.get("file")
f_binary = f_data.read()
req_field = RequestField(name="file", data=f_binary, filename=f_name)
req_field.make_multipart(content_type=f_type)
fields.append(req_field)
data, content_type = encode_multipart_formdata(fields)
return data, content_type
def _get_update_service_target(obj, module):
"""Returns all the URI which is required for firmware update dynamically."""
action_resp = obj.invoke_request("GET", "{0}{1}".format(obj.root_uri, UPDATE_SERVICE))
action_attr = action_resp.json_data["Actions"]
protocol = module.params["transfer_protocol"]
update_uri = None
push_uri = action_resp.json_data.get('HttpPushUri')
inventory_uri = action_resp.json_data.get('FirmwareInventory').get('@odata.id')
if "#UpdateService.SimpleUpdate" in action_attr:
update_service = action_attr.get("#UpdateService.SimpleUpdate")
proto = update_service.get("TransferProtocol@Redfish.AllowableValues")
if isinstance(proto, list) and protocol in proto and 'target' in update_service:
update_uri = update_service.get('target')
else:
module.fail_json(msg="Target firmware version does not support {0} protocol.".format(protocol))
if update_uri is None or push_uri is None or inventory_uri is None:
module.fail_json(msg="Target firmware version does not support redfish firmware update.")
return str(inventory_uri), str(push_uri), str(update_uri)
def firmware_update(obj, module):
"""Firmware update using single binary file from Local path or HTTP location."""
image_path = module.params.get("image_uri")
trans_proto = module.params["transfer_protocol"]
inventory_uri, push_uri, update_uri = _get_update_service_target(obj, module)
if image_path.startswith("http"):
payload = {"ImageURI": image_path, "TransferProtocol": trans_proto}
update_status = obj.invoke_request("POST", update_uri, data=payload)
else:
resp_inv = obj.invoke_request("GET", inventory_uri)
with open(os.path.join(image_path), "rb") as img_file:
binary_payload = {"file": (image_path.split(os.sep)[-1], img_file, "multipart/form-data")}
data, ctype = _encode_form_data(binary_payload)
headers = {"If-Match": resp_inv.headers.get("etag")}
headers.update({"Content-Type": ctype})
upload_status = obj.invoke_request("POST", push_uri, data=data, headers=headers, dump=False,
api_timeout=100)
if upload_status.status_code == 201:
payload = {"ImageURI": upload_status.headers.get("location")}
update_status = obj.invoke_request("POST", update_uri, data=payload)
else:
update_status = upload_status
return update_status
def main():
module = AnsibleModule(
argument_spec={
"baseuri": {"required": True, "type": "str"},
"username": {"required": True, "type": "str"},
"password": {"required": True, "type": "str", "no_log": True},
"image_uri": {"required": True, "type": "str"},
"transfer_protocol": {"type": "str", "default": "HTTP",
"choices": ["CIFS", "FTP", "HTTP", "HTTPS", "NSF", "OEM", "SCP", "SFTP", "TFTP"]},
},
supports_check_mode=False)
if not HAS_LIB:
module.fail_json(msg=missing_required_lib("urllib3"))
try:
message = "Failed to submit the firmware update task."
with Redfish(module.params, req_session=True) as obj:
status = firmware_update(obj, module)
if status.success:
message = "Successfully submitted the firmware update task."
task_uri = status.headers.get("Location")
task_id = task_uri.split("/")[-1]
module.exit_json(msg=message, task={"id": task_id, "uri": task_uri}, changed=True)
module.fail_json(msg=message, error_info=json.loads(status))
except HTTPError as err:
module.fail_json(msg=str(err), error_info=json.load(err))
except (RuntimeError, URLError, SSLValidationError, ConnectionError, KeyError,
ImportError, ValueError, TypeError, IOError, AssertionError) as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()