summaryrefslogtreecommitdiffstats
path: root/ansible_collections/dellemc/unity/plugins/modules/host.py
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
commit975f66f2eebe9dadba04f275774d4ab83f74cf25 (patch)
tree89bd26a93aaae6a25749145b7e4bca4a1e75b2be /ansible_collections/dellemc/unity/plugins/modules/host.py
parentInitial commit. (diff)
downloadansible-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.py1026
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()