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