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_switch_controller_sflow
27short_description: Configure FortiSwitch sFlow 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 switch_controller feature and sflow 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    switch_controller_sflow:
68        description:
69            - Configure FortiSwitch sFlow.
70        default: null
71        type: dict
72        suboptions:
73            collector_ip:
74                description:
75                    - Collector IP.
76                type: str
77            collector_port:
78                description:
79                    - SFlow collector port (0 - 65535).
80                type: int
81'''
82
83EXAMPLES = '''
84- hosts: fortigates
85  collections:
86    - fortinet.fortios
87  connection: httpapi
88  vars:
89   vdom: "root"
90   ansible_httpapi_use_ssl: yes
91   ansible_httpapi_validate_certs: no
92   ansible_httpapi_port: 443
93  tasks:
94  - name: Configure FortiSwitch sFlow.
95    fortios_switch_controller_sflow:
96      vdom:  "{{ vdom }}"
97      switch_controller_sflow:
98        collector_ip: "<your_own_value>"
99        collector_port: "4"
100
101'''
102
103RETURN = '''
104build:
105  description: Build number of the fortigate image
106  returned: always
107  type: str
108  sample: '1547'
109http_method:
110  description: Last method used to provision the content into FortiGate
111  returned: always
112  type: str
113  sample: 'PUT'
114http_status:
115  description: Last result given by FortiGate on last operation applied
116  returned: always
117  type: str
118  sample: "200"
119mkey:
120  description: Master key (id) used in the last call to FortiGate
121  returned: success
122  type: str
123  sample: "id"
124name:
125  description: Name of the table used to fulfill the request
126  returned: always
127  type: str
128  sample: "urlfilter"
129path:
130  description: Path of the table used to fulfill the request
131  returned: always
132  type: str
133  sample: "webfilter"
134revision:
135  description: Internal revision number
136  returned: always
137  type: str
138  sample: "17.0.2.10658"
139serial:
140  description: Serial number of the unit
141  returned: always
142  type: str
143  sample: "FGVMEVYYQT3AB5352"
144status:
145  description: Indication of the operation's result
146  returned: always
147  type: str
148  sample: "success"
149vdom:
150  description: Virtual domain used
151  returned: always
152  type: str
153  sample: "root"
154version:
155  description: Version of the FortiGate
156  returned: always
157  type: str
158  sample: "v5.6.3"
159
160'''
161from ansible.module_utils.basic import AnsibleModule
162from ansible.module_utils.connection import Connection
163from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import FortiOSHandler
164from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_legacy_fortiosapi
165from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import schema_to_module_spec
166from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_schema_versioning
167from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG
168from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import is_same_comparison
169from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import serialize
170
171
172def filter_switch_controller_sflow_data(json):
173    option_list = ['collector_ip', 'collector_port']
174    dictionary = {}
175
176    for attribute in option_list:
177        if attribute in json and json[attribute] is not None:
178            dictionary[attribute] = json[attribute]
179
180    return dictionary
181
182
183def underscore_to_hyphen(data):
184    if isinstance(data, list):
185        for i, elem in enumerate(data):
186            data[i] = underscore_to_hyphen(elem)
187    elif isinstance(data, dict):
188        new_data = {}
189        for k, v in data.items():
190            new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
191        data = new_data
192
193    return data
194
195
196def switch_controller_sflow(data, fos):
197    vdom = data['vdom']
198    switch_controller_sflow_data = data['switch_controller_sflow']
199    filtered_data = underscore_to_hyphen(filter_switch_controller_sflow_data(switch_controller_sflow_data))
200
201    return fos.set('switch-controller',
202                   'sflow',
203                   data=filtered_data,
204                   vdom=vdom)
205
206
207def is_successful_status(status):
208    return status['status'] == "success" or \
209        status['http_method'] == "DELETE" and status['http_status'] == 404
210
211
212def fortios_switch_controller(data, fos):
213
214    if data['switch_controller_sflow']:
215        resp = switch_controller_sflow(data, fos)
216    else:
217        fos._module.fail_json(msg='missing task body: %s' % ('switch_controller_sflow'))
218
219    return not is_successful_status(resp), \
220        resp['status'] == "success" and \
221        (resp['revision_changed'] if 'revision_changed' in resp else True), \
222        resp
223
224
225versioned_schema = {
226    "type": "dict",
227    "children": {
228        "collector_port": {
229            "type": "integer",
230            "revisions": {
231                "v6.0.0": True,
232                "v7.0.0": True,
233                "v6.0.5": True,
234                "v6.4.4": True,
235                "v6.4.0": True,
236                "v6.4.1": True,
237                "v6.2.0": True,
238                "v6.2.3": True,
239                "v6.2.5": True,
240                "v6.2.7": True,
241                "v6.0.11": True
242            }
243        },
244        "collector_ip": {
245            "type": "string",
246            "revisions": {
247                "v6.0.0": True,
248                "v7.0.0": True,
249                "v6.0.5": True,
250                "v6.4.4": True,
251                "v6.4.0": True,
252                "v6.4.1": True,
253                "v6.2.0": True,
254                "v6.2.3": True,
255                "v6.2.5": True,
256                "v6.2.7": True,
257                "v6.0.11": True
258            }
259        }
260    },
261    "revisions": {
262        "v6.0.0": True,
263        "v7.0.0": True,
264        "v6.0.5": True,
265        "v6.4.4": True,
266        "v6.4.0": True,
267        "v6.4.1": True,
268        "v6.2.0": True,
269        "v6.2.3": True,
270        "v6.2.5": True,
271        "v6.2.7": True,
272        "v6.0.11": True
273    }
274}
275
276
277def main():
278    module_spec = schema_to_module_spec(versioned_schema)
279    mkeyname = None
280    fields = {
281        "access_token": {"required": False, "type": "str", "no_log": True},
282        "enable_log": {"required": False, "type": bool},
283        "vdom": {"required": False, "type": "str", "default": "root"},
284        "switch_controller_sflow": {
285            "required": False, "type": "dict", "default": None,
286            "options": {
287            }
288        }
289    }
290    for attribute_name in module_spec['options']:
291        fields["switch_controller_sflow"]['options'][attribute_name] = module_spec['options'][attribute_name]
292        if mkeyname and mkeyname == attribute_name:
293            fields["switch_controller_sflow"]['options'][attribute_name]['required'] = True
294
295    check_legacy_fortiosapi()
296    module = AnsibleModule(argument_spec=fields,
297                           supports_check_mode=False)
298
299    versions_check_result = None
300    if module._socket_path:
301        connection = Connection(module._socket_path)
302        if 'access_token' in module.params:
303            connection.set_option('access_token', module.params['access_token'])
304
305        if 'enable_log' in module.params:
306            connection.set_option('enable_log', module.params['enable_log'])
307        else:
308            connection.set_option('enable_log', False)
309        fos = FortiOSHandler(connection, module, mkeyname)
310        versions_check_result = check_schema_versioning(fos, versioned_schema, "switch_controller_sflow")
311
312        is_error, has_changed, result = fortios_switch_controller(module.params, fos)
313
314    else:
315        module.fail_json(**FAIL_SOCKET_MSG)
316
317    if versions_check_result and versions_check_result['matched'] is False:
318        module.warn("Ansible has detected version mismatch between FortOS system and your playbook, see more details by specifying option -vvv")
319
320    if not is_error:
321        if versions_check_result and versions_check_result['matched'] is False:
322            module.exit_json(changed=has_changed, version_check_warning=versions_check_result, meta=result)
323        else:
324            module.exit_json(changed=has_changed, meta=result)
325    else:
326        if versions_check_result and versions_check_result['matched'] is False:
327            module.fail_json(msg="Error in repo", version_check_warning=versions_check_result, meta=result)
328        else:
329            module.fail_json(msg="Error in repo", meta=result)
330
331
332if __name__ == '__main__':
333    main()
334