#!/usr/bin/python # Copyright: (c) 2020, Dell Technologies # Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) """Ansible module for managing host on Unity""" from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = r''' --- module: host version_added: '1.1.0' short_description: Manage Host operations on Unity description: - The Host module contains the operations Creation of a Host, Addition of initiators to Host, Removal of initiators from Host, Modification of host attributes, Get details of a Host, Deletion of a Host, Addition of network address to Host, Removal of network address from Host. extends_documentation_fragment: - dellemc.unity.unity author: - Rajshree Khare (@kharer5) options: host_name: description: - Name of the host. - Mandatory for host creation. type: str host_id: description: - Unique identifier of the host. - Host Id is auto generated during creation. - Except create, all other operations require either I(host_id) or Ihost_name). type: str description: description: - Host description. type: str host_os: description: - Operating system running on the host. choices: ['AIX', 'Citrix XenServer', 'HP-UX', 'IBM VIOS', 'Linux', 'Mac OS', 'Solaris', 'VMware ESXi', 'Windows Client', 'Windows Server'] type: str new_host_name: description: - New name for the host. - Only required in rename host operation. type: str initiators: description: - List of initiators to be added/removed to/from host. type: list elements: str initiator_state: description: - State of the initiator. choices: [present-in-host , absent-in-host] type: str network_address: description: - Network address to be added/removed to/from the host. - Enter valid IPV4 or host name. type: str network_address_state: description: - State of the Network address. choices: [present-in-host , absent-in-host] type: str state: description: - State of the host. choices: [present , absent] type: str required: true notes: - The I(check_mode) is not supported. ''' EXAMPLES = r''' - name: Create empty Host dellemc.unity.host: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" host_name: "ansible-test-host" host_os: "Linux" description: "ansible-test-host" state: "present" - name: Create Host with Initiators dellemc.unity.host: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" host_name: "ansible-test-host-1" host_os: "Linux" description: "ansible-test-host-1" initiators: - "iqn.1994-05.com.redhat:c38e6e8cfd81" - "20:00:00:90:FA:13:81:8D:10:00:00:90:FA:13:81:8D" initiator_state: "present-in-host" state: "present" - name: Modify Host using host_id dellemc.unity.host: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" host_id: "Host_253" new_host_name: "ansible-test-host-2" host_os: "Mac OS" description: "Ansible tesing purpose" state: "present" - name: Add Initiators to Host dellemc.unity.host: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" host_name: "ansible-test-host-2" initiators: - "20:00:00:90:FA:13:81:8C:10:00:00:90:FA:13:81:8C" initiator_state: "present-in-host" state: "present" - name: Get Host details using host_name dellemc.unity.host: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" host_name: "ansible-test-host-2" state: "present" - name: Get Host details using host_id dellemc.unity.host: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" host_id: "Host_253" state: "present" - name: Delete Host dellemc.unity.host: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" host_name: "ansible-test-host-2" state: "absent" - name: Add network address to Host dellemc.unity.host: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" host_name: "{{host_name}}" network_address: "192.168.1.2" network_address_state: "present-in-host" state: "present" - name: Delete network address from Host dellemc.unity.host: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" host_name: "{{host_name}}" network_address: "192.168.1.2" network_address_state: "absent-in-host" state: "present" ''' RETURN = r''' changed: description: Whether or not the resource has changed. returned: always type: bool sample: true host_details: description: Details of the host. returned: When host exists. type: dict contains: id: description: The system ID given to the host. type: str name: description: The name of the host. type: str description: description: Description about the host. type: str fc_host_initiators: description: Details of the FC initiators associated with the host. type: list contains: id: description: Unique identifier of the FC initiator path. type: str name: description: FC Qualified Name (WWN) of the initiator. type: str paths: description: Details of the paths associated with the FC initiator. type: list contains: id: description: Unique identifier of the path. type: str is_logged_in: description: Indicates whether the host initiator is logged into the storage system. type: bool iscsi_host_initiators: description: Details of the ISCSI initiators associated with the host. type: list contains: id: description: Unique identifier of the ISCSI initiator path. type: str name: description: ISCSI Qualified Name (IQN) of the initiator. type: str paths: description: Details of the paths associated with the ISCSI initiator. type: list contains: id: description: Unique identifier of the path. type: str is_logged_in: description: Indicates whether the host initiator is logged into the storage system. type: bool network_addresses: description: List of network addresses mapped to the host. type: list os_type: description: Operating system running on the host. type: str type: description: HostTypeEnum of the host. type: str host_luns: description: Details of luns attached to host. type: list sample: { "auto_manage_type": "HostManageEnum.UNKNOWN", "datastores": null, "description": "ansible-test-host-1", "existed": true, "fc_host_initiators": [ { "id": "HostInitiator_1", "name": "HostName_1", "paths": [ { "id": "HostInitiator_1_Id1", "is_logged_in": true }, { "id": "HostInitiator_1_Id2", "is_logged_in": true } ] } ], "hash": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", "health": { "UnityHealth": { "hash": 8764429420954 } }, "host_container": null, "host_luns": [], "host_polled_uuid": null, "host_pushed_uuid": null, "host_uuid": null, "host_v_vol_datastore": null, "id": "Host_2198", "iscsi_host_initiators": [ { "id": "HostInitiator_2", "name": "HostName_2", "paths": [ { "id": "HostInitiator_2_Id1", "is_logged_in": true }, { "id": "HostInitiator_2_Id2", "is_logged_in": true } ] } ], "last_poll_time": null, "name": "ansible-test-host-1", "network_addresses": [], "os_type": "Linux", "registration_type": null, "storage_resources": null, "tenant": null, "type": "HostTypeEnum.HOST_MANUAL", "vms": null } ''' from ansible.module_utils.basic import AnsibleModule from ansible_collections.dellemc.unity.plugins.module_utils.storage.dell \ import utils import ipaddress LOG = utils.get_logger('host') application_type = "Ansible/1.6.0" class Host(object): """Class with Host operations""" def __init__(self): """ Define all parameters required by this module""" self.module_params = utils.get_unity_management_host_parameters() self.module_params.update(get_host_parameters()) mutually_exclusive = [['host_name', 'host_id']] required_one_of = [['host_name', 'host_id']] required_together = [['network_address', 'network_address_state']] """ initialize the ansible module """ self.module = AnsibleModule(argument_spec=self.module_params, supports_check_mode=False, mutually_exclusive=mutually_exclusive, required_together=required_together, required_one_of=required_one_of) utils.ensure_required_libs(self.module) self.unity = utils.get_unity_unisphere_connection(self.module.params, application_type) LOG.info('Got the unity instance for provisioning on Unity') def get_host_count(self, host_name): """ To get the count of hosts with same host_name """ hosts = [] host_count = 0 hosts = utils.host.UnityHostList.get(cli=self.unity._cli, name=host_name) host_count = len(hosts) return host_count def get_host_details(self, host_id=None, host_name=None): """ Get details of a given host """ host_id_or_name = host_id if host_id else host_name try: LOG.info("Getting host %s details", host_id_or_name) if host_id: host_details = self.unity.get_host(_id=host_id) if host_details.name is None: return None if host_name: ''' get the count of hosts with same host_name ''' host_count = self.get_host_count(host_name) if host_count < 1: return None elif host_count > 1: error_message = "Duplicate hosts found: There are "\ + host_count + " hosts(s) with the same" \ " host_name: " + host_name LOG.error(error_message) self.module.fail_json(msg=error_message) else: host_details = self.unity.get_host(name=host_name) return host_details except utils.HttpError as e: if e.http_status == 401: msg = 'Incorrect username or password provided.' LOG.error(msg) self.module.fail_json(msg=msg) else: msg = "Got HTTP Connection Error while getting host " \ "details %s : Error %s " % (host_id_or_name, str(e)) LOG.error(msg) self.module.fail_json(msg=msg) except utils.UnityResourceNotFoundError as e: error_message = "Failed to get details of host " \ "{0} with error {1}".format(host_id_or_name, str(e)) LOG.error(error_message) return None except Exception as e: error_message = "Got error %s while getting details of host %s" \ % (str(e), host_id_or_name) LOG.error(error_message) self.module.fail_json(msg=error_message) def create_host(self, host_name): """ Create a new host """ try: description = self.module.params['description'] host_os = self.module.params['host_os'] host_type = utils.HostTypeEnum.HOST_MANUAL initiators = self.module.params['initiators'] initiator_state = self.module.params['initiator_state'] empty_initiators_flag = False if (initiators and initiator_state == 'absent-in-host'): error_message = "Incorrect 'initiator_state' given." LOG.error(error_message) self.module.fail_json(msg=error_message) if (initiators is None or len(initiators) == 0 or not initiator_state or initiator_state == 'absent-in-host'): empty_initiators_flag = True """ if any of the Initiators is invalid or already mapped """ if (initiators and initiator_state == 'present-in-host'): unmapped_initiators \ = self.get_list_unmapped_initiators(initiators) if unmapped_initiators is None \ or len(unmapped_initiators) < len(initiators): error_message = "Provide valid initiators." LOG.error(error_message) self.module.fail_json(msg=error_message) if not empty_initiators_flag: self.validate_initiators(initiators) LOG.info("Creating empty host %s ", host_name) new_host = utils.host.UnityHost.create(self.unity._cli, name=host_name, desc=description, os=host_os, host_type=host_type) if not empty_initiators_flag: host_details = self.unity.get_host(name=host_name) LOG.info("Adding initiators to %s host", host_name) result, new_host \ = self.add_initiator_to_host(host_details, initiators) return True, new_host except Exception as e: error_message = "Got error %s while creation of host %s" \ % (str(e), host_name) LOG.error(error_message) self.module.fail_json(msg=error_message) def validate_initiators(self, initiators): results = [] for item in initiators: results.append(utils.is_initiator_valid(item)) if False in results: error_message = "One or more initiator provided is not valid, please provide valid initiators" LOG.error(error_message) self.module.fail_json(msg=error_message) def get_host_initiators_list(self, host_details): """ Get the list of existing initiators in host""" existing_initiators = [] if host_details.fc_host_initiators is not None: fc_len = len(host_details.fc_host_initiators) if fc_len > 0: for count in range(fc_len): """ get initiator 'wwn' id""" ini_id \ = host_details.fc_host_initiators.initiator_id[count] """ update existing_initiators list with 'wwn' """ existing_initiators.append(ini_id) if host_details.iscsi_host_initiators is not None: iscsi_len = len(host_details.iscsi_host_initiators) if iscsi_len > 0: for count in range(iscsi_len): """ get initiator 'iqn' id""" ini_id \ = host_details.iscsi_host_initiators.\ initiator_id[count] """ update existing_initiators list with 'iqn' """ existing_initiators.append(ini_id) return existing_initiators def is_host_modified(self, host_details): """ Determines whether the Host details are to be updated or not """ LOG.info("Checking host attribute values.") modified_flag = False if (self.module.params['description'] is not None and self.module.params['description'] != host_details.description) \ or (self.module.params['host_os'] is not None and self.module.params['host_os'] != host_details.os_type) \ or (self.module.params['new_host_name'] is not None and self.module.params[ 'new_host_name'] != host_details.name) \ or (self.module.params['initiators'] is not None and self.module.params['initiators'] != self.get_host_initiators_list(host_details)): LOG.info("Modification required.") modified_flag = True return modified_flag def modify_host(self, host_details, new_host_name=None, description=None, host_os=None): """ Modify a host """ try: hosts = utils.host.UnityHostList.get(cli=self.unity._cli) host_names_list = hosts.name for name in host_names_list: if new_host_name == name: error_message = "Cannot modify name, new_host_name: " \ + new_host_name + " already in use." LOG.error(error_message) self.module.fail_json(msg=error_message) host_details.modify(name=new_host_name, desc=description, os=host_os) return True except Exception as e: error_message = "Got error %s while modifying host %s" \ % (str(e), host_details.name) LOG.error(error_message) self.module.fail_json(msg=error_message) def get_list_unmapped_initiators(self, initiators, host_id=None): """ Get the list of those initiators which are not mapped to any host""" unmapped_initiators = [] for id in initiators: initiator_details = utils.host.UnityHostInitiatorList \ .get(cli=self.unity._cli, initiator_id=id) \ ._get_properties() """ if an already existing initiator is passed along with an unmapped initiator""" if None in initiator_details["parent_host"]: unmapped_initiators.append(initiator_details ["initiator_id"][0]) elif not initiator_details["parent_host"]: unmapped_initiators.append(id) else: error_message = "Initiator " + id + " mapped to another Host." LOG.error(error_message) self.module.fail_json(msg=error_message) return unmapped_initiators def add_initiator_to_host(self, host_details, initiators): """ Add initiator to host """ try: existing_initiators = self.get_host_initiators_list(host_details) """ if current and exisitng initiators are same""" if initiators \ and (set(initiators).issubset(set(existing_initiators))): LOG.info("Initiators are already present in host: %s", host_details.name) return False, host_details """ get the list of non-mapped initiators out of the given initiators""" host_id = host_details.id unmapped_initiators \ = self.get_list_unmapped_initiators(initiators, host_id) """ if any of the Initiators is invalid or already mapped """ if unmapped_initiators is None \ or len(unmapped_initiators) < len(initiators): error_message = "Provide valid initiators." LOG.error(error_message) self.module.fail_json(msg=error_message) LOG.info("Adding initiators to host %s", host_details.name) for id in unmapped_initiators: host_details.add_initiator(uid=id) updated_host \ = self.unity.get_host(name=host_details.name) return True, updated_host except Exception as e: error_message = "Got error %s while adding initiator to host %s" \ % (str(e), host_details.name) LOG.error(error_message) self.module.fail_json(msg=error_message) def remove_initiator_from_host(self, host_details, initiators): """ Remove initiator from host """ try: existing_initiators = self.get_host_initiators_list(host_details) if existing_initiators is None: LOG.info("No exisiting initiators in host: %s", host_details.name) return False, host_details if not (set(initiators).issubset(set(existing_initiators))): LOG.info("Initiators already absent in host: %s", host_details.name) return False, host_details LOG.info("Removing initiators from host %s", host_details.name) if len(initiators) > 1: self.check_if_initiators_logged_in(initiators) for id in initiators: initiator_details = utils.host.UnityHostInitiatorList \ .get(cli=self.unity._cli, initiator_id=id) \ ._get_properties() """ if initiator has no active paths, then remove it """ if initiator_details["paths"][0] is None: LOG.info("Initiator Path does not exist.") host_details.delete_initiator(uid=id) updated_host \ = self.unity.get_host(name=host_details.name) else: """ Checking for initiator logged_in state """ for path in initiator_details["paths"][0]["UnityHostInitiatorPathList"]: path_id = path["UnityHostInitiatorPath"]["id"] path_id_obj = utils.host.UnityHostInitiatorPathList \ .get(cli=self.unity._cli, _id=path_id) path_id_details = path_id_obj._get_properties() """ if is_logged_in is True, can't remove initiator""" if (path_id_details["is_logged_in"]): error_message = "Cannot remove initiator "\ + id + ", as it is logged in " \ "the with host." LOG.error(error_message) self.module.fail_json(msg=error_message) elif (not path_id_details["is_logged_in"]): """ if is_logged_in is False, remove initiator """ path_id_obj.delete() else: """ if logged_in state does not exist """ error_message = " logged_in state does not " \ "exist for initiator " + id + "." LOG.error(error_message) self.module.fail_json(msg=error_message) host_details.delete_initiator(uid=id) updated_host \ = self.unity.get_host(name=host_details.name) return True, updated_host except Exception as e: error_message = "Got error %s while removing initiator from " \ "host %s" \ % (str(e), host_details.name) LOG.error(error_message) self.module.fail_json(msg=error_message) def check_if_initiators_logged_in(self, initiators): """ Checks if any of the initiators is of type logged-in""" for item in initiators: initiator_details = (utils.host.UnityHostInitiatorList .get(cli=self.unity._cli, initiator_id=item) ._get_properties()) if initiator_details["paths"][0] is not None and "UnityHostInitiatorPathList" in initiator_details["paths"][0]: error_message = "Removal operation cannot be done since host has logged in initiator(s)" LOG.error(error_message) self.module.fail_json(msg=error_message) def delete_host(self, host_details): """ Delete an existing host """ try: host_details.delete() return True except Exception as e: error_message = "Got error %s while deletion of host %s" \ % (str(e), host_details.name) LOG.error(error_message) self.module.fail_json(msg=error_message) def get_iscsi_host_initiators_details(self, iscsi_host_initiators): """ Get the details of existing ISCSI initiators in host""" iscsi_initiator_list = [] for iscsi in iscsi_host_initiators: iscsi_initiator_details = self.unity.get_initiator(_id=iscsi.id) iscsi_path_list = [] if iscsi_initiator_details.paths is not None: for path in iscsi_initiator_details.paths: iscsi_path_list.append({ 'id': path.id, 'is_logged_in': path.is_logged_in }) iscsi_initiator_list.append({ 'id': iscsi_initiator_details.id, 'name': iscsi_initiator_details.initiator_id, 'paths': iscsi_path_list }) return iscsi_initiator_list def get_host_network_address_list(self, host_details): network_address_list = [] if host_details and host_details.host_ip_ports is not None: for port in host_details.host_ip_ports: network_address_list.append(port.address) return network_address_list def manage_network_address(self, host_details, network_address_list, network_address, network_address_state): try: is_mapped = False changed = False for addr in network_address_list: if addr.lower() == network_address.lower(): is_mapped = True break if not is_mapped and network_address_state == 'present-in-host': LOG.info("Adding network address %s to Host %s", network_address, host_details.name) host_details.add_ip_port(network_address) changed = True elif is_mapped and network_address_state == 'absent-in-host': LOG.info("Deleting network address %s from Host %s", network_address, host_details.name) host_details.delete_ip_port(network_address) changed = True if changed: updated_host = self.unity.get_host(name=host_details.name) network_address_list = self.get_host_network_address_list(updated_host) return network_address_list, changed except Exception as e: error_message = "Got error %s while modifying network address %s of host %s" \ % (str(e), network_address, host_details.name) LOG.error(error_message) self.module.fail_json(msg=error_message) def get_host_lun_list(self, host_details): """ Get luns attached to host""" host_luns_list = [] if host_details and host_details.host_luns is not None: for lun in host_details.host_luns.lun: host_lun = {"name": lun.name, "id": lun.id} host_luns_list.append(host_lun) return host_luns_list def get_fc_host_initiators_details(self, fc_host_initiators): """ Get the details of existing FC initiators in host""" fc_initiator_list = [] for fc in fc_host_initiators: fc_initiator_details = self.unity.get_initiator(_id=fc.id) fc_path_list = [] if fc_initiator_details.paths is not None: for path in fc_initiator_details.paths: fc_path_list.append({ 'id': path.id, 'is_logged_in': path.is_logged_in }) fc_initiator_list.append({ 'id': fc_initiator_details.id, 'name': fc_initiator_details.initiator_id, 'paths': fc_path_list }) return fc_initiator_list def perform_module_operation(self): """ Perform different actions on host based on user parameter chosen in playbook """ host_name = self.module.params['host_name'] host_id = self.module.params['host_id'] description = self.module.params['description'] host_os = self.module.params['host_os'] new_host_name = self.module.params['new_host_name'] initiator_state = self.module.params['initiator_state'] initiators = self.module.params['initiators'] network_address = self.module.params['network_address'] network_address_state = self.module.params['network_address_state'] state = self.module.params['state'] if host_name and len(host_name) > 255: err_msg = "'host_name' is greater than 255 characters." LOG.error(err_msg) self.module.fail_json(msg=err_msg) if new_host_name and len(new_host_name) > 255: err_msg = "'new_host_name' is greater than 255 characters." LOG.error(err_msg) self.module.fail_json(msg=err_msg) if description and len(description) > 255: err_msg = "'description' is greater than 255 characters." LOG.error(err_msg) self.module.fail_json(msg=err_msg) if not initiators and initiator_state: err_msg = "'initiator_state' is given, " \ "'initiators' are not specified" LOG.error(err_msg) self.module.fail_json(msg=err_msg) if not initiator_state and initiators: err_msg = "'initiators' are given, " \ "'initiator_state' is not specified" LOG.error(err_msg) self.module.fail_json(msg=err_msg) # result is a dictionary that contains changed status and # host details result = dict( changed=False, host_details={} ) ''' Get host details based on host_name/host_id''' host_details = self.get_host_details(host_id, host_name) if not host_details and state == 'present': if host_id: err_msg = "Invalid argument 'host_id' while " \ "creating a host" LOG.error(err_msg) self.module.fail_json(msg=err_msg) if not host_name: err_msg = "host_name is required to create a host" LOG.error(err_msg) self.module.fail_json(msg=err_msg) if new_host_name: err_msg = "Invalid argument 'new_host_name' while " \ "creating a host" LOG.error(err_msg) self.module.fail_json(msg=err_msg) if (initiators and initiator_state == 'absent-in-host'): error_message = "Incorrect 'initiator_state' given." LOG.error(error_message) self.module.fail_json(msg=error_message) # Create new host LOG.info("Creating host: %s", host_name) result['changed'], host_details \ = self.create_host(host_name) result['host_details'] = host_details._get_properties() # Modify host (Attributes and ADD/REMOVE Initiators) elif (state == 'present' and host_details): modified_flag = self.is_host_modified(host_details) if modified_flag: # Modify host result['changed'] = self.modify_host(host_details, new_host_name, description, host_os) if new_host_name: host_details = self.get_host_details(host_id, new_host_name) else: host_details = self.get_host_details(host_id, host_name) result['host_details'] = host_details._get_properties() # Add Initiators to host if (initiator_state == 'present-in-host' and initiators and len(initiators) > 0): LOG.info("Adding Initiators to Host %s", host_details.name) result['changed'], host_details \ = self.add_initiator_to_host(host_details, initiators) result['host_details'] = host_details._get_properties() else: LOG.info('Host modification is not applicable, ' 'as none of the attributes has changed.') result['changed'] = False result['host_details'] = host_details._get_properties() # Remove initiators from host if (host_details and initiator_state == 'absent-in-host' and initiators and len(initiators) > 0): LOG.info("Removing Initiators from Host %s", host_details.name) result['changed'], host_details \ = self.remove_initiator_from_host(host_details, initiators) result['host_details'] = host_details._get_properties() """ display WWN/IQN w.r.t. initiators mapped to host, if host exists """ if host_details and host_details.fc_host_initiators is not None: host_details.fc_host_initiators = self.get_fc_host_initiators_details(host_details.fc_host_initiators) result['host_details'] = host_details._get_properties() if host_details and host_details.iscsi_host_initiators is not None: host_details.iscsi_host_initiators = self.get_iscsi_host_initiators_details(host_details.iscsi_host_initiators) result['host_details'] = host_details._get_properties() ''' Get host luns details and network addresses''' if result['host_details']: result['host_details']['host_luns'] = self.get_host_lun_list(host_details) result['host_details']['network_addresses'] = self.get_host_network_address_list(host_details) if 'host_ip_ports' in result['host_details']: del result['host_details']['host_ip_ports'] # manage network address if host_details is not None and network_address_state is not None: self.validate_network_address_params(network_address) network_address_list, changed = self.manage_network_address( host_details, result['host_details']['network_addresses'], network_address, network_address_state) result['host_details']['network_addresses'] = network_address_list result['changed'] = changed # Delete a host if state == 'absent': if host_details: LOG.info("Deleting host %s", host_details.name) result['changed'] = self.delete_host(host_details) else: result['changed'] = False result['host_details'] = [] self.module.exit_json(**result) def validate_network_address_params(self, network_address): if '.' in network_address and not is_valid_ip(network_address): err_msg = 'Please enter valid IPV4 address for network address' LOG.error(err_msg) self.module.fail_json(msg=err_msg) if len(network_address) < 1 or len(network_address) > 63: err_msg = "'network_address' should be in range of 1 to 63 characters." LOG.error(err_msg) self.module.fail_json(msg=err_msg) if utils.has_special_char(network_address) or ' ' in network_address: err_msg = 'Please enter valid IPV4 address or host name for network address' LOG.error(err_msg) self.module.fail_json(msg=err_msg) def is_valid_ip(address): try: ipaddress.ip_address(address) return True except ValueError: return False def get_host_parameters(): """This method provides parameters required for the ansible host module on Unity""" return dict( host_name=dict(required=False, type='str'), host_id=dict(required=False, type='str'), description=dict(required=False, type='str'), host_os=dict(required=False, type='str', choices=['AIX', 'Citrix XenServer', 'HP-UX', 'IBM VIOS', 'Linux', 'Mac OS', 'Solaris', 'VMware ESXi', 'Windows Client', 'Windows Server']), new_host_name=dict(required=False, type='str'), initiators=dict(required=False, type='list', elements='str'), initiator_state=dict(required=False, type='str', choices=['present-in-host', 'absent-in-host']), network_address=dict(required=False, type='str'), network_address_state=dict(required=False, type='str', choices=['present-in-host', 'absent-in-host']), state=dict(required=True, type='str', choices=['present', 'absent']) ) def main(): """ Create Unity host object and perform action on it based on user input from playbook""" obj = Host() obj.perform_module_operation() if __name__ == '__main__': main()