Browse code

Initial commit for Pure Storage Ansible module (#25386)

* Initial commit for Pure Storage Ansible module

* Initial commit for Pure Storage Ansible module

* Initial commit for Pure Storage Ansible module

* Fix import issues as required by post-2.2

* Move last import to top

* Follow suggestions and only implement one module per PR
Fix documentation changes requested

* Documentation and formatting changes

Simon Dodsley authored on 2017/06/16 19:28:50
Showing 6 changed files
... ...
@@ -203,6 +203,11 @@ Ansible Changes By Release
203 203
   * gcp_healthcheck
204 204
   * gcp_target_proxy
205 205
   * gcp_url_map
206
+- purestorage
207
+  * purefa_host
208
+  * purefa_volume
209
+  * purefa_hg
210
+  * purefa_pg
206 211
 - rundeck
207 212
   * rundeck_acl_policy
208 213
   * rundeck_project
... ...
@@ -37,6 +37,7 @@ The following is a list of module_utils files and a general description. The mod
37 37
 - openstack.py - Utilities for modules that work with Openstack instances.
38 38
 - openswitch.py - Definitions and helper functions for modules that manage OpenSwitch devices
39 39
 - powershell.ps1 - Utilities for working with Microsoft Windows clients
40
+- pure.py - Functions and utilities for modules that work with the Pure Storage storage platforms.
40 41
 - pycompat24.py - Exception workaround for Python 2.4.
41 42
 - rax.py -  Definitions and helper functions for modules that work with Rackspace resources.
42 43
 - redhat.py - Functions for modules that manage Red Hat Network registration and subscriptions
43 44
new file mode 100644
... ...
@@ -0,0 +1,77 @@
0
+# -*- coding: utf-8 -*-
1
+
2
+# This code is part of Ansible, but is an independent component.
3
+# This particular file snippet, and this file snippet only, is BSD licensed.
4
+# Modules you write using this snippet, which is embedded dynamically by Ansible
5
+# still belong to the author of the module, and may assign their own license
6
+# to the complete work.
7
+#
8
+# Copyright (c), Simon Dodsley <simon@purestorage.com>,2017
9
+# All rights reserved.
10
+#
11
+# Redistribution and use in source and binary forms, with or without modification,
12
+# are permitted provided that the following conditions are met:
13
+#
14
+#    * Redistributions of source code must retain the above copyright
15
+#      notice, this list of conditions and the following disclaimer.
16
+#    * Redistributions in binary form must reproduce the above copyright notice,
17
+#      this list of conditions and the following disclaimer in the documentation
18
+#      and/or other materials provided with the distribution.
19
+#
20
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23
+# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
28
+# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+
30
+HAS_PURESTORAGE = True
31
+try:
32
+    from purestorage import purestorage
33
+except ImportError:
34
+    HAS_PURESTORAGE = False
35
+
36
+from functools import wraps
37
+from os import environ
38
+from os import path
39
+import platform
40
+
41
+VERSION = 1.0
42
+USER_AGENT_BASE = 'Ansible'
43
+
44
+
45
+def get_system(module):
46
+    """Return System Object or Fail"""
47
+    user_agent = '%(base)s %(class)s/%(version)s (%(platform)s)' % {
48
+        'base': USER_AGENT_BASE,
49
+        'class': __name__,
50
+        'version': VERSION,
51
+        'platform': platform.platform()
52
+    }
53
+    array_name = module.params['fa_url']
54
+    api = module.params['api_token']
55
+
56
+    if array_name and api:
57
+        system = purestorage.FlashArray(array_name, api_token=api, user_agent=user_agent)
58
+    elif environ.get('PUREFA_URL') and environ.get('PUREFA_API'):
59
+        system = purestorage.FlashArray(environ.get('PUREFA_URL'), api_token=(environ.get('PUREFA_API')), user_agent=user_agent)
60
+    else:
61
+        module.fail_json(msg="You must set PUREFA_URL and PUREFA_API environment variables or the fa_url and api_token module arguments")
62
+
63
+    try:
64
+        system.get()
65
+    except Exception:
66
+        module.fail_json(msg="Pure Storage FlashArray authentication failed. Check your credentials")
67
+    return system
68
+
69
+
70
+def purefa_argument_spec():
71
+    """Return standard base dictionary used for the argument_spec argument in AnsibleModule"""
72
+
73
+    return dict(
74
+        fa_url=dict(),
75
+        api_token=dict(no_log=True),
76
+    )
0 77
new file mode 100644
1 78
new file mode 100644
... ...
@@ -0,0 +1,207 @@
0
+#!/usr/bin/python
1
+# -*- coding: utf-8 -*-
2
+
3
+# (c) 2017, Simon Dodsley (simon@purestorage.com)
4
+#
5
+# This file is part of Ansible
6
+#
7
+# Ansible is free software: you can redistribute it and/or modify
8
+# it under the terms of the GNU General Public License as published by
9
+# the Free Software Foundation, either version 3 of the License, or
10
+# (at your option) any later version.
11
+#
12
+# Ansible is distributed in the hope that it will be useful,
13
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+# GNU General Public License for more details.
16
+#
17
+# You should have received a copy of the GNU General Public License
18
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ANSIBLE_METADATA = {'metadata_version': '1.0',
21
+                    'status': ['preview'],
22
+                    'supported_by': 'community'}
23
+
24
+
25
+DOCUMENTATION = '''
26
+---
27
+module: purefa_host
28
+version_added: "2.4"
29
+short_description: Create, Delete and Modify Hosts on Pure Storage FlashArray
30
+description:
31
+    - This module creates, deletes or modifies hosts on Pure Storage FlashArray.
32
+author: Simon Dodsley (@simondodsley)
33
+options:
34
+  host:
35
+    description:
36
+      - Host Name
37
+    required: true
38
+  state:
39
+    description:
40
+      - Creates host.
41
+      - When removing host all connected volumes will be disconnected.
42
+    required: false
43
+    default: present
44
+    choices: [ "present", "absent" ]
45
+  protocol:
46
+    description:
47
+      - Defines the host connection protocol for volumes.
48
+    required: false
49
+    default: iscsi
50
+    choices: [ "iscsi", "fc" ]
51
+  wwns:
52
+    description:
53
+      - List of wwns of the host if protocol is fc
54
+    required: false
55
+  iqn:
56
+    description:
57
+      - List of IQNs of the host if protocol is iscsi
58
+    required: false
59
+  volume:
60
+    description:
61
+      - Volume name to map to the host
62
+    required: false
63
+extends_documentation_fragment:
64
+    - purestorage
65
+'''
66
+
67
+EXAMPLES = '''
68
+- name: Create new new host
69
+  purefa_host:
70
+    host: foo
71
+    fa_url: 10.10.10.2
72
+    api_token: e31060a7-21fc-e277-6240-25983c6c4592
73
+
74
+- name: Delete host
75
+  purefa_host:
76
+    host: foo
77
+    state: absent
78
+    fa_url: 10.10.10.2
79
+    api_token: e31060a7-21fc-e277-6240-25983c6c4592
80
+
81
+- name: Make sure host bar is available with wwn ports
82
+  purefa_host:
83
+    host: bar
84
+    protocol: fc
85
+    wwns:
86
+      - "00:00:00:00:00:00:00"
87
+      - "11:11:11:11:11:11:11"
88
+    fa_url: 10.10.10.2
89
+    api_token: e31060a7-21fc-e277-6240-25983c6c4592
90
+
91
+- name: Make sure host bar is available with iSCSI ports
92
+  purefa_host:
93
+    host: bar
94
+    protocol: iscsi
95
+    iqn:
96
+      - "iqn.1994-05.com.redhat:7d366003913"
97
+    fa_url: 10.10.10.2
98
+    api_token: e31060a7-21fc-e277-6240-25983c6c4592
99
+
100
+- name: Map host foo to volume bar
101
+  purefa_host:
102
+    host: foo
103
+    volume: bar
104
+    fa_url: 10.10.10.2
105
+    api_token: e31060a7-21fc-e277-6240-25983c6c4592
106
+'''
107
+
108
+RETURN = '''
109
+'''
110
+
111
+from ansible.module_utils.basic import AnsibleModule
112
+from ansible.module_utils.pure import get_system, purefa_argument_spec
113
+
114
+
115
+HAS_PURESTORAGE = True
116
+try:
117
+    from purestorage import purestorage
118
+except ImportError:
119
+    HAS_PURESTORAGE = False
120
+
121
+
122
+def get_host(module, array):
123
+
124
+    host = None
125
+
126
+    for h in array.list_hosts():
127
+        if h["name"] == module.params['host']:
128
+            host = h
129
+            break
130
+
131
+    return host
132
+
133
+
134
+def make_host(module, array):
135
+
136
+    changed = True
137
+
138
+    if not module.check_mode:
139
+        host = array.create_host(module.params['host'])
140
+        if module.params['protocol'] == 'iscsi':
141
+            if module.params['iqn']:
142
+                array.set_host(module.params['host'], addiqnlist=module.params['iqn'])
143
+        if module.params['protocol'] == 'fc':
144
+            if module.params['wwns']:
145
+                array.set_host(module.params['host'], addwwnlist=module.params['wwns'])
146
+        if module.params['volume']:
147
+            array.connect_host(module.params['host'], module.params['volume'])
148
+    module.exit_json(changed=changed)
149
+
150
+
151
+def update_host(module, array):
152
+    changed = False
153
+    host = module.params['host']
154
+    module.exit_json(changed=changed)
155
+
156
+
157
+def delete_host(module, array):
158
+    changed = True
159
+    if not module.check_mode:
160
+        for vol in array.list_host_connections(module.params['host']):
161
+            array.disconnect_host(module.params['host'], vol["vol"])
162
+        array.delete_host(module.params['host'])
163
+    module.exit_json(changed=changed)
164
+
165
+
166
+def main():
167
+    argument_spec = purefa_argument_spec()
168
+    argument_spec.update(
169
+        dict(
170
+            host=dict(required=True),
171
+            state=dict(default='present', choices=['present', 'absent']),
172
+            protocol=dict(default='iscsi', choices=['iscsi', 'fc']),
173
+            iqn=dict(type='list'),
174
+            wwns=dict(type='list'),
175
+            volume=dict()
176
+        )
177
+    )
178
+
179
+    module = AnsibleModule(argument_spec, supports_check_mode=True)
180
+
181
+    if not HAS_PURESTORAGE:
182
+        module.fail_json(msg='purestorage sdk is required for this module in host')
183
+
184
+    state = module.params['state']
185
+    protocol = module.params['protocol']
186
+    array = get_system(module)
187
+    host = get_host(module, array)
188
+
189
+    if module.params['volume']:
190
+        try:
191
+            array.get_volume(module.params['volume'])
192
+        except:
193
+            module.fail_json(msg='Volume {} not found'.format(module.params['volume']))
194
+
195
+    if host and state == 'present':
196
+        update_host(module, array)
197
+    elif host and state == 'absent':
198
+        delete_host(module, array)
199
+    elif host is None and state == 'absent':
200
+        module.exit_json(changed=False)
201
+    else:
202
+        make_host(module, array)
203
+
204
+
205
+if __name__ == '__main__':
206
+    main()
0 207
new file mode 100644
... ...
@@ -0,0 +1,40 @@
0
+#
1
+# (c) 2017,  Simon Dodsley <simon@purestorage.com>
2
+#
3
+# This file is part of Ansible
4
+#
5
+# Ansible is free software: you can redistribute it and/or modify
6
+# it under the terms of the GNU General Public License as published by
7
+# the Free Software Foundation, either version 3 of the License, or
8
+# (at your option) any later version.
9
+#
10
+# Ansible is distributed in the hope that it will be useful,
11
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+# GNU General Public License for more details.
14
+#
15
+# You should have received a copy of the GNU General Public License
16
+# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
17
+
18
+
19
+class ModuleDocFragment(object):
20
+
21
+    # Standard Pure Storage documentation fragment
22
+    DOCUMENTATION = '''
23
+options:
24
+  fa_url:
25
+    description:
26
+      - FlashArray management IPv4 address or Hostname.
27
+    required: true
28
+  api_token:
29
+    description:
30
+      - FlashArray API token for admin privilaged user.
31
+    required: true
32
+notes:
33
+  - This module requires purestorage python library
34
+  - You must set C(PUREFA_URL) and C(PUREFA_API) environment variables
35
+    if I(url) and I(api_token) arguments are not passed to the module directly
36
+requirements:
37
+  - "python >= 2.7"
38
+  - purestorage
39
+'''