1#!/usr/local/bin/python3.8
2from __future__ import (absolute_import, division, print_function)
3# Copyright 2019-2020 Fortinet, Inc.
4#
5# This program 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# This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
17
18__metaclass__ = type
19
20ANSIBLE_METADATA = {'status': ['preview'],
21                    'supported_by': 'community',
22                    'metadata_version': '1.1'}
23
24DOCUMENTATION = '''
25---
26module: fortios_wireless_controller_ssid_policy
27short_description: Configure WiFi SSID policies in Fortinet's FortiOS and FortiGate.
28description:
29    - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the
30      user to set and modify wireless_controller feature and ssid_policy category.
31      Examples include all parameters and values need to be adjusted to datasources before usage.
32      Tested with FOS v6.0.0
33version_added: "2.10"
34author:
35    - Link Zheng (@chillancezen)
36    - Jie Xue (@JieX19)
37    - Hongbin Lu (@fgtdev-hblu)
38    - Frank Shen (@frankshen01)
39    - Miguel Angel Munoz (@mamunozgonzalez)
40    - Nicolas Thomas (@thomnico)
41notes:
42    - Legacy fortiosapi has been deprecated, httpapi is the preferred way to run playbooks
43
44requirements:
45    - ansible>=2.9.0
46options:
47    access_token:
48        description:
49            - Token-based authentication.
50              Generated from GUI of Fortigate.
51        type: str
52        required: false
53    enable_log:
54        description:
55            - Enable/Disable logging for task.
56        type: bool
57        required: false
58        default: false
59    vdom:
60        description:
61            - Virtual domain, among those defined previously. A vdom is a
62              virtual instance of the FortiGate that can be configured and
63              used as a different unit.
64        type: str
65        default: root
66
67    state:
68        description:
69            - Indicates whether to create or remove the object.
70        type: str
71        required: true
72        choices:
73            - present
74            - absent
75    wireless_controller_ssid_policy:
76        description:
77            - Configure WiFi SSID policies.
78        default: null
79        type: dict
80        suboptions:
81            description:
82                description:
83                    - Description.
84                type: str
85            name:
86                description:
87                    - Name.
88                required: true
89                type: str
90            vlan:
91                description:
92                    - VLAN interface name. Source system.interface.name.
93                type: str
94'''
95
96EXAMPLES = '''
97- hosts: fortigates
98  collections:
99    - fortinet.fortios
100  connection: httpapi
101  vars:
102   vdom: "root"
103   ansible_httpapi_use_ssl: yes
104   ansible_httpapi_validate_certs: no
105   ansible_httpapi_port: 443
106  tasks:
107  - name: Configure WiFi SSID policies.
108    fortios_wireless_controller_ssid_policy:
109      vdom:  "{{ vdom }}"
110      state: "present"
111      access_token: "<your_own_value>"
112      wireless_controller_ssid_policy:
113        description: "<your_own_value>"
114        name: "default_name_4"
115        vlan: "<your_own_value> (source system.interface.name)"
116
117'''
118
119RETURN = '''
120build:
121  description: Build number of the fortigate image
122  returned: always
123  type: str
124  sample: '1547'
125http_method:
126  description: Last method used to provision the content into FortiGate
127  returned: always
128  type: str
129  sample: 'PUT'
130http_status:
131  description: Last result given by FortiGate on last operation applied
132  returned: always
133  type: str
134  sample: "200"
135mkey:
136  description: Master key (id) used in the last call to FortiGate
137  returned: success
138  type: str
139  sample: "id"
140name:
141  description: Name of the table used to fulfill the request
142  returned: always
143  type: str
144  sample: "urlfilter"
145path:
146  description: Path of the table used to fulfill the request
147  returned: always
148  type: str
149  sample: "webfilter"
150revision:
151  description: Internal revision number
152  returned: always
153  type: str
154  sample: "17.0.2.10658"
155serial:
156  description: Serial number of the unit
157  returned: always
158  type: str
159  sample: "FGVMEVYYQT3AB5352"
160status:
161  description: Indication of the operation's result
162  returned: always
163  type: str
164  sample: "success"
165vdom:
166  description: Virtual domain used
167  returned: always
168  type: str
169  sample: "root"
170version:
171  description: Version of the FortiGate
172  returned: always
173  type: str
174  sample: "v5.6.3"
175
176'''
177from ansible.module_utils.basic import AnsibleModule
178from ansible.module_utils.connection import Connection
179from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import FortiOSHandler
180from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_legacy_fortiosapi
181from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import schema_to_module_spec
182from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_schema_versioning
183from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG
184from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import is_same_comparison
185from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import serialize
186
187
188def filter_wireless_controller_ssid_policy_data(json):
189    option_list = ['description', 'name', 'vlan']
190    dictionary = {}
191
192    for attribute in option_list:
193        if attribute in json and json[attribute] is not None:
194            dictionary[attribute] = json[attribute]
195
196    return dictionary
197
198
199def underscore_to_hyphen(data):
200    if isinstance(data, list):
201        for i, elem in enumerate(data):
202            data[i] = underscore_to_hyphen(elem)
203    elif isinstance(data, dict):
204        new_data = {}
205        for k, v in data.items():
206            new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
207        data = new_data
208
209    return data
210
211
212def wireless_controller_ssid_policy(data, fos):
213    vdom = data['vdom']
214
215    state = data['state']
216
217    wireless_controller_ssid_policy_data = data['wireless_controller_ssid_policy']
218    filtered_data = underscore_to_hyphen(filter_wireless_controller_ssid_policy_data(wireless_controller_ssid_policy_data))
219
220    if state == "present" or state is True:
221        return fos.set('wireless-controller',
222                       'ssid-policy',
223                       data=filtered_data,
224                       vdom=vdom)
225
226    elif state == "absent":
227        return fos.delete('wireless-controller',
228                          'ssid-policy',
229                          mkey=filtered_data['name'],
230                          vdom=vdom)
231    else:
232        fos._module.fail_json(msg='state must be present or absent!')
233
234
235def is_successful_status(status):
236    return status['status'] == "success" or \
237        status['http_method'] == "DELETE" and status['http_status'] == 404
238
239
240def fortios_wireless_controller(data, fos):
241
242    if data['wireless_controller_ssid_policy']:
243        resp = wireless_controller_ssid_policy(data, fos)
244    else:
245        fos._module.fail_json(msg='missing task body: %s' % ('wireless_controller_ssid_policy'))
246
247    return not is_successful_status(resp), \
248        resp['status'] == "success" and \
249        (resp['revision_changed'] if 'revision_changed' in resp else True), \
250        resp
251
252
253versioned_schema = {
254    "type": "list",
255    "children": {
256        "vlan": {
257            "type": "string",
258            "revisions": {
259                "v7.0.0": True
260            }
261        },
262        "name": {
263            "type": "string",
264            "revisions": {
265                "v7.0.0": True
266            }
267        },
268        "description": {
269            "type": "string",
270            "revisions": {
271                "v7.0.0": True
272            }
273        }
274    },
275    "revisions": {
276        "v7.0.0": True
277    }
278}
279
280
281def main():
282    module_spec = schema_to_module_spec(versioned_schema)
283    mkeyname = 'name'
284    fields = {
285        "access_token": {"required": False, "type": "str", "no_log": True},
286        "enable_log": {"required": False, "type": bool},
287        "vdom": {"required": False, "type": "str", "default": "root"},
288        "state": {"required": True, "type": "str",
289                  "choices": ["present", "absent"]},
290        "wireless_controller_ssid_policy": {
291            "required": False, "type": "dict", "default": None,
292            "options": {
293            }
294        }
295    }
296    for attribute_name in module_spec['options']:
297        fields["wireless_controller_ssid_policy"]['options'][attribute_name] = module_spec['options'][attribute_name]
298        if mkeyname and mkeyname == attribute_name:
299            fields["wireless_controller_ssid_policy"]['options'][attribute_name]['required'] = True
300
301    check_legacy_fortiosapi()
302    module = AnsibleModule(argument_spec=fields,
303                           supports_check_mode=False)
304
305    versions_check_result = None
306    if module._socket_path:
307        connection = Connection(module._socket_path)
308        if 'access_token' in module.params:
309            connection.set_option('access_token', module.params['access_token'])
310
311        if 'enable_log' in module.params:
312            connection.set_option('enable_log', module.params['enable_log'])
313        else:
314            connection.set_option('enable_log', False)
315        fos = FortiOSHandler(connection, module, mkeyname)
316        versions_check_result = check_schema_versioning(fos, versioned_schema, "wireless_controller_ssid_policy")
317
318        is_error, has_changed, result = fortios_wireless_controller(module.params, fos)
319
320    else:
321        module.fail_json(**FAIL_SOCKET_MSG)
322
323    if versions_check_result and versions_check_result['matched'] is False:
324        module.warn("Ansible has detected version mismatch between FortOS system and your playbook, see more details by specifying option -vvv")
325
326    if not is_error:
327        if versions_check_result and versions_check_result['matched'] is False:
328            module.exit_json(changed=has_changed, version_check_warning=versions_check_result, meta=result)
329        else:
330            module.exit_json(changed=has_changed, meta=result)
331    else:
332        if versions_check_result and versions_check_result['matched'] is False:
333            module.fail_json(msg="Error in repo", version_check_warning=versions_check_result, meta=result)
334        else:
335            module.fail_json(msg="Error in repo", meta=result)
336
337
338if __name__ == '__main__':
339    main()
340