diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 12:04:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 12:04:41 +0000 |
commit | 975f66f2eebe9dadba04f275774d4ab83f74cf25 (patch) | |
tree | 89bd26a93aaae6a25749145b7e4bca4a1e75b2be /ansible_collections/dellemc/unity/plugins/modules/host.py | |
parent | Initial commit. (diff) | |
download | ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.tar.xz ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.zip |
Adding upstream version 7.7.0+dfsg.upstream/7.7.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/dellemc/unity/plugins/modules/host.py')
-rw-r--r-- | ansible_collections/dellemc/unity/plugins/modules/host.py | 1026 |
1 files changed, 1026 insertions, 0 deletions
diff --git a/ansible_collections/dellemc/unity/plugins/modules/host.py b/ansible_collections/dellemc/unity/plugins/modules/host.py new file mode 100644 index 000000000..21a5fbae1 --- /dev/null +++ b/ansible_collections/dellemc/unity/plugins/modules/host.py @@ -0,0 +1,1026 @@ +#!/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) <ansible.team@dell.com> + +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() |