Server IP : 85.214.239.14 / Your IP : 18.191.218.206 Web Server : Apache/2.4.62 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Tue Jan 9 19:45:01 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.18 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /usr/lib/python3/dist-packages/ansible_collections/dellemc/openmanage/plugins/modules/ |
Upload File : |
#!/usr/bin/python # -*- coding: utf-8 -*- # # Dell EMC OpenManage Ansible Modules # Version 5.1.0 # Copyright (C) 2020-2022 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: ome_identity_pool short_description: Manages identity pool settings on OpenManage Enterprise version_added: "2.1.0" description: This module allows to create, modify, or delete a single identity pool on OpenManage Enterprise. extends_documentation_fragment: - dellemc.openmanage.ome_auth_options options: state: description: - C(present) modifies an existing identity pool. If the provided I (pool_name) does not exist, it creates an identity pool. - C(absent) deletes an existing identity pool. type: str default: present choices: [present, absent] pool_name: type: str required: True description: - This option is mandatory for I(state) when creating, modifying and deleting an identity pool. new_pool_name: type: str description: - After creating an identity pool, I(pool_name) can be changed to I(new_pool_name). - This option is ignored when creating an identity pool. pool_description: type: str description: - Description of the identity pool. ethernet_settings: type: dict description: - Applicable for creating and modifying an identity pool using Ethernet settings. - I(starting_mac_address) and I(identity_count) are required to create an identity pool. suboptions: starting_mac_address: description: Starting MAC address of the ethernet setting. type: str identity_count: description: Number of MAC addresses. type: int fcoe_settings: type: dict description: - Applicable for creating and modifying an identity pool using FCoE settings. - I(starting_mac_address) and I(identity_count) are required to create an identity pool. suboptions: starting_mac_address: description: Starting MAC Address of the FCoE setting. type: str identity_count: description: Number of MAC addresses. type: int iscsi_settings: type: dict description: - Applicable for creating and modifying an identity pool using ISCSI settings. - I(starting_mac_address), I(identity_count), I(iqn_prefix), I(ip_range) and I(subnet_mask) are required to create an identity pool. suboptions: starting_mac_address: description: Starting MAC address of the iSCSI setting.This is required option for iSCSI setting. type: str identity_count: description: Number of MAC addresses. type: int initiator_config: type: dict description: - Applicable for creating and modifying an identity pool using iSCSI Initiator settings. suboptions: iqn_prefix: description: IQN prefix addresses. type: str initiator_ip_pool_settings: type: dict description: - Applicable for creating and modifying an identity pool using ISCSI Initiator IP pool settings. suboptions: ip_range: description: Range of non-multicast IP addresses. type: str subnet_mask: description: Subnet mask for I(ip_range). type: str gateway: description: IP address of gateway. type: str primary_dns_server: description: IP address of the primary DNS server. type: str secondary_dns_server: description: IP address of the secondary DNS server. type: str fc_settings: type: dict description: - Applicable for creating and modifying an identity pool using fibre channel(FC) settings. - This option allows OpenManage Enterprise to generate a Worldwide port name (WWPN) and Worldwide node name (WWNN) address. - The value 0x2001 is beginning to the starting address for the generation of a WWPN, and 0x2000 for a WWNN. - I(starting_address) and I(identity_count) are required to create an identity pool. suboptions: starting_address: description: Starting MAC Address of FC setting.I(starting_address) is required to option to create FC settings. type: str identity_count: description: Number of MAC addresses.I(identity_count) is required to option to create FC settings. type: int requirements: - "python >= 3.8.6" author: - "Sajna Shetty(@Sajna-Shetty)" - "Deepak Joshi(@Dell-Deepak-Joshi))" notes: - Run this module from a system that has direct access to DellEMC OpenManage Enterprise. - This module supports C(check_mode). ''' EXAMPLES = r''' --- - name: Create an identity pool using ethernet, FCoE, iSCSI and FC settings dellemc.openmanage.ome_identity_pool: hostname: "192.168.0.1" username: "username" password: "password" ca_path: "/path/to/ca_cert.pem" state: present pool_name: "pool1" pool_description: "Identity pool with Ethernet, FCoE, iSCSI and FC settings" ethernet_settings: starting_mac_address: "50:50:50:50:50:00" identity_count: 60 fcoe_settings: starting_mac_address: "70:70:70:70:70:00" identity_count: 75 iscsi_settings: starting_mac_address: "60:60:60:60:60:00" identity_count: 30 initiator_config: iqn_prefix: "iqn.myprefix." initiator_ip_pool_settings: ip_range: "10.33.0.1-10.33.0.255" subnet_mask: "255.255.255.0" gateway: "192.168.4.1" primary_dns_server : "10.8.8.8" secondary_dns_server : "8.8.8.8" fc_settings: starting_address: "30:30:30:30:30:00" identity_count: 45 - name: Create an identity pool using only ethernet settings dellemc.openmanage.ome_identity_pool: hostname: "192.168.0.1" username: "username" password: "password" ca_path: "/path/to/ca_cert.pem" pool_name: "pool2" pool_description: "create identity pool with ethernet" ethernet_settings: starting_mac_address: "aa-bb-cc-dd-ee-aa" identity_count: 80 - name: Modify an identity pool dellemc.openmanage.ome_identity_pool: hostname: "192.168.0.1" username: "username" password: "password" ca_path: "/path/to/ca_cert.pem" pool_name: "pool2" new_pool_name: "pool3" pool_description: "modifying identity pool with ethernet and fcoe settings" ethernet_settings: starting_mac_address: "90-90-90-90-90-90" identity_count: 61 fcoe_settings: starting_mac_address: "aabb.ccdd.5050" identity_count: 77 - name: Modify an identity pool using iSCSI and FC settings dellemc.openmanage.ome_identity_pool: hostname: "{{hostname}}" username: "{{username}}" password: "{{password}}" ca_path: "/path/to/ca_cert.pem" pool_name: "pool_new" new_pool_name: "pool_new2" pool_description: "modifying identity pool with iscsi and fc settings" iscsi_settings: identity_count: 99 initiator_config: iqn_prefix: "iqn1.myprefix2." initiator_ip_pool_settings: gateway: "192.168.4.5" fc_settings: starting_address: "10:10:10:10:10:10" identity_count: 98 - name: Delete an identity pool dellemc.openmanage.ome_identity_pool: hostname: "192.168.0.1" username: "username" password: "password" ca_path: "/path/to/ca_cert.pem" state: "absent" pool_name: "pool2" ''' RETURN = r''' --- msg: type: str description: Overall status of the identity pool operation. returned: always sample: "Successfully created an identity pool." pool_status: type: dict description: Details of the user operation, when I(state) is C(present). returned: success sample: { "Id":29, "IsSuccessful":True, "Issues":[] } error_info: description: Details of the HTTP Error. returned: on HTTP error type: dict sample: { "error": { "@Message.ExtendedInfo": [{ "Message": "Unable to process the request because an error occurred: Ethernet-MAC Range overlap found (in this Identity Pool or in a different one) .", "MessageArgs": [Ethernet-MAC Range overlap found (in this Identity Pool or in a different one)"], "MessageId": "CGEN6001", "RelatedProperties": [], "Resolution": "Retry the operation. If the issue persists, contact your system administrator.", "Severity": "Critical" }], "code": "Base.1.0.GeneralError", "message": "A general error has occurred. See ExtendedInfo for more information." }} ''' import re import json import codecs import binascii from ssl import SSLError from ansible.module_utils.basic import AnsibleModule from ansible_collections.dellemc.openmanage.plugins.module_utils.ome import RestOME, ome_auth_params from ansible.module_utils.urls import open_url, ConnectionError, SSLValidationError from ansible.module_utils.six.moves.urllib.error import URLError, HTTPError IDENTITY_URI = "IdentityPoolService/IdentityPools" CHANGES_FOUND = "Changes found to be applied." NO_CHANGES_FOUND = "No changes found to be applied." def get_identity_pool_id_by_name(pool_name, rest_obj): pool_id = 0 attributes = None identity_list = rest_obj.get_all_report_details(IDENTITY_URI)["report_list"] for item in identity_list: if pool_name == item["Name"]: pool_id = item["Id"] attributes = item break return pool_id, attributes def mac_validation(mac_input): match_found = re.match("[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$|" "([0-9a-f]{4}([.])[0-9a-f]{4}([.])[0-9a-f]{4})$", mac_input.lower()) return match_found def mac_to_base64_conversion(mac_address, module): try: if mac_address: allowed_mac_separators = [':', '-', '.'] for sep in allowed_mac_separators: if sep in mac_address: b64_mac_address = codecs.encode(codecs.decode( mac_address.replace(sep, ''), 'hex'), 'base64') address = codecs.decode(b64_mac_address, 'utf-8').rstrip() return address except binascii.Error: module.fail_json(msg='Encoding of MAC address {0} to base64 ' 'failed'.format(mac_address)) def update_modify_setting(modify_payload, existing_payload, setting_type, sub_keys): """update current pool sub setting setting to modify payload if not provided in the options to avoid the null update from ome""" for sub_key in sub_keys: if sub_key not in modify_payload[setting_type] and sub_key in existing_payload[setting_type]: modify_payload[setting_type][sub_key] = existing_payload[setting_type][sub_key] elif existing_payload[setting_type]: if modify_payload[setting_type].get(sub_key) and existing_payload[setting_type].get(sub_key): modify_setting = modify_payload[setting_type][sub_key] existing_setting_payload = existing_payload[setting_type][sub_key] diff_item = list(set(existing_setting_payload) - set(modify_setting)) for key in diff_item: modify_payload[setting_type][sub_key][key] = existing_setting_payload[key] def get_updated_modify_payload(modify_payload, existing_payload): """update current pool setting setting to modify payload if not provided in the options to avoid the null update from ome""" remove_unwanted_key_list = ['@odata.type', '@odata.id', 'CreatedBy', 'CreationTime', 'LastUpdatedBy', 'LastUpdateTime', 'UsageCounts', 'UsageIdentitySets@odata.navigationLink'] [existing_payload.pop(key) for key in remove_unwanted_key_list if key in existing_payload] for key, val in existing_payload.items(): if key not in modify_payload: modify_payload[key] = val else: if existing_payload.get(key) and key == "EthernetSettings" or key == "FcoeSettings": update_modify_setting(modify_payload, existing_payload, key, ["Mac"]) elif existing_payload.get(key) and key == "FcSettings": update_modify_setting(modify_payload, existing_payload, key, ["Wwnn", "Wwpn"]) elif existing_payload.get(key) and key == "IscsiSettings": update_modify_setting(modify_payload, existing_payload, key, ["Mac", "InitiatorConfig", "InitiatorIpPoolSettings"]) modify_payload = dict([(k, v) for k, v in modify_payload.items() if v is not None]) return modify_payload def update_mac_settings(payload, settings_params, setting_type, module): """payload update for ethernet and fcoe settings and isci settings and convert to MAC address to base 64 format""" mac_address = settings_params.get("starting_mac_address") mac_base_64_format = None if mac_address: match_found = mac_validation(mac_address) if match_found: mac_base_64_format = mac_to_base64_conversion(mac_address, module) else: module.fail_json(msg="Please provide the valid MAC address format for {0} settings." .format(setting_type.split('Settings')[0])) sub_setting_mapper = {"StartingMacAddress": mac_base_64_format, "IdentityCount": settings_params.get("identity_count")} sub_settings_payload = dict([(k, v) for k, v in sub_setting_mapper.items() if v is not None]) if any(sub_settings_payload): payload.update({setting_type: {"Mac": sub_settings_payload}}) def update_iscsi_specific_settings(payload, settings_params, setting_type): """payload update for Iscsi specific settings""" sub_setting_mapper = {} initiator_config = settings_params.get("initiator_config") if initiator_config and initiator_config.get("iqn_prefix"): sub_setting_mapper.update({ "InitiatorConfig": {"IqnPrefix": initiator_config.get("iqn_prefix")}}) if settings_params.get("initiator_ip_pool_settings"): initiator_ip_pool_settings = settings_params["initiator_ip_pool_settings"] initiator_ip_pool_settings = {"IpRange": initiator_ip_pool_settings.get("ip_range"), "SubnetMask": initiator_ip_pool_settings.get("subnet_mask"), "Gateway": initiator_ip_pool_settings.get("gateway"), "PrimaryDnsServer": initiator_ip_pool_settings.get("primary_dns_server"), "SecondaryDnsServer": initiator_ip_pool_settings.get("secondary_dns_server")} initiator_ip_pool_settings = dict([(k, v) for k, v in initiator_ip_pool_settings.items() if v is not None]) sub_setting_mapper.update({ "InitiatorIpPoolSettings": initiator_ip_pool_settings}) if any(sub_setting_mapper): if "IscsiSettings" in payload: """update MAC address setting""" sub_setting_mapper.update(payload[setting_type]) sub_setting_mapper = dict([(key, val) for key, val in sub_setting_mapper.items() if any(val)]) payload.update({setting_type: sub_setting_mapper}) def get_wwn_address_prefix(starting_address): """Prefix wwnn and wwpn MAC address with 20x00 and 20x01 respectively""" delimiter, wwnn_prefix, wwpn_prefix = None, None, None if "." in starting_address: delimiter = "." elif ":" in starting_address: delimiter = ":" elif "-" in starting_address: delimiter = "-" length = len(starting_address.split(delimiter)[0]) if length == 4: wwnn_prefix = "2000{0}".format(delimiter) wwpn_prefix = "2001{0}".format(delimiter) else: wwnn_prefix = "20{0}00{0}".format(delimiter) wwpn_prefix = "20{0}01{0}".format(delimiter) return wwnn_prefix, wwpn_prefix def update_fc_settings(payload, settings_params, setting_type, module): """payload update for Fibre Channel specific settings payload: other setting payload settings_params: fc setting parameters setting_type: "FcSettings" """ sub_setting_mapper = {} starting_address = settings_params.get("starting_address") identity_count = settings_params.get("identity_count") wwnn_payload = {} wwpn_payload = {} if starting_address: if not mac_validation(starting_address): module.fail_json(msg="Please provide the valid starting address format for FC settings.") wwnn_prefix, wwpn_prefix = get_wwn_address_prefix(starting_address) wwnn_address = mac_to_base64_conversion(wwnn_prefix + starting_address, module) wwpn_address = mac_to_base64_conversion(wwpn_prefix + starting_address, module) wwnn_payload.update({"StartingAddress": wwnn_address}) wwpn_payload.update({"StartingAddress": wwpn_address}) if identity_count is not None: wwnn_payload.update({"IdentityCount": identity_count}) wwpn_payload.update({"IdentityCount": identity_count}) sub_setting_mapper.update({"Wwnn": wwnn_payload, "Wwpn": wwpn_payload}) sub_setting_mapper = dict([(key, val) for key, val in sub_setting_mapper.items() if any(val)]) if any(sub_setting_mapper): payload.update({setting_type: sub_setting_mapper}) def get_payload(module, pool_id=None): """create payload for create and modify operations""" module_params = module.params setting_payload = { "Description": module_params.get("pool_description"), "Name": module_params["pool_name"] } fcoe_settings_params = module_params.get("fcoe_settings") ethernet_settings_params = module_params.get("ethernet_settings") iscsi_settings_params = module_params.get("iscsi_settings") fc_settings_params = module_params.get("fc_settings") if fcoe_settings_params: update_mac_settings(setting_payload, fcoe_settings_params, "FcoeSettings", module) if ethernet_settings_params: update_mac_settings(setting_payload, ethernet_settings_params, "EthernetSettings", module) if iscsi_settings_params: update_mac_settings(setting_payload, iscsi_settings_params, "IscsiSettings", module) update_iscsi_specific_settings(setting_payload, iscsi_settings_params, "IscsiSettings") if fc_settings_params: update_fc_settings(setting_payload, fc_settings_params, "FcSettings", module) if pool_id: new_name = module_params.get("new_pool_name") if new_name is not None: setting_payload.update({"Name": new_name}) setting_payload["Id"] = pool_id payload = dict([(k, v) for k, v in setting_payload.items() if v is not None]) return payload def compare_nested_dict(modify_setting_payload, existing_setting_payload): """compare existing and requested setting values of identity pool in case of modify operations if both are same return True""" for key, val in modify_setting_payload.items(): if existing_setting_payload is None or existing_setting_payload.get(key) is None: return False elif isinstance(val, dict): if not compare_nested_dict(val, existing_setting_payload.get(key)): return False elif val != existing_setting_payload.get(key): return False return True def validate_modify_create_payload(setting_payload, module, action): for key, val in setting_payload.items(): if key in ["EthernetSettings", "FcoeSettings"] and val: sub_config = val.get("Mac") if sub_config is None or not all([sub_config.get("IdentityCount"), sub_config.get("StartingMacAddress")]): module.fail_json(msg="Both starting MAC address and identity count is required to {0} an" " identity pool using {1} settings.".format(action, ''.join(key.split('Settings')))) elif key == "FcSettings" and val: sub_config = val.get("Wwnn") if sub_config is None or not all([sub_config.get("IdentityCount"), sub_config.get("StartingAddress")]): module.fail_json(msg="Both starting MAC address and identity count is required to" " {0} an identity pool using Fc settings.".format(action)) elif key == "IscsiSettings" and val: sub_config1 = val.get("Mac") sub_config2 = val.get("InitiatorIpPoolSettings") if sub_config1 is None or not all([sub_config1.get("IdentityCount"), sub_config1.get("StartingMacAddress")]): module.fail_json(msg="Both starting MAC address and identity count is required to {0} an" " identity pool using {1} settings.".format(action, ''.join(key.split('Settings')))) elif sub_config2: if not all([sub_config2.get("IpRange"), sub_config2.get("SubnetMask")]): module.fail_json(msg="Both ip range and subnet mask in required to {0} an identity" " pool using iSCSI settings.".format(action)) def pool_create_modify(module, rest_obj): pool_name = module.params["pool_name"] pool_id, existing_payload = get_identity_pool_id_by_name(pool_name, rest_obj) method = "POST" uri = IDENTITY_URI action = "create" setting_payload = get_payload(module, pool_id) if pool_id: action = "modify" method = "PUT" uri = uri + "({0})".format(pool_id) if compare_nested_dict(setting_payload, existing_payload): module.exit_json(msg=NO_CHANGES_FOUND) else: setting_payload = get_updated_modify_payload(setting_payload, existing_payload) validate_modify_create_payload(setting_payload, module, action) if module.check_mode: module.exit_json(msg=CHANGES_FOUND, changed=True) resp = rest_obj.invoke_request(method, uri, data=setting_payload) msg = get_success_message(action, resp.json_data) return msg def pool_delete(module, rest_obj): try: pool_name = module.params["pool_name"] pool_id, existing_payload = get_identity_pool_id_by_name(pool_name, rest_obj) if not pool_id: message = "The identity pool '{0}' is not present in the system.".format(pool_name) module.exit_json(msg=message) method = "DELETE" uri = IDENTITY_URI + "({0})".format(pool_id) if module.check_mode: module.exit_json(msg=CHANGES_FOUND, changed=True) rest_obj.invoke_request(method, uri) return {"msg": "Successfully deleted the identity pool."} except Exception as err: raise err def get_success_message(action, resp_data): message = { "create": "Successfully created an identity pool.", "modify": "Successfully modified the identity pool." } return {"msg": message[action], "result": resp_data} def main(): settings_options = {"starting_mac_address": {"type": 'str'}, "identity_count": {"type": 'int'}} iscsi_specific_settings = {"starting_mac_address": {"type": 'str'}, "identity_count": {"type": 'int'}, "initiator_config": {"options": {"iqn_prefix": {"type": 'str'}}, "type": "dict"}, "initiator_ip_pool_settings": {"options": {"ip_range": {"type": 'str'}, "subnet_mask": {"type": 'str'}, "gateway": {"type": 'str'}, "primary_dns_server": {"type": 'str'}, "secondary_dns_server": {"type": 'str'}}, "type": "dict"}} fc_settings = {"starting_address": {"type": "str"}, "identity_count": {"type": "int"}} specs = { "state": {"type": "str", "required": False, "default": "present", "choices": ['present', 'absent']}, "pool_name": {"required": True, "type": "str"}, "new_pool_name": {"required": False, "type": "str"}, "pool_description": {"required": False, "type": "str"}, "ethernet_settings": {"required": False, "type": "dict", "options": settings_options}, "fcoe_settings": {"required": False, "type": "dict", "options": settings_options}, "iscsi_settings": {"required": False, "type": "dict", "options": iscsi_specific_settings}, "fc_settings": {"required": False, "type": "dict", "options": fc_settings}, } specs.update(ome_auth_params) module = AnsibleModule( argument_spec=specs, supports_check_mode=True ) try: with RestOME(module.params, req_session=True) as rest_obj: state = module.params["state"] if state == "present": message = pool_create_modify(module, rest_obj) module.exit_json(msg=message["msg"], pool_status=message["result"], changed=True) else: message = pool_delete(module, rest_obj) module.exit_json(msg=message["msg"], changed=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 (IOError, ValueError, SSLError, TypeError, ConnectionError, OSError) as err: module.fail_json(msg=str(err)) except Exception as err: module.fail_json(msg=str(err)) if __name__ == "__main__": main()