summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorNathan Swartz <swartzn@netapp.com>2019-08-28 23:15:19 +0200
committerJake Jackson <jljacks93@gmail.com>2019-08-28 23:15:19 +0200
commitf02a62db509dc7463fab642c9c3458b9bc3476cc (patch)
tree104870d9410c09e72d1ca8232ba2e1a8cb4da166 /lib
parentvalidate-modules - Use error codes instead of numbers (#60711) (diff)
downloadansible-f02a62db509dc7463fab642c9c3458b9bc3476cc.tar.xz
ansible-f02a62db509dc7463fab642c9c3458b9bc3476cc.zip
Add netapp_e_drive_firmware (#59528)
* Add na_santricity_drive_firmware module Manage NetApp E-Series drive firmware downloads Includes unit and integration tests * Rename na_santricity_drive_firmware to netapp_e_drive_firmware
Diffstat (limited to 'lib')
-rw-r--r--lib/ansible/modules/storage/netapp/netapp_e_drive_firmware.py215
1 files changed, 215 insertions, 0 deletions
diff --git a/lib/ansible/modules/storage/netapp/netapp_e_drive_firmware.py b/lib/ansible/modules/storage/netapp/netapp_e_drive_firmware.py
new file mode 100644
index 0000000000..88e9faef35
--- /dev/null
+++ b/lib/ansible/modules/storage/netapp/netapp_e_drive_firmware.py
@@ -0,0 +1,215 @@
+#!/usr/bin/python
+
+# (c) 2016, NetApp, Inc
+# 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
+
+
+ANSIBLE_METADATA = {'metadata_version': '1.1',
+ 'status': ['preview'],
+ 'supported_by': 'community'}
+
+DOCUMENTATION = """
+---
+module: netapp_e_drive_firmware
+version_added: "2.9"
+short_description: NetApp E-Series manage drive firmware
+description:
+ - Ensure drive firmware version is activated on specified drive model.
+author:
+ - Nathan Swartz (@ndswartz)
+extends_documentation_fragment:
+ - netapp.eseries
+options:
+ firmware:
+ description:
+ - list of drive firmware file paths.
+ - NetApp E-Series drives require special firmware which can be downloaded from https://mysupport.netapp.com/NOW/download/tools/diskfw_eseries/
+ type: list
+ required: True
+ wait_for_completion:
+ description:
+ - This flag will cause module to wait for any upgrade actions to complete.
+ type: bool
+ default: false
+ ignore_inaccessible_drives:
+ description:
+ - This flag will determine whether drive firmware upgrade should fail if any affected drives are inaccessible.
+ type: bool
+ default: false
+ upgrade_drives_online:
+ description:
+ - This flag will determine whether drive firmware can be upgrade while drives are accepting I/O.
+ - When I(upgrade_drives_online==False) stop all I/O before running task.
+ type: bool
+ default: true
+"""
+EXAMPLES = """
+- name: Ensure correct firmware versions
+ nac_santricity_drive_firmware:
+ ssid: "1"
+ api_url: "https://192.168.1.100:8443/devmgr/v2"
+ api_username: "admin"
+ api_password: "adminpass"
+ validate_certs: true
+ firmware: "path/to/drive_firmware"
+ wait_for_completion: true
+ ignore_inaccessible_drives: false
+"""
+RETURN = """
+msg:
+ description: Whether any drive firmware was upgraded and whether it is in progress.
+ type: str
+ returned: always
+ sample:
+ { changed: True, upgrade_in_process: True }
+"""
+import os
+import re
+
+from time import sleep
+from ansible.module_utils.netapp import NetAppESeriesModule, create_multipart_formdata
+from ansible.module_utils._text import to_native, to_text, to_bytes
+
+
+class NetAppESeriesDriveFirmware(NetAppESeriesModule):
+ WAIT_TIMEOUT_SEC = 60 * 15
+
+ def __init__(self):
+ ansible_options = dict(
+ firmware=dict(type="list", required=True),
+ wait_for_completion=dict(type="bool", default=False),
+ ignore_inaccessible_drives=dict(type="bool", default=False),
+ upgrade_drives_online=dict(type="bool", default=True))
+
+ super(NetAppESeriesDriveFirmware, self).__init__(ansible_options=ansible_options,
+ web_services_version="02.00.0000.0000",
+ supports_check_mode=True)
+
+ args = self.module.params
+ self.firmware_list = args["firmware"]
+ self.wait_for_completion = args["wait_for_completion"]
+ self.ignore_inaccessible_drives = args["ignore_inaccessible_drives"]
+ self.upgrade_drives_online = args["upgrade_drives_online"]
+
+ self.upgrade_list_cache = None
+
+ self.upgrade_required_cache = None
+ self.upgrade_in_progress = False
+ self.drive_info_cache = None
+
+ def upload_firmware(self):
+ """Ensure firmware has been upload prior to uploaded."""
+ for firmware in self.firmware_list:
+ firmware_name = os.path.basename(firmware)
+ files = [("file", firmware_name, firmware)]
+ headers, data = create_multipart_formdata(files)
+ try:
+ rc, response = self.request("/files/drive", method="POST", headers=headers, data=data)
+ except Exception as error:
+ self.module.fail_json(msg="Failed to upload drive firmware [%s]. Array [%s]. Error [%s]." % (firmware_name, self.ssid, to_native(error)))
+
+ def upgrade_list(self):
+ """Determine whether firmware is compatible with the specified drives."""
+ if self.upgrade_list_cache is None:
+ self.upgrade_list_cache = list()
+ try:
+ rc, response = self.request("storage-systems/%s/firmware/drives" % self.ssid)
+
+ # Create upgrade list, this ensures only the firmware uploaded is applied
+ for firmware in self.firmware_list:
+ filename = os.path.basename(firmware)
+
+ for uploaded_firmware in response["compatibilities"]:
+ if uploaded_firmware["filename"] == filename:
+
+ # Determine whether upgrade is required
+ drive_reference_list = []
+ for drive in uploaded_firmware["compatibleDrives"]:
+ try:
+ rc, drive_info = self.request("storage-systems/%s/drives/%s" % (self.ssid, drive["driveRef"]))
+
+ # Add drive references that are supported and differ from current firmware
+ if (drive_info["firmwareVersion"] != uploaded_firmware["firmwareVersion"] and
+ uploaded_firmware["firmwareVersion"] in uploaded_firmware["supportedFirmwareVersions"]):
+
+ if self.ignore_inaccessible_drives or (not drive_info["offline"] and drive_info["available"]):
+ drive_reference_list.append(drive["driveRef"])
+
+ if not drive["onlineUpgradeCapable"] and self.upgrade_drives_online:
+ self.module.fail_json(msg="Drive is not capable of online upgrade. Array [%s]. Drive [%s]."
+ % (self.ssid, drive["driveRef"]))
+
+ except Exception as error:
+ self.module.fail_json(msg="Failed to retrieve drive information. Array [%s]. Drive [%s]. Error [%s]."
+ % (self.ssid, drive["driveRef"], to_native(error)))
+
+ if drive_reference_list:
+ self.upgrade_list_cache.extend([{"filename": filename, "driveRefList": drive_reference_list}])
+
+ except Exception as error:
+ self.module.fail_json(msg="Failed to complete compatibility and health check. Array [%s]. Error [%s]." % (self.ssid, to_native(error)))
+
+ return self.upgrade_list_cache
+
+ def wait_for_upgrade_completion(self):
+ """Wait for drive firmware upgrade to complete."""
+ drive_references = [reference for drive in self.upgrade_list() for reference in drive["driveRefList"]]
+ last_status = None
+ for attempt in range(int(self.WAIT_TIMEOUT_SEC / 5)):
+ try:
+ rc, response = self.request("storage-systems/%s/firmware/drives/state" % self.ssid)
+
+ # Check drive status
+ for status in response["driveStatus"]:
+ last_status = status
+ if status["driveRef"] in drive_references:
+ if status["status"] == "okay":
+ continue
+ elif status["status"] in ["inProgress", "inProgressRecon", "pending", "notAttempted"]:
+ break
+ else:
+ self.module.fail_json(msg="Drive firmware upgrade failed. Array [%s]. Drive [%s]. Status [%s]."
+ % (self.ssid, status["driveRef"], status["status"]))
+ else:
+ self.upgrade_in_progress = False
+ break
+ except Exception as error:
+ self.module.fail_json(msg="Failed to retrieve drive status. Array [%s]. Error [%s]." % (self.ssid, to_native(error)))
+
+ sleep(5)
+ else:
+ self.module.fail_json(msg="Timed out waiting for drive firmware upgrade. Array [%s]. Status [%s]." % (self.ssid, last_status))
+
+ def upgrade(self):
+ """Apply firmware to applicable drives."""
+ try:
+ rc, response = self.request("storage-systems/%s/firmware/drives/initiate-upgrade?onlineUpdate=%s"
+ % (self.ssid, "true" if self.upgrade_drives_online else "false"), method="POST", data=self.upgrade_list())
+ self.upgrade_in_progress = True
+ except Exception as error:
+ self.module.fail_json(msg="Failed to upgrade drive firmware. Array [%s]. Error [%s]." % (self.ssid, to_native(error)))
+
+ if self.wait_for_completion:
+ self.wait_for_upgrade_completion()
+
+ def apply(self):
+ """Apply firmware policy has been enforced on E-Series storage system."""
+ self.upload_firmware()
+
+ if self.upgrade_list() and not self.module.check_mode:
+ self.upgrade()
+
+ self.module.exit_json(changed=True if self.upgrade_list() else False,
+ upgrade_in_process=self.upgrade_in_progress)
+
+
+def main():
+ drive_firmware = NetAppESeriesDriveFirmware()
+ drive_firmware.apply()
+
+
+if __name__ == '__main__':
+ main()