#!/usr/bin/python # -*- coding: utf-8 -*- # # Dell EMC OpenManage Ansible Modules # Version 3.5.0 # Copyright (C) 2018-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 = """ --- module: idrac_bios short_description: Configure the BIOS attributes version_added: "2.1.0" description: - This module allows to configure the BIOS attributes. extends_documentation_fragment: - dellemc.openmanage.idrac_auth_options options: share_name: type: str description: Network share or a local path. share_user: type: str description: Network share user name. Use the format 'user@domain' or 'domain\\user' if user is part of a domain. This option is mandatory for CIFS share. share_password: type: str description: Network share user password. This option is mandatory for CIFS share. aliases: ['share_pwd'] share_mnt: type: str description: Local mount path of the network share with read-write permission for ansible user. This option is mandatory for network shares. boot_mode: type: str description: - (deprecated)Sets boot mode to BIOS or UEFI. - This option is deprecated, and will be removed in later version. Use I(attributes) for configuring the BIOS attributes. - I(boot_mode) is mutually exclusive with I(boot_sources). choices: [Bios, Uefi] nvme_mode: type: str description: - (deprecated)Configures the NVME mode in the iDRAC 9 based PowerEdge Servers. - This option is deprecated, and will be removed in later version. Use I(attributes) for configuring the BIOS attributes. - I(nvme_mode) is mutually exclusive with I(boot_sources). choices: [NonRaid, Raid] secure_boot_mode: type: str description: - (deprecated)Configures how the BIOS uses the Secure Boot Policy Objects in iDRAC 9 based PowerEdge Servers. - This option is deprecated, and will be removed in later version. Use I(attributes) for configuring the BIOS attributes. - I(secure_boot_mode) is mutually exclusive with I(boot_sources). choices: [AuditMode, DeployedMode, SetupMode, UserMode] onetime_boot_mode: type: str description: - (deprecated)Configures the one time boot mode setting. - This option is deprecated, and will be removed in later version. Use I(attributes) for configuring the BIOS attributes. - I(onetime_boot_mode) is mutually exclusive with I(boot_sources). choices: [Disabled, OneTimeBootSeq, OneTimeCustomBootSeqStr, OneTimeCustomHddSeqStr, OneTimeCustomUefiBootSeqStr, OneTimeHddSeq, OneTimeUefiBootSeq] boot_sequence: type: str description: - "(deprecated)Allows to set the boot sequence in BIOS boot mode or Secure UEFI boot mode by rearranging the boot entries in Fully Qualified Device Descriptor (FQDD)." - TThis option is deprecated, and will be removed in later version. Use I(attributes) for configuring the BIOS attributes. - I(boot_sequence) is mutually exclusive with I(boot_sources). attributes: type: dict description: - Dictionary of BIOS attributes and value pair. Attributes should be part of the Redfish Dell BIOS Attribute Registry. Use U(https://I(idrac_ip)/redfish/v1/Systems/System.Embedded.1/Bios) to view the Redfish URI. - If deprecated options are provided and the same is repeated in I(attributes) then values in I(attributes) will take precedence. - I(attributes) is mutually exclusive with I(boot_sources). boot_sources: type: list elements: raw description: - List of boot devices to set the boot sources settings. - I(boot_sources) is mutually exclusive with I(attributes), I(boot_sequence), I(onetime_boot_mode), I(secure_boot_mode), I(nvme_mode), I(boot_mode). requirements: - "omsdk" - "python >= 2.7.5" author: - "Felix Stephen (@felixs88)" - "Anooja Vardhineni (@anooja-vardhineni)" notes: - This module requires 'Administrator' privilege for I(idrac_user). - Run this module from a system that has direct access to DellEMC iDRAC. - This module supports C(check_mode). """ EXAMPLES = """ --- - name: Configure generic attributes of the BIOS dellemc.openmanage.idrac_bios: idrac_ip: "192.168.0.1" idrac_user: "user_name" idrac_password: "user_password" attributes: BootMode : "Bios" OneTimeBootMode: "Enabled" BootSeqRetry: "Enabled" - name: Configure PXE generic attributes dellemc.openmanage.idrac_bios: idrac_ip: "192.168.0.1" idrac_user: "user_name" idrac_password: "user_password" attributes: PxeDev1EnDis: "Enabled" PxeDev1Protocol: "IPV4" PxeDev1VlanEnDis: "Enabled" PxeDev1VlanId: 1 PxeDev1Interface: "NIC.Embedded.1-1-1" PxeDev1VlanPriority: 2 - name: Configure boot sources dellemc.openmanage.idrac_bios: idrac_ip: "192.168.0.1" idrac_user: "user_name" idrac_password: "user_password" boot_sources: - Name : "NIC.Integrated.1-2-3" Enabled : true Index : 0 - name: Configure multiple boot sources dellemc.openmanage.idrac_bios: idrac_ip: "192.168.0.1" idrac_user: "user_name" idrac_password: "user_password" boot_sources: - Name : "NIC.Integrated.1-1-1" Enabled : true Index : 0 - Name : "NIC.Integrated.2-2-2" Enabled : true Index : 1 - Name : "NIC.Integrated.3-3-3" Enabled : true Index : 2 - name: Configure boot sources - Enabling dellemc.openmanage.idrac_bios: idrac_ip: "192.168.0.1" idrac_user: "user_name" idrac_password: "user_password" boot_sources: - Name : "NIC.Integrated.1-1-1" Enabled : true - name: Configure boot sources - Index dellemc.openmanage.idrac_bios: idrac_ip: "192.168.0.1" idrac_user: "user_name" idrac_password: "user_password" boot_sources: - Name : "NIC.Integrated.1-1-1" Index : 0 """ RETURN = r''' --- msg: description: Configures the BIOS configuration attributes. returned: success type: dict sample: { "@odata.context": "/redfish/v1/$metadata#DellJob.DellJob", "@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/Jobs/JID_873888162305", "@odata.type": "#DellJob.v1_0_0.DellJob", "CompletionTime": "2020-04-20T18:50:20", "Description": "Job Instance", "EndTime": null, "Id": "JID_873888162305", "JobState": "Completed", "JobType": "ImportConfiguration", "Message": "Successfully imported and applied Server Configuration Profile.", "MessageArgs": [], "MessageId": "SYS053", "Name": "Import Configuration", "PercentComplete": 100, "StartTime": "TIME_NOW", "Status": "Success", "TargetSettingsURI": null, "retval": true } error_info: description: Details of the HTTP Error. returned: on HTTP error type: dict sample: { "error": { "code": "Base.1.0.GeneralError", "message": "A general error has occurred. See ExtendedInfo for more information.", "@Message.ExtendedInfo": [ { "MessageId": "GEN1234", "RelatedProperties": [], "Message": "Unable to process the request because an error occurred.", "MessageArgs": [], "Severity": "Critical", "Resolution": "Retry the operation. If the issue persists, contact your system administrator." } ] } } ''' import os import tempfile import json from ansible.module_utils.six.moves.urllib.error import URLError, HTTPError from ansible.module_utils.urls import ConnectionError, SSLValidationError from ansible_collections.dellemc.openmanage.plugins.module_utils.dellemc_idrac import iDRACConnection from ansible.module_utils.basic import AnsibleModule try: from omdrivers.enums.iDRAC.BIOS import (BootModeTypes, NvmeModeTypes, SecureBootModeTypes, OneTimeBootModeTypes) from omdrivers.enums.iDRAC.iDRACEnums import BootModeEnum from omsdk.sdkfile import file_share_manager from omsdk.sdkcreds import UserCredentials except ImportError: pass def run_server_bios_config(idrac, module): msg = {} temp_dir = tempfile.gettempdir() + os.sep share_name = module.params.get('share_name') share_path = share_name if share_name is not None else temp_dir idrac.use_redfish = True upd_share = file_share_manager.create_share_obj(share_path=share_path, mount_point=module.params['share_mnt'], isFolder=True, creds=UserCredentials( module.params['share_user'], module.params['share_password']) ) if not upd_share.IsValid: module.fail_json(msg="Unable to access the share. Ensure that the share name, " "share mount, and share credentials provided are correct.") if module.params['boot_sources']: _validate_params(module.params['boot_sources']) if module.check_mode: idrac.config_mgr.is_change_applicable() msg = idrac.config_mgr.configure_boot_sources( input_boot_devices=module.params['boot_sources']) return msg idrac.config_mgr.set_liason_share(upd_share) if module.params['boot_mode'] and not (module.params['attributes'] and 'BootMode' in module.params['attributes']): idrac.config_mgr.configure_boot_mode(boot_mode=BootModeTypes[module.params['boot_mode']]) if module.params['nvme_mode'] and not (module.params['attributes'] and 'NvmeMode' in module.params['attributes']): idrac.config_mgr.configure_nvme_mode(nvme_mode=NvmeModeTypes[module.params['nvme_mode']]) if module.params['secure_boot_mode'] and not (module.params['attributes'] and 'SecureBootMode' in module.params['attributes']): idrac.config_mgr.configure_secure_boot_mode( secure_boot_mode=SecureBootModeTypes[module.params['secure_boot_mode']]) if module.params['onetime_boot_mode'] and not (module.params['attributes'] and 'OneTimeBootMode' in module.params['attributes']): idrac.config_mgr.configure_onetime_boot_mode( onetime_boot_mode=OneTimeBootModeTypes[module.params['onetime_boot_mode']]) if module.params["boot_mode"] is not None and module.params["boot_sequence"] is not None: idrac.config_mgr.configure_boot_sequence( boot_mode=BootModeEnum[module.params['boot_mode']], boot_sequence=module.params['boot_sequence'] ) if module.params['attributes']: msg['msg'] = idrac.config_mgr.configure_bios( bios_attr_val=module.params['attributes']) if module.check_mode: msg = idrac.config_mgr.is_change_applicable() else: msg = idrac.config_mgr.apply_changes(reboot=True) return msg def _validate_params(params): """ Validate list of dict params. :param params: Ansible list of dict :return: bool or error. """ fields = [ {"name": "Name", "type": str, "required": True}, {"name": "Index", "type": int, "required": False, "min": 0}, {"name": "Enabled", "type": bool, "required": False} ] default = ['Name', 'Index', 'Enabled'] for attr in params: if not isinstance(attr, dict): msg = "{0} must be of type: {1}. {2} ({3}) provided.".format( "attribute values", dict, attr, type(attr)) return msg elif all(k in default for k in attr.keys()): msg = check_params(attr, fields) return msg else: msg = "attribute keys must be one of the {0}.".format(default) return msg msg = _validate_name_index_duplication(params) return msg def _validate_name_index_duplication(params): """ Validate for duplicate names and indices. :param params: Ansible list of dict :return: bool or error. """ msg = "" for i in range(len(params) - 1): for j in range(i + 1, len(params)): if params[i]['Name'] == params[j]['Name']: msg = "duplicate name {0}".format(params[i]['Name']) return msg return msg def check_params(each, fields): """ Each dictionary parameters validation as per the rule defined in fields. :param each: validating each dictionary :param fields: list of dictionary which has the set of rules. :return: tuple which has err and message """ msg = "" for f in fields: if f['name'] not in each and f["required"] is False: continue if not f["name"] in each and f["required"] is True: msg = "{0} is required and must be of type: {1}".format(f['name'], f['type']) elif not isinstance(each[f["name"]], f["type"]): msg = "{0} must be of type: {1}. {2} ({3}) provided.".format( f['name'], f['type'], each[f['name']], type(each[f['name']])) elif f['name'] in each and isinstance(each[f['name']], int) and 'min' in f: if each[f['name']] < f['min']: msg = "{0} must be greater than or equal to: {1}".format(f['name'], f['min']) return msg def main(): mutual_exclusive_args = [['boot_sources', 'attributes'], ['boot_sources', 'secure_boot_mode'], ['boot_sources', 'boot_mode'], ['boot_sources', 'boot_sequence'], ['boot_sources', 'nvme_mode'], ['boot_sources', 'onetime_boot_mode']] module = AnsibleModule( argument_spec={ "idrac_ip": {"required": True, "type": 'str'}, "idrac_user": {"required": True, "type": 'str'}, "idrac_password": {"required": True, "type": 'str', "aliases": ['idrac_pwd'], "no_log": True}, "idrac_port": {"required": False, "default": 443, "type": 'int'}, "share_name": {"required": False, "type": 'str'}, "share_user": {"required": False, "type": 'str'}, "share_password": {"required": False, "type": 'str', "aliases": ['share_pwd'], "no_log": True}, "share_mnt": {"required": False, "type": 'str'}, "boot_mode": {"required": False, "choices": ['Bios', 'Uefi']}, "nvme_mode": {"required": False, "choices": ['NonRaid', 'Raid']}, "secure_boot_mode": {"required": False, "choices": ['AuditMode', 'DeployedMode', 'SetupMode', 'UserMode']}, "onetime_boot_mode": {"required": False, "choices": ['Disabled', 'OneTimeBootSeq', 'OneTimeCustomBootSeqStr', 'OneTimeCustomHddSeqStr', 'OneTimeCustomUefiBootSeqStr', 'OneTimeHddSeq', 'OneTimeUefiBootSeq']}, "boot_sequence": {"required": False, "type": "str"}, "attributes": {"required": False, "type": 'dict'}, "boot_sources": {"required": False, "type": 'list', 'elements': 'raw'} }, mutually_exclusive=mutual_exclusive_args, supports_check_mode=True ) try: with iDRACConnection(module.params) as idrac: msg = run_server_bios_config(idrac, module) changed, failed = False, False if msg.get('Status') == "Success": changed = True if msg.get('Message') == "No changes found to commit!": changed = False elif msg.get('Status') == "Failed": failed = True 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 AttributeError as err: if "NoneType" in str(err): module.fail_json(msg="Unable to access the share. Ensure that the share name, " "share mount, and share credentials provided are correct.") except (RuntimeError, SSLValidationError, ConnectionError, KeyError, ImportError, ValueError, TypeError) as e: module.fail_json(msg=str(e)) module.exit_json(msg=msg, changed=changed, failed=failed) if __name__ == '__main__': main()