1#!/usr/bin/python
2from __future__ import (absolute_import, division, print_function)
3# Copyright 2019 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_report_dataset
27short_description: Report dataset configuration 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 report feature and dataset category.
31      Examples include all parameters and values need to be adjusted to datasources before usage.
32      Tested with FOS v6.0.5
33version_added: "2.8"
34author:
35    - Miguel Angel Munoz (@mamunozgonzalez)
36    - Nicolas Thomas (@thomnico)
37notes:
38    - Requires fortiosapi library developed by Fortinet
39    - Run as a local_action in your playbook
40requirements:
41    - fortiosapi>=0.9.8
42options:
43    host:
44        description:
45            - FortiOS or FortiGate IP address.
46        type: str
47        required: false
48    username:
49        description:
50            - FortiOS or FortiGate username.
51        type: str
52        required: false
53    password:
54        description:
55            - FortiOS or FortiGate password.
56        type: str
57        default: ""
58    vdom:
59        description:
60            - Virtual domain, among those defined previously. A vdom is a
61              virtual instance of the FortiGate that can be configured and
62              used as a different unit.
63        type: str
64        default: root
65    https:
66        description:
67            - Indicates if the requests towards FortiGate must use HTTPS protocol.
68        type: bool
69        default: true
70    ssl_verify:
71        description:
72            - Ensures FortiGate certificate must be verified by a proper CA.
73        type: bool
74        default: true
75        version_added: 2.9
76    state:
77        description:
78            - Indicates whether to create or remove the object.
79              This attribute was present already in previous version in a deeper level.
80              It has been moved out to this outer level.
81        type: str
82        required: false
83        choices:
84            - present
85            - absent
86        version_added: 2.9
87    report_dataset:
88        description:
89            - Report dataset configuration.
90        default: null
91        type: dict
92        suboptions:
93            state:
94                description:
95                    - B(Deprecated)
96                    - Starting with Ansible 2.9 we recommend using the top-level 'state' parameter.
97                    - HORIZONTALLINE
98                    - Indicates whether to create or remove the object.
99                type: str
100                required: false
101                choices:
102                    - present
103                    - absent
104            field:
105                description:
106                    - Fields.
107                type: list
108                suboptions:
109                    displayname:
110                        description:
111                            - Display name.
112                        type: str
113                    id:
114                        description:
115                            - Field ID (1 to number of columns in SQL result).
116                        required: true
117                        type: int
118                    name:
119                        description:
120                            - Name.
121                        type: str
122                    type:
123                        description:
124                            - Field type.
125                        type: str
126                        choices:
127                            - text
128                            - integer
129                            - double
130            name:
131                description:
132                    - Name.
133                required: true
134                type: str
135            parameters:
136                description:
137                    - Parameters.
138                type: list
139                suboptions:
140                    data_type:
141                        description:
142                            - Data type.
143                        type: str
144                        choices:
145                            - text
146                            - integer
147                            - double
148                            - long-integer
149                            - date-time
150                    display_name:
151                        description:
152                            - Display name.
153                        type: str
154                    field:
155                        description:
156                            - SQL field name.
157                        type: str
158                    id:
159                        description:
160                            - Parameter ID (1 to number of columns in SQL result).
161                        required: true
162                        type: int
163            policy:
164                description:
165                    - Used by monitor policy.
166                type: int
167            query:
168                description:
169                    - SQL query statement.
170                type: str
171'''
172
173EXAMPLES = '''
174- hosts: localhost
175  vars:
176   host: "192.168.122.40"
177   username: "admin"
178   password: ""
179   vdom: "root"
180   ssl_verify: "False"
181  tasks:
182  - name: Report dataset configuration.
183    fortios_report_dataset:
184      host:  "{{ host }}"
185      username: "{{ username }}"
186      password: "{{ password }}"
187      vdom:  "{{ vdom }}"
188      https: "False"
189      state: "present"
190      report_dataset:
191        field:
192         -
193            displayname: "<your_own_value>"
194            id:  "5"
195            name: "default_name_6"
196            type: "text"
197        name: "default_name_8"
198        parameters:
199         -
200            data_type: "text"
201            display_name: "<your_own_value>"
202            field: "<your_own_value>"
203            id:  "13"
204        policy: "14"
205        query: "<your_own_value>"
206'''
207
208RETURN = '''
209build:
210  description: Build number of the fortigate image
211  returned: always
212  type: str
213  sample: '1547'
214http_method:
215  description: Last method used to provision the content into FortiGate
216  returned: always
217  type: str
218  sample: 'PUT'
219http_status:
220  description: Last result given by FortiGate on last operation applied
221  returned: always
222  type: str
223  sample: "200"
224mkey:
225  description: Master key (id) used in the last call to FortiGate
226  returned: success
227  type: str
228  sample: "id"
229name:
230  description: Name of the table used to fulfill the request
231  returned: always
232  type: str
233  sample: "urlfilter"
234path:
235  description: Path of the table used to fulfill the request
236  returned: always
237  type: str
238  sample: "webfilter"
239revision:
240  description: Internal revision number
241  returned: always
242  type: str
243  sample: "17.0.2.10658"
244serial:
245  description: Serial number of the unit
246  returned: always
247  type: str
248  sample: "FGVMEVYYQT3AB5352"
249status:
250  description: Indication of the operation's result
251  returned: always
252  type: str
253  sample: "success"
254vdom:
255  description: Virtual domain used
256  returned: always
257  type: str
258  sample: "root"
259version:
260  description: Version of the FortiGate
261  returned: always
262  type: str
263  sample: "v5.6.3"
264
265'''
266
267from ansible.module_utils.basic import AnsibleModule
268from ansible.module_utils.connection import Connection
269from ansible.module_utils.network.fortios.fortios import FortiOSHandler
270from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG
271
272
273def login(data, fos):
274    host = data['host']
275    username = data['username']
276    password = data['password']
277    ssl_verify = data['ssl_verify']
278
279    fos.debug('on')
280    if 'https' in data and not data['https']:
281        fos.https('off')
282    else:
283        fos.https('on')
284
285    fos.login(host, username, password, verify=ssl_verify)
286
287
288def filter_report_dataset_data(json):
289    option_list = ['field', 'name', 'parameters',
290                   'policy', 'query']
291    dictionary = {}
292
293    for attribute in option_list:
294        if attribute in json and json[attribute] is not None:
295            dictionary[attribute] = json[attribute]
296
297    return dictionary
298
299
300def underscore_to_hyphen(data):
301    if isinstance(data, list):
302        for elem in data:
303            elem = underscore_to_hyphen(elem)
304    elif isinstance(data, dict):
305        new_data = {}
306        for k, v in data.items():
307            new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
308        data = new_data
309
310    return data
311
312
313def report_dataset(data, fos):
314    vdom = data['vdom']
315    if 'state' in data and data['state']:
316        state = data['state']
317    elif 'state' in data['report_dataset'] and data['report_dataset']:
318        state = data['report_dataset']['state']
319    else:
320        state = True
321    report_dataset_data = data['report_dataset']
322    filtered_data = underscore_to_hyphen(filter_report_dataset_data(report_dataset_data))
323
324    if state == "present":
325        return fos.set('report',
326                       'dataset',
327                       data=filtered_data,
328                       vdom=vdom)
329
330    elif state == "absent":
331        return fos.delete('report',
332                          'dataset',
333                          mkey=filtered_data['name'],
334                          vdom=vdom)
335
336
337def is_successful_status(status):
338    return status['status'] == "success" or \
339        status['http_method'] == "DELETE" and status['http_status'] == 404
340
341
342def fortios_report(data, fos):
343
344    if data['report_dataset']:
345        resp = report_dataset(data, fos)
346
347    return not is_successful_status(resp), \
348        resp['status'] == "success", \
349        resp
350
351
352def main():
353    fields = {
354        "host": {"required": False, "type": "str"},
355        "username": {"required": False, "type": "str"},
356        "password": {"required": False, "type": "str", "default": "", "no_log": True},
357        "vdom": {"required": False, "type": "str", "default": "root"},
358        "https": {"required": False, "type": "bool", "default": True},
359        "ssl_verify": {"required": False, "type": "bool", "default": True},
360        "state": {"required": False, "type": "str",
361                  "choices": ["present", "absent"]},
362        "report_dataset": {
363            "required": False, "type": "dict", "default": None,
364            "options": {
365                "state": {"required": False, "type": "str",
366                          "choices": ["present", "absent"]},
367                "field": {"required": False, "type": "list",
368                          "options": {
369                              "displayname": {"required": False, "type": "str"},
370                              "id": {"required": True, "type": "int"},
371                              "name": {"required": False, "type": "str"},
372                              "type": {"required": False, "type": "str",
373                                       "choices": ["text", "integer", "double"]}
374                          }},
375                "name": {"required": True, "type": "str"},
376                "parameters": {"required": False, "type": "list",
377                               "options": {
378                                   "data_type": {"required": False, "type": "str",
379                                                 "choices": ["text", "integer", "double",
380                                                             "long-integer", "date-time"]},
381                                   "display_name": {"required": False, "type": "str"},
382                                   "field": {"required": False, "type": "str"},
383                                   "id": {"required": True, "type": "int"}
384                               }},
385                "policy": {"required": False, "type": "int"},
386                "query": {"required": False, "type": "str"}
387
388            }
389        }
390    }
391
392    module = AnsibleModule(argument_spec=fields,
393                           supports_check_mode=False)
394
395    # legacy_mode refers to using fortiosapi instead of HTTPAPI
396    legacy_mode = 'host' in module.params and module.params['host'] is not None and \
397                  'username' in module.params and module.params['username'] is not None and \
398                  'password' in module.params and module.params['password'] is not None
399
400    if not legacy_mode:
401        if module._socket_path:
402            connection = Connection(module._socket_path)
403            fos = FortiOSHandler(connection)
404
405            is_error, has_changed, result = fortios_report(module.params, fos)
406        else:
407            module.fail_json(**FAIL_SOCKET_MSG)
408    else:
409        try:
410            from fortiosapi import FortiOSAPI
411        except ImportError:
412            module.fail_json(msg="fortiosapi module is required")
413
414        fos = FortiOSAPI()
415
416        login(module.params, fos)
417        is_error, has_changed, result = fortios_report(module.params, fos)
418        fos.logout()
419
420    if not is_error:
421        module.exit_json(changed=has_changed, meta=result)
422    else:
423        module.fail_json(msg="Error in repo", meta=result)
424
425
426if __name__ == '__main__':
427    main()
428