Add DellEMC iDRAC Firmware module (#46675)
Co-Authored-By: rajeevarakkal <36444805+rajeevarakkal@users.noreply.github.com> Co-Authored-By: Sviatoslav Sydorenko <578543+webknjaz@users.noreply.github.com>
This commit is contained in:
parent
c9040d7579
commit
62b2a08cfb
6 changed files with 269 additions and 0 deletions
3
.github/BOTMETA.yml
vendored
3
.github/BOTMETA.yml
vendored
|
@ -310,6 +310,7 @@ files:
|
|||
$modules/remote_management/redfish/: $team_redfish
|
||||
$modules/remote_management/stacki/stacki_host.py: bbyhuy bsanders
|
||||
$modules/remote_management/ucs/: $team_ucs
|
||||
$modules/remote_management/dellemc/: rajeevarakkal
|
||||
$modules/source_control/git.py: $team_ansible
|
||||
$modules/source_control/github_key.py:
|
||||
ignored: erydo
|
||||
|
@ -623,6 +624,8 @@ files:
|
|||
$module_utils/remote_management/ucs:
|
||||
maintainers: $team_ucs
|
||||
labels: ucs
|
||||
$module_utils/remote_management/dellemc:
|
||||
maintainers: rajeevarakkal
|
||||
$module_utils/pure.py:
|
||||
maintainers: $team_purestorage
|
||||
labels: pure_storage
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Dell EMC OpenManage Ansible Modules
|
||||
# Version 1.0
|
||||
# Copyright (C) 2018 Dell Inc.
|
||||
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# All rights reserved. Dell, EMC, and other trademarks are trademarks of Dell Inc. or its subsidiaries.
|
||||
# Other trademarks may be trademarks of their respective owners.
|
||||
#
|
||||
|
||||
from __future__ import (absolute_import, division,
|
||||
print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
try:
|
||||
from omsdk.sdkinfra import sdkinfra
|
||||
from omsdk.sdkcreds import UserCredentials
|
||||
from omsdk.sdkfile import FileOnShare, file_share_manager
|
||||
from omsdk.sdkprotopref import ProtoPreference, ProtocolEnum
|
||||
from omsdk.http.sdkwsmanbase import WsManOptions
|
||||
HAS_OMSDK = True
|
||||
except ImportError:
|
||||
HAS_OMSDK = False
|
||||
|
||||
|
||||
class iDRACConnection:
|
||||
|
||||
def __init__(self, module_params):
|
||||
if not HAS_OMSDK:
|
||||
raise ImportError("Dell EMC OMSDK library is required for this module")
|
||||
self.idrac_ip = module_params['idrac_ip']
|
||||
self.idrac_user = module_params['idrac_user']
|
||||
self.idrac_pwd = module_params['idrac_pwd']
|
||||
self.idrac_port = module_params['idrac_port']
|
||||
if not all((self.idrac_ip, self.idrac_user, self.idrac_pwd)):
|
||||
raise ValueError("hostname, username and password required")
|
||||
self.handle = None
|
||||
self.creds = UserCredentials(self.idrac_user, self.idrac_pwd)
|
||||
self.pOp = WsManOptions(port=self.idrac_port)
|
||||
self.sdk = sdkinfra()
|
||||
if self.sdk is None:
|
||||
msg = "Could not initialize iDRAC drivers."
|
||||
raise RuntimeError(msg)
|
||||
|
||||
def __enter__(self):
|
||||
self.sdk.importPath()
|
||||
self.handle = self.sdk.get_driver(self.sdk.driver_enum.iDRAC, self.idrac_ip, self.creds, pOptions=self.pOp)
|
||||
if self.handle is None:
|
||||
msg = "Could not find device driver for iDRAC with IP Address: {0}".format(self.idrac_ip)
|
||||
raise RuntimeError(msg)
|
||||
return self.handle
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.handle.disconnect()
|
||||
return False
|
|
@ -0,0 +1,209 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Dell EMC OpenManage Ansible Modules
|
||||
# Version 1.0
|
||||
# Copyright (C) 2018 Dell Inc.
|
||||
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# All rights reserved. Dell, EMC, and other trademarks are trademarks of Dell Inc. or its subsidiaries.
|
||||
# Other trademarks may be trademarks of their respective owners.
|
||||
#
|
||||
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: dellemc_idrac_firmware
|
||||
short_description: Firmware update from a repository on a network share (CIFS, NFS).
|
||||
version_added: "2.8"
|
||||
description:
|
||||
- Update the Firmware by connecting to a network share (either CIFS or NFS) that contains a catalog of
|
||||
available updates.
|
||||
- Network share should contain a valid repository of Update Packages (DUPs) and a catalog file describing the DUPs.
|
||||
- All applicable updates contained in the repository are applied to the system.
|
||||
- This feature is available only with iDRAC Enterprise License.
|
||||
options:
|
||||
idrac_ip:
|
||||
required: True
|
||||
description: iDRAC IP Address.
|
||||
idrac_user:
|
||||
required: True
|
||||
description: iDRAC username.
|
||||
idrac_pwd:
|
||||
required: True
|
||||
description: iDRAC user password.
|
||||
idrac_port:
|
||||
required: False
|
||||
description: iDRAC port.
|
||||
default: 443
|
||||
share_name:
|
||||
required: True
|
||||
description: CIFS or NFS Network share.
|
||||
share_user:
|
||||
required: False
|
||||
description: Network share user in the format 'user@domain' or 'domain\\user' if user is
|
||||
part of a domain else 'user'. This option is mandatory for CIFS Network Share.
|
||||
share_pwd:
|
||||
required: False
|
||||
description: Network share user password. This option is mandatory for CIFS Network Share.
|
||||
share_mnt:
|
||||
required: True
|
||||
description: Local mount path of the network share with read-write permission for ansible user.
|
||||
This option is mandatory for Network Share.
|
||||
reboot:
|
||||
required: False
|
||||
description: Whether to reboots after applying the updates or not.
|
||||
default: False
|
||||
type: bool
|
||||
job_wait:
|
||||
required: False
|
||||
description: Whether to wait for job completion or not.
|
||||
type: bool
|
||||
default: True
|
||||
catalog_file_name:
|
||||
required: False
|
||||
description: Catalog file name relative to the I(share_name).
|
||||
type: str
|
||||
default: 'Catalog.xml'
|
||||
|
||||
requirements:
|
||||
- "omsdk"
|
||||
- "python >= 2.7.5"
|
||||
author: "Rajeev Arakkal (@rajeevarakkal)"
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
---
|
||||
- name: Update firmware from repository on a Network Share
|
||||
dellemc_idrac_firmware:
|
||||
idrac_ip: "192.168.0.1"
|
||||
idrac_user: "user_name"
|
||||
idrac_pwd: "user_pwd"
|
||||
share_name: "192.168.0.0:/share"
|
||||
share_user: "share_user_name"
|
||||
share_pwd: "share_user_pwd"
|
||||
share_mnt: "/mnt/share"
|
||||
reboot: True
|
||||
job_wait: True
|
||||
catalog_file_name: "Catalog.xml"
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
---
|
||||
msg:
|
||||
type: string
|
||||
description: Over all firmware update status.
|
||||
returned: always
|
||||
sample: "Successfully updated the firmware."
|
||||
update_status:
|
||||
type: dict
|
||||
description: Firmware Update job and progress details from the iDRAC.
|
||||
returned: success
|
||||
sample: {
|
||||
'InstanceID': 'JID_XXXXXXXXXXXX',
|
||||
'JobState': 'Completed',
|
||||
'Message': 'Job completed successfully.',
|
||||
'MessageId': 'REDXXX',
|
||||
'Name': 'Repository Update',
|
||||
'JobStartTime': 'NA',
|
||||
'Status': 'Success',
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
from ansible.module_utils.remote_management.dellemc.dellemc_idrac import iDRACConnection
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
try:
|
||||
from omsdk.sdkcreds import UserCredentials
|
||||
from omsdk.sdkfile import FileOnShare
|
||||
HAS_OMSDK = True
|
||||
except ImportError:
|
||||
HAS_OMSDK = False
|
||||
|
||||
|
||||
def _validate_catalog_file(catalog_file_name):
|
||||
normilized_file_name = catalog_file_name.lower()
|
||||
if not normilized_file_name:
|
||||
raise ValueError('catalog_file_name should be a non-empty string.')
|
||||
elif not normilized_file_name.endswith("xml"):
|
||||
raise ValueError('catalog_file_name should be an XML file.')
|
||||
|
||||
|
||||
def update_firmware(idrac, module):
|
||||
"""Update firmware from a network share and return the job details."""
|
||||
msg = {}
|
||||
msg['changed'] = False
|
||||
msg['update_status'] = {}
|
||||
|
||||
try:
|
||||
upd_share = FileOnShare(remote=module.params['share_name'] + "/" + module.params['catalog_file_name'],
|
||||
mount_point=module.params['share_mnt'],
|
||||
isFolder=False,
|
||||
creds=UserCredentials(
|
||||
module.params['share_user'],
|
||||
module.params['share_pwd'])
|
||||
)
|
||||
|
||||
idrac.use_redfish = True
|
||||
if '12' in idrac.ServerGeneration or '13' in idrac.ServerGeneration:
|
||||
idrac.use_redfish = False
|
||||
|
||||
apply_update = True
|
||||
msg['update_status'] = idrac.update_mgr.update_from_repo(upd_share,
|
||||
apply_update,
|
||||
module.params['reboot'],
|
||||
module.params['job_wait'])
|
||||
except RuntimeError as e:
|
||||
module.fail_json(msg=str(e))
|
||||
|
||||
if "Status" in msg['update_status']:
|
||||
if msg['update_status']['Status'] == "Success":
|
||||
if module.params['job_wait']:
|
||||
msg['changed'] = True
|
||||
else:
|
||||
module.fail_json(msg='Failed to update firmware.', update_status=msg['update_status'])
|
||||
return msg
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec={
|
||||
"idrac_ip": {"required": True, "type": str},
|
||||
"idrac_user": {"required": True, "type": str},
|
||||
"idrac_pwd": {"required": True, "type": str, "no_log": True},
|
||||
"idrac_port": {"required": False, "default": 443, "type": int},
|
||||
|
||||
"share_name": {"required": True, "type": str},
|
||||
"share_user": {"required": False, "type": str},
|
||||
"share_pwd": {"required": False, "type": str, "no_log": True},
|
||||
"share_mnt": {"required": True, "type": str},
|
||||
|
||||
"catalog_file_name": {"required": False, "type": str, "default": "Catalog.xml"},
|
||||
"reboot": {"required": False, "type": bool, "default": False},
|
||||
"job_wait": {"required": False, "type": bool, "default": True},
|
||||
},
|
||||
|
||||
supports_check_mode=False)
|
||||
|
||||
try:
|
||||
# Validate the catalog file
|
||||
_validate_catalog_file(module.params['catalog_file_name'])
|
||||
# Connect to iDRAC and update firmware
|
||||
with iDRACConnection(module.params) as idrac:
|
||||
update_status = update_firmware(idrac, module)
|
||||
except (ImportError, ValueError, RuntimeError) as e:
|
||||
module.fail_json(msg=str(e))
|
||||
|
||||
module.exit_json(msg='Successfully updated the firmware.', update_status=update_status)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Loading…
Reference in a new issue