2017-06-16 10:28:50 +00:00
|
|
|
#!/usr/bin/python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
# (c) 2017, Simon Dodsley (simon@purestorage.com)
|
Remove wildcard imports
Made the following changes:
* Removed wildcard imports
* Replaced long form of GPL header with short form
* Removed get_exception usage
* Added from __future__ boilerplate
* Adjust division operator to // where necessary
For the following files:
* web_infrastructure modules
* system modules
* linode, lxc, lxd, atomic, cloudscale, dimensiondata, ovh, packet,
profitbricks, pubnub, smartos, softlayer, univention modules
* compat dirs (disabled as its used intentionally)
2017-07-28 05:55:24 +00:00
|
|
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
|
|
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
|
|
__metaclass__ = type
|
|
|
|
|
2017-08-16 03:16:38 +00:00
|
|
|
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
2017-06-16 10:28:50 +00:00
|
|
|
'status': ['preview'],
|
|
|
|
'supported_by': 'community'}
|
|
|
|
|
2017-08-22 00:25:13 +00:00
|
|
|
DOCUMENTATION = r'''
|
2017-06-16 10:28:50 +00:00
|
|
|
---
|
|
|
|
module: purefa_host
|
2017-08-22 00:25:13 +00:00
|
|
|
version_added: '2.4'
|
|
|
|
short_description: Manage hosts on Pure Storage FlashArrays
|
2017-06-16 10:28:50 +00:00
|
|
|
description:
|
2017-08-22 00:25:13 +00:00
|
|
|
- Create, delete or modify hosts on Pure Storage FlashArrays.
|
|
|
|
author:
|
|
|
|
- Simon Dodsley (@sdodsley)
|
2018-12-07 14:42:59 +00:00
|
|
|
notes:
|
|
|
|
- If specifying C(lun) option ensure host support requested value
|
2017-06-16 10:28:50 +00:00
|
|
|
options:
|
|
|
|
host:
|
|
|
|
description:
|
2017-08-22 00:25:13 +00:00
|
|
|
- The name of the host.
|
2017-06-16 10:28:50 +00:00
|
|
|
required: true
|
|
|
|
state:
|
|
|
|
description:
|
2017-08-22 00:25:13 +00:00
|
|
|
- Define whether the host should exist or not.
|
|
|
|
- When removing host all connected volumes will be disconnected.
|
2017-06-16 10:28:50 +00:00
|
|
|
default: present
|
2017-08-22 00:25:13 +00:00
|
|
|
choices: [ absent, present ]
|
2017-06-16 10:28:50 +00:00
|
|
|
protocol:
|
|
|
|
description:
|
2017-08-22 00:25:13 +00:00
|
|
|
- Defines the host connection protocol for volumes.
|
2017-06-16 10:28:50 +00:00
|
|
|
default: iscsi
|
2018-12-07 14:42:59 +00:00
|
|
|
choices: [ fc, iscsi, nvmef, mixed ]
|
2017-06-16 10:28:50 +00:00
|
|
|
wwns:
|
|
|
|
description:
|
2018-04-10 20:50:43 +00:00
|
|
|
- List of wwns of the host if protocol is fc or mixed.
|
2017-06-16 10:28:50 +00:00
|
|
|
iqn:
|
|
|
|
description:
|
2018-04-10 20:50:43 +00:00
|
|
|
- List of IQNs of the host if protocol is iscsi or mixed.
|
2018-12-07 14:42:59 +00:00
|
|
|
nqn:
|
|
|
|
description:
|
|
|
|
- List of NQNs of the host if protocol is nvmef or mixed.
|
|
|
|
version_added: '2.8'
|
2017-06-16 10:28:50 +00:00
|
|
|
volume:
|
|
|
|
description:
|
2017-08-22 00:25:13 +00:00
|
|
|
- Volume name to map to the host.
|
2018-12-07 14:42:59 +00:00
|
|
|
lun:
|
|
|
|
description:
|
|
|
|
- LUN ID to assign to volume for host. Must be unique.
|
|
|
|
- If not provided the ID will be automatically assigned.
|
|
|
|
- Range for LUN ID is 1 to 4095.
|
|
|
|
version_added: '2.8'
|
2018-08-04 05:48:49 +00:00
|
|
|
personality:
|
|
|
|
description:
|
2018-12-07 14:42:59 +00:00
|
|
|
- Define which operating system the host is. Recommend for
|
|
|
|
ActiveCluster integration.
|
|
|
|
default: ''
|
|
|
|
choices: ['hpux', 'vms', 'aix', 'esxi', 'solaris', 'hitachi-vsp', 'oracle-vm-server', 'delete', '']
|
2018-08-04 05:48:49 +00:00
|
|
|
version_added: '2.7'
|
2017-06-16 10:28:50 +00:00
|
|
|
extends_documentation_fragment:
|
2018-02-22 20:33:48 +00:00
|
|
|
- purestorage.fa
|
2017-06-16 10:28:50 +00:00
|
|
|
'''
|
|
|
|
|
2017-08-22 00:25:13 +00:00
|
|
|
EXAMPLES = r'''
|
2018-08-04 05:48:49 +00:00
|
|
|
- name: Create new AIX host
|
2017-06-16 10:28:50 +00:00
|
|
|
purefa_host:
|
|
|
|
host: foo
|
2018-08-04 05:48:49 +00:00
|
|
|
personaility: aix
|
2017-06-16 10:28:50 +00:00
|
|
|
fa_url: 10.10.10.2
|
|
|
|
api_token: e31060a7-21fc-e277-6240-25983c6c4592
|
|
|
|
|
|
|
|
- name: Delete host
|
|
|
|
purefa_host:
|
|
|
|
host: foo
|
|
|
|
fa_url: 10.10.10.2
|
|
|
|
api_token: e31060a7-21fc-e277-6240-25983c6c4592
|
2017-08-22 00:25:13 +00:00
|
|
|
state: absent
|
2017-06-16 10:28:50 +00:00
|
|
|
|
2018-04-10 20:50:43 +00:00
|
|
|
- name: Make host bar with wwn ports
|
2017-06-16 10:28:50 +00:00
|
|
|
purefa_host:
|
|
|
|
host: bar
|
|
|
|
protocol: fc
|
|
|
|
wwns:
|
2017-08-22 00:25:13 +00:00
|
|
|
- 00:00:00:00:00:00:00
|
|
|
|
- 11:11:11:11:11:11:11
|
2017-06-16 10:28:50 +00:00
|
|
|
fa_url: 10.10.10.2
|
|
|
|
api_token: e31060a7-21fc-e277-6240-25983c6c4592
|
|
|
|
|
2018-04-10 20:50:43 +00:00
|
|
|
- name: Make host bar with iSCSI ports
|
2017-06-16 10:28:50 +00:00
|
|
|
purefa_host:
|
|
|
|
host: bar
|
|
|
|
protocol: iscsi
|
|
|
|
iqn:
|
2017-08-22 00:25:13 +00:00
|
|
|
- iqn.1994-05.com.redhat:7d366003913
|
2017-06-16 10:28:50 +00:00
|
|
|
fa_url: 10.10.10.2
|
|
|
|
api_token: e31060a7-21fc-e277-6240-25983c6c4592
|
|
|
|
|
2018-12-07 14:42:59 +00:00
|
|
|
- name: Make host bar with NVMeF ports
|
|
|
|
purefa_host:
|
|
|
|
host: bar
|
|
|
|
protocol: nvmef
|
|
|
|
nqn:
|
|
|
|
- nqn.2014-08.com.vendor:nvme:nvm-subsystem-sn-d78432
|
|
|
|
fa_url: 10.10.10.2
|
|
|
|
api_token: e31060a7-21fc-e277-6240-25983c6c4592
|
|
|
|
|
2018-04-10 20:50:43 +00:00
|
|
|
- name: Make mixed protocol host
|
|
|
|
purefa_host:
|
|
|
|
host: bar
|
|
|
|
protocol: mixed
|
2018-12-07 14:42:59 +00:00
|
|
|
nqn:
|
|
|
|
- nqn.2014-08.com.vendor:nvme:nvm-subsystem-sn-d78432
|
2018-04-10 20:50:43 +00:00
|
|
|
iqn:
|
|
|
|
- iqn.1994-05.com.redhat:7d366003914
|
|
|
|
wwns:
|
|
|
|
- 00:00:00:00:00:00:01
|
|
|
|
- 11:11:11:11:11:11:12
|
|
|
|
fa_url: 10.10.10.2
|
|
|
|
api_token: e31060a7-21fc-e277-6240-25983c6c4592
|
|
|
|
|
2018-12-07 14:42:59 +00:00
|
|
|
- name: Map host foo to volume bar as LUN ID 12
|
2017-06-16 10:28:50 +00:00
|
|
|
purefa_host:
|
|
|
|
host: foo
|
|
|
|
volume: bar
|
2018-12-07 14:42:59 +00:00
|
|
|
lun: 12
|
2017-06-16 10:28:50 +00:00
|
|
|
fa_url: 10.10.10.2
|
|
|
|
api_token: e31060a7-21fc-e277-6240-25983c6c4592
|
|
|
|
'''
|
|
|
|
|
2017-08-22 00:25:13 +00:00
|
|
|
RETURN = r'''
|
2017-06-16 10:28:50 +00:00
|
|
|
'''
|
|
|
|
|
|
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
|
|
from ansible.module_utils.pure import get_system, purefa_argument_spec
|
|
|
|
|
|
|
|
|
2018-08-04 05:48:49 +00:00
|
|
|
AC_REQUIRED_API_VERSION = '1.14'
|
2018-12-07 14:42:59 +00:00
|
|
|
NVMEF_API_VERSION = '1.16'
|
2018-08-04 05:48:49 +00:00
|
|
|
|
|
|
|
|
2017-06-16 10:28:50 +00:00
|
|
|
try:
|
|
|
|
from purestorage import purestorage
|
2017-08-22 00:25:13 +00:00
|
|
|
HAS_PURESTORAGE = True
|
2017-06-16 10:28:50 +00:00
|
|
|
except ImportError:
|
|
|
|
HAS_PURESTORAGE = False
|
|
|
|
|
|
|
|
|
2018-04-10 20:50:43 +00:00
|
|
|
def _set_host_initiators(module, array):
|
2018-12-07 14:42:59 +00:00
|
|
|
"""Set host initiators."""
|
|
|
|
if module.params['protocol'] in ['nvmef', 'mixed']:
|
|
|
|
if module.params['nqn']:
|
|
|
|
try:
|
|
|
|
array.set_host(module.params['host'],
|
|
|
|
nqnlist=module.params['nqn'])
|
2018-09-08 00:59:46 +00:00
|
|
|
except Exception:
|
2018-12-07 14:42:59 +00:00
|
|
|
module.fail_json(msg='Setting of NVMeF NQN failed.')
|
2018-04-10 20:50:43 +00:00
|
|
|
if module.params['protocol'] in ['iscsi', 'mixed']:
|
|
|
|
if module.params['iqn']:
|
2018-12-07 14:42:59 +00:00
|
|
|
try:
|
|
|
|
array.set_host(module.params['host'],
|
|
|
|
iqnlist=module.params['iqn'])
|
2018-09-08 00:59:46 +00:00
|
|
|
except Exception:
|
2018-12-07 14:42:59 +00:00
|
|
|
module.fail_json(msg='Setting of iSCSI IQN failed.')
|
2018-04-10 20:50:43 +00:00
|
|
|
if module.params['protocol'] in ['fc', 'mixed']:
|
|
|
|
if module.params['wwns']:
|
2018-12-07 14:42:59 +00:00
|
|
|
try:
|
|
|
|
array.set_host(module.params['host'],
|
|
|
|
wwnlist=module.params['wwns'])
|
2018-09-08 00:59:46 +00:00
|
|
|
except Exception:
|
2018-12-07 14:42:59 +00:00
|
|
|
module.fail_json(msg='Setting of FC WWNs failed.')
|
|
|
|
|
|
|
|
|
|
|
|
def _update_host_initiators(module, array):
|
|
|
|
"""Change host initiator if iscsi or nvmef or add new FC WWNs"""
|
|
|
|
if module.params['protocol'] in ['nvmef', 'mixed']:
|
|
|
|
if module.params['nqn']:
|
|
|
|
try:
|
|
|
|
array.set_host(module.params['host'],
|
|
|
|
nqnlist=module.params['nqn'])
|
2018-09-08 00:59:46 +00:00
|
|
|
except Exception:
|
2018-12-07 14:42:59 +00:00
|
|
|
module.fail_json(msg='Change of NVMeF NQN failed.')
|
|
|
|
if module.params['protocol'] in ['iscsi', 'mixed']:
|
|
|
|
if module.params['iqn']:
|
|
|
|
try:
|
|
|
|
array.set_host(module.params['host'],
|
|
|
|
iqnlist=module.params['iqn'])
|
2018-09-08 00:59:46 +00:00
|
|
|
except Exception:
|
2018-12-07 14:42:59 +00:00
|
|
|
module.fail_json(msg='Change of iSCSI IQN failed.')
|
|
|
|
if module.params['protocol'] in ['fc', 'mixed']:
|
|
|
|
if module.params['wwns']:
|
|
|
|
try:
|
|
|
|
array.set_host(module.params['host'],
|
|
|
|
addwwnlist=module.params['wwns'])
|
2018-09-08 00:59:46 +00:00
|
|
|
except Exception:
|
2018-12-07 14:42:59 +00:00
|
|
|
module.fail_json(msg='FC WWN additiona failed.')
|
|
|
|
|
|
|
|
|
|
|
|
def _connect_new_volume(module, array, answer=False):
|
|
|
|
"""Connect volume to host"""
|
|
|
|
api_version = array._list_available_rest_versions()
|
|
|
|
if AC_REQUIRED_API_VERSION in api_version and module.params['lun']:
|
|
|
|
try:
|
|
|
|
array.connect_host(module.params['host'],
|
|
|
|
module.params['volume'],
|
|
|
|
lun=module.params['lun'])
|
|
|
|
answer = True
|
2018-09-08 00:59:46 +00:00
|
|
|
except Exception:
|
2018-12-07 14:42:59 +00:00
|
|
|
module.fail_json(msg='LUN ID {0} invalid. Check for duplicate LUN IDs.'.format(module.params['lun']))
|
|
|
|
else:
|
|
|
|
array.connect_host(module.params['host'], module.params['volume'])
|
|
|
|
answer = True
|
|
|
|
return answer
|
2018-08-04 05:48:49 +00:00
|
|
|
|
|
|
|
|
|
|
|
def _set_host_personality(module, array):
|
2018-12-07 14:42:59 +00:00
|
|
|
"""Set host personality. Only called when supported"""
|
|
|
|
if module.params['personality'] != 'delete':
|
|
|
|
array.set_host(module.params['host'],
|
|
|
|
personality=module.params['personality'])
|
|
|
|
else:
|
|
|
|
array.set_host(module.params['host'], personality='')
|
|
|
|
|
|
|
|
|
|
|
|
def _update_host_personality(module, array, answer=False):
|
|
|
|
"""Change host personality. Only called when supported"""
|
|
|
|
personality = array.get_host(module.params['host'], personality=True)['personality']
|
|
|
|
if personality is None and module.params['personality'] != 'delete':
|
|
|
|
array.set_host(module.params['host'],
|
|
|
|
personality=module.params['personality'])
|
|
|
|
answer = True
|
|
|
|
if personality is not None:
|
|
|
|
if module.params['personality'] == 'delete':
|
|
|
|
array.set_host(module.params['host'], personality='')
|
|
|
|
answer = True
|
|
|
|
elif personality != module.params['personality']:
|
|
|
|
array.set_host(module.params['host'],
|
|
|
|
personality=module.params['personality'])
|
|
|
|
answer = True
|
|
|
|
return answer
|
2018-04-10 20:50:43 +00:00
|
|
|
|
|
|
|
|
2017-06-16 10:28:50 +00:00
|
|
|
def get_host(module, array):
|
|
|
|
host = None
|
2018-08-04 05:48:49 +00:00
|
|
|
for hst in array.list_hosts():
|
|
|
|
if hst["name"] == module.params['host']:
|
|
|
|
host = hst
|
2017-06-16 10:28:50 +00:00
|
|
|
break
|
|
|
|
return host
|
|
|
|
|
|
|
|
|
|
|
|
def make_host(module, array):
|
2018-12-07 14:42:59 +00:00
|
|
|
changed = False
|
2018-08-04 05:48:49 +00:00
|
|
|
try:
|
|
|
|
array.create_host(module.params['host'])
|
2018-12-07 14:42:59 +00:00
|
|
|
changed = True
|
2018-09-08 00:59:46 +00:00
|
|
|
except Exception:
|
2018-12-07 14:42:59 +00:00
|
|
|
module.fail_json(msg='Host {0} creation failed.'.format(module.params['host']))
|
|
|
|
try:
|
2018-08-04 05:48:49 +00:00
|
|
|
_set_host_initiators(module, array)
|
|
|
|
api_version = array._list_available_rest_versions()
|
2018-12-07 14:42:59 +00:00
|
|
|
if AC_REQUIRED_API_VERSION in api_version and module.params['personality']:
|
2018-08-04 05:48:49 +00:00
|
|
|
_set_host_personality(module, array)
|
|
|
|
if module.params['volume']:
|
2018-12-07 14:42:59 +00:00
|
|
|
if module.params['lun']:
|
|
|
|
array.connect_host(module.params['host'],
|
|
|
|
module.params['volume'],
|
|
|
|
lun=module.params['lun'])
|
|
|
|
else:
|
|
|
|
array.connect_host(module.params['host'], module.params['volume'])
|
2018-09-08 00:59:46 +00:00
|
|
|
except Exception:
|
2018-12-07 14:42:59 +00:00
|
|
|
module.fail_json(msg='Host {0} configuration failed.'.format(module.params['host']))
|
2017-06-16 10:28:50 +00:00
|
|
|
module.exit_json(changed=changed)
|
|
|
|
|
|
|
|
|
|
|
|
def update_host(module, array):
|
|
|
|
changed = False
|
2018-12-07 14:42:59 +00:00
|
|
|
volumes = array.list_host_connections(module.params['host'])
|
|
|
|
if module.params['iqn'] or module.params['wwns']:
|
|
|
|
_update_host_initiators(module, array)
|
|
|
|
changed = True
|
|
|
|
if module.params['volume']:
|
|
|
|
current_vols = [vol['vol'] for vol in volumes]
|
|
|
|
if not module.params['volume'] in current_vols:
|
|
|
|
changed = _connect_new_volume(module, array)
|
2018-08-04 05:48:49 +00:00
|
|
|
api_version = array._list_available_rest_versions()
|
|
|
|
if AC_REQUIRED_API_VERSION in api_version:
|
2018-12-07 14:42:59 +00:00
|
|
|
if module.params['personality']:
|
|
|
|
changed = _update_host_personality(module, array)
|
2017-06-16 10:28:50 +00:00
|
|
|
module.exit_json(changed=changed)
|
|
|
|
|
|
|
|
|
|
|
|
def delete_host(module, array):
|
2018-12-07 14:42:59 +00:00
|
|
|
changed = False
|
|
|
|
try:
|
2017-06-16 10:28:50 +00:00
|
|
|
for vol in array.list_host_connections(module.params['host']):
|
|
|
|
array.disconnect_host(module.params['host'], vol["vol"])
|
|
|
|
array.delete_host(module.params['host'])
|
2018-12-07 14:42:59 +00:00
|
|
|
changed = True
|
2018-09-08 00:59:46 +00:00
|
|
|
except Exception:
|
2018-12-07 14:42:59 +00:00
|
|
|
module.fail_json(msg='Host {0} deletion failed'.format(module.params['host']))
|
2017-06-16 10:28:50 +00:00
|
|
|
module.exit_json(changed=changed)
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
argument_spec = purefa_argument_spec()
|
2017-08-22 00:25:13 +00:00
|
|
|
argument_spec.update(dict(
|
|
|
|
host=dict(type='str', required=True),
|
2017-08-22 15:43:14 +00:00
|
|
|
state=dict(type='str', default='present', choices=['absent', 'present']),
|
2018-12-07 14:42:59 +00:00
|
|
|
protocol=dict(type='str', default='iscsi', choices=['fc', 'iscsi', 'nvmef', 'mixed']),
|
|
|
|
nqn=dict(type='list'),
|
2017-08-22 00:25:13 +00:00
|
|
|
iqn=dict(type='list'),
|
|
|
|
wwns=dict(type='list'),
|
|
|
|
volume=dict(type='str'),
|
2018-12-07 14:42:59 +00:00
|
|
|
lun=dict(type='int'),
|
2018-08-04 05:48:49 +00:00
|
|
|
personality=dict(type='str', default='',
|
|
|
|
choices=['hpux', 'vms', 'aix', 'esxi', 'solaris',
|
2018-12-07 14:42:59 +00:00
|
|
|
'hitachi-vsp', 'oracle-vm-server', 'delete', '']),
|
2017-08-22 00:25:13 +00:00
|
|
|
))
|
2017-06-16 10:28:50 +00:00
|
|
|
|
2018-08-04 05:48:49 +00:00
|
|
|
module = AnsibleModule(argument_spec, supports_check_mode=False)
|
2017-06-16 10:28:50 +00:00
|
|
|
|
|
|
|
if not HAS_PURESTORAGE:
|
|
|
|
module.fail_json(msg='purestorage sdk is required for this module in host')
|
|
|
|
|
2018-12-26 15:14:20 +00:00
|
|
|
array = get_system(module)
|
2018-12-07 14:42:59 +00:00
|
|
|
api_version = array._list_available_rest_versions()
|
|
|
|
if module.params['nqn'] is not None and NVMEF_API_VERSION not in api_version:
|
|
|
|
module.fail_json(msg='NVMeF protocol not supported. Please upgrade your array.')
|
2017-06-16 10:28:50 +00:00
|
|
|
state = module.params['state']
|
|
|
|
protocol = module.params['protocol']
|
|
|
|
host = get_host(module, array)
|
2018-12-07 14:42:59 +00:00
|
|
|
if module.params['lun'] and not 1 <= module.params['lun'] <= 4095:
|
|
|
|
module.fail_json(msg='LUN ID of {0} is out of range (1 to 4095)'.format(module.params['lun']))
|
2017-06-16 10:28:50 +00:00
|
|
|
if module.params['volume']:
|
|
|
|
try:
|
|
|
|
array.get_volume(module.params['volume'])
|
2018-09-08 00:59:46 +00:00
|
|
|
except Exception:
|
2017-06-16 10:28:50 +00:00
|
|
|
module.fail_json(msg='Volume {} not found'.format(module.params['volume']))
|
|
|
|
|
2018-08-04 05:48:49 +00:00
|
|
|
if host is None and state == 'present':
|
|
|
|
make_host(module, array)
|
|
|
|
elif host and state == 'present':
|
2017-06-16 10:28:50 +00:00
|
|
|
update_host(module, array)
|
|
|
|
elif host and state == 'absent':
|
|
|
|
delete_host(module, array)
|
|
|
|
elif host is None and state == 'absent':
|
|
|
|
module.exit_json(changed=False)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|