1#!/usr/local/bin/python3.8
2
3# (c) 2017-2019, NetApp, Inc
4# GNU General Public License v3.0+
5# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6
7from __future__ import absolute_import, division, print_function
8__metaclass__ = type
9
10
11ANSIBLE_METADATA = {'metadata_version': '1.1',
12                    'status': ['preview'],
13                    'supported_by': 'certified'}
14
15
16DOCUMENTATION = '''
17
18module: na_ontap_iscsi
19
20short_description: NetApp ONTAP manage iSCSI service
21extends_documentation_fragment:
22    - netapp.ontap.netapp.na_ontap
23version_added: 2.6.0
24author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
25
26description:
27- create, delete, start, stop iSCSI service on SVM.
28
29options:
30
31  state:
32    description:
33    - Whether the service should be present or deleted.
34    choices: ['present', 'absent']
35    type: str
36    default: present
37
38  service_state:
39    description:
40    - Whether the specified service should running.
41    choices: ['started', 'stopped']
42    type: str
43
44  vserver:
45    required: true
46    type: str
47    description:
48    - The name of the vserver to use.
49
50'''
51
52EXAMPLES = """
53- name: Create iscsi service
54  na_ontap_iscsi:
55    state: present
56    service_state: started
57    vserver: ansibleVServer
58    hostname: "{{ netapp_hostname }}"
59    username: "{{ netapp_username }}"
60    password: "{{ netapp_password }}"
61
62- name: Stop Iscsi service
63  na_ontap_iscsi:
64    state: present
65    service_state: stopped
66    vserver: ansibleVServer
67    hostname: "{{ netapp_hostname }}"
68    username: "{{ netapp_username }}"
69    password: "{{ netapp_password }}"
70
71- name: Delete Iscsi service
72  na_ontap_iscsi:
73    state: absent
74    vserver: ansibleVServer
75    hostname: "{{ netapp_hostname }}"
76    username: "{{ netapp_username }}"
77    password: "{{ netapp_password }}"
78"""
79
80RETURN = """
81
82"""
83
84import traceback
85
86from ansible.module_utils.basic import AnsibleModule
87from ansible.module_utils._text import to_native
88import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
89
90HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
91
92
93class NetAppOntapISCSI(object):
94
95    def __init__(self):
96
97        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
98        self.argument_spec.update(dict(
99            state=dict(required=False, type='str', choices=['present', 'absent'], default='present'),
100            service_state=dict(required=False, type='str', choices=['started', 'stopped'], default=None),
101            vserver=dict(required=True, type='str'),
102        ))
103
104        self.module = AnsibleModule(
105            argument_spec=self.argument_spec,
106            supports_check_mode=True
107        )
108
109        params = self.module.params
110
111        # set up state variables
112        self.state = params['state']
113        self.service_state = params['service_state']
114        if self.state == 'present' and self.service_state is None:
115            self.service_state = 'started'
116        self.vserver = params['vserver']
117        self.is_started = None
118
119        if HAS_NETAPP_LIB is False:
120            self.module.fail_json(
121                msg="the python NetApp-Lib module is required")
122        else:
123            self.server = netapp_utils.setup_na_ontap_zapi(
124                module=self.module, vserver=self.vserver)
125
126    def get_iscsi(self):
127        """
128        Return details about the iscsi service
129
130        :return: Details about the iscsi service
131        :rtype: dict
132        """
133        iscsi_info = netapp_utils.zapi.NaElement('iscsi-service-get-iter')
134        iscsi_attributes = netapp_utils.zapi.NaElement('iscsi-service-info')
135
136        iscsi_attributes.add_new_child('vserver', self.vserver)
137
138        query = netapp_utils.zapi.NaElement('query')
139        query.add_child_elem(iscsi_attributes)
140
141        iscsi_info.add_child_elem(query)
142
143        result = self.server.invoke_successfully(iscsi_info, True)
144        return_value = None
145
146        if result.get_child_by_name('num-records') and \
147                int(result.get_child_content('num-records')) >= 1:
148
149            iscsi = result.get_child_by_name(
150                'attributes-list').get_child_by_name('iscsi-service-info')
151            if iscsi:
152                is_started = iscsi.get_child_content('is-available') == 'true'
153                return_value = {
154                    'is_started': is_started
155                }
156
157        return return_value
158
159    def create_iscsi_service(self):
160        """
161        Create iscsi service and start if requested
162        """
163        iscsi_service = netapp_utils.zapi.NaElement.create_node_with_children(
164            'iscsi-service-create',
165            **{'start': 'true' if self.state == 'started' else 'false'
166               })
167
168        try:
169            self.server.invoke_successfully(
170                iscsi_service, enable_tunneling=True)
171        except netapp_utils.zapi.NaApiError as e:
172            self.module.fail_json(msg="Error creating iscsi service: % s"
173                                  % (to_native(e)),
174                                  exception=traceback.format_exc())
175
176    def delete_iscsi_service(self):
177        """
178         Delete the iscsi service
179        """
180        if self.is_started:
181            self.stop_iscsi_service()
182
183        iscsi_delete = netapp_utils.zapi.NaElement.create_node_with_children(
184            'iscsi-service-destroy')
185
186        try:
187            self.server.invoke_successfully(
188                iscsi_delete, enable_tunneling=True)
189        except netapp_utils.zapi.NaApiError as e:
190            self.module.fail_json(msg="Error deleting iscsi service \
191                                  on vserver %s: %s"
192                                  % (self.vserver, to_native(e)),
193                                  exception=traceback.format_exc())
194
195    def stop_iscsi_service(self):
196        """
197         Stop iscsi service
198        """
199
200        iscsi_stop = netapp_utils.zapi.NaElement.create_node_with_children(
201            'iscsi-service-stop')
202
203        try:
204            self.server.invoke_successfully(iscsi_stop, enable_tunneling=True)
205        except netapp_utils.zapi.NaApiError as e:
206            self.module.fail_json(msg="Error Stopping iscsi service \
207                                  on vserver %s: %s"
208                                  % (self.vserver, to_native(e)),
209                                  exception=traceback.format_exc())
210
211    def start_iscsi_service(self):
212        """
213        Start iscsi service
214        """
215        iscsi_start = netapp_utils.zapi.NaElement.create_node_with_children(
216            'iscsi-service-start')
217
218        try:
219            self.server.invoke_successfully(iscsi_start, enable_tunneling=True)
220        except netapp_utils.zapi.NaApiError as e:
221            self.module.fail_json(msg="Error starting iscsi service \
222                                  on vserver %s: %s"
223                                  % (self.vserver, to_native(e)),
224                                  exception=traceback.format_exc())
225
226    def apply(self):
227        property_changed = False
228        iscsi_service_exists = False
229        netapp_utils.ems_log_event("na_ontap_iscsi", self.server)
230        iscsi_service_detail = self.get_iscsi()
231
232        if iscsi_service_detail:
233            self.is_started = iscsi_service_detail['is_started']
234            iscsi_service_exists = True
235
236            if self.state == 'absent':
237                property_changed = True
238
239            elif self.state == 'present':
240                is_started = 'started' if self.is_started else 'stopped'
241                property_changed = is_started != self.service_state
242
243        else:
244            if self.state == 'present':
245                property_changed = True
246
247        if property_changed:
248            if self.module.check_mode:
249                pass
250            else:
251                if self.state == 'present':
252                    if not iscsi_service_exists:
253                        self.create_iscsi_service()  # the service is stopped when initially created
254                    if self.service_state == 'started':
255                        self.start_iscsi_service()
256                    if iscsi_service_exists and self.service_state == 'stopped':
257                        self.stop_iscsi_service()
258
259                elif self.state == 'absent':
260                    self.delete_iscsi_service()
261
262        changed = property_changed
263        # TODO: include other details about the lun (size, etc.)
264        self.module.exit_json(changed=changed)
265
266
267def main():
268    v = NetAppOntapISCSI()
269    v.apply()
270
271
272if __name__ == '__main__':
273    main()
274