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_system_dns_server
27short_description: Configure DNS servers 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 dns_server 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.9"
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    state:
76        description:
77            - Indicates whether to create or remove the object.
78        type: str
79        required: true
80        choices:
81            - present
82            - absent
83    system_dns_server:
84        description:
85            - Configure DNS servers.
86        default: null
87        type: dict
88        suboptions:
89            dnsfilter_profile:
90                description:
91                    - DNS filter profile. Source dnsfilter.profile.name.
92                type: str
93            mode:
94                description:
95                    - DNS server mode.
96                type: str
97                choices:
98                    - recursive
99                    - non-recursive
100                    - forward-only
101            name:
102                description:
103                    - DNS server name. Source system.interface.name.
104                required: true
105                type: str
106'''
107
108EXAMPLES = '''
109- hosts: localhost
110  vars:
111   host: "192.168.122.40"
112   username: "admin"
113   password: ""
114   vdom: "root"
115   ssl_verify: "False"
116  tasks:
117  - name: Configure DNS servers.
118    fortios_system_dns_server:
119      host:  "{{ host }}"
120      username: "{{ username }}"
121      password: "{{ password }}"
122      vdom:  "{{ vdom }}"
123      https: "False"
124      state: "present"
125      system_dns_server:
126        dnsfilter_profile: "<your_own_value> (source dnsfilter.profile.name)"
127        mode: "recursive"
128        name: "default_name_5 (source system.interface.name)"
129'''
130
131RETURN = '''
132build:
133  description: Build number of the fortigate image
134  returned: always
135  type: str
136  sample: '1547'
137http_method:
138  description: Last method used to provision the content into FortiGate
139  returned: always
140  type: str
141  sample: 'PUT'
142http_status:
143  description: Last result given by FortiGate on last operation applied
144  returned: always
145  type: str
146  sample: "200"
147mkey:
148  description: Master key (id) used in the last call to FortiGate
149  returned: success
150  type: str
151  sample: "id"
152name:
153  description: Name of the table used to fulfill the request
154  returned: always
155  type: str
156  sample: "urlfilter"
157path:
158  description: Path of the table used to fulfill the request
159  returned: always
160  type: str
161  sample: "webfilter"
162revision:
163  description: Internal revision number
164  returned: always
165  type: str
166  sample: "17.0.2.10658"
167serial:
168  description: Serial number of the unit
169  returned: always
170  type: str
171  sample: "FGVMEVYYQT3AB5352"
172status:
173  description: Indication of the operation's result
174  returned: always
175  type: str
176  sample: "success"
177vdom:
178  description: Virtual domain used
179  returned: always
180  type: str
181  sample: "root"
182version:
183  description: Version of the FortiGate
184  returned: always
185  type: str
186  sample: "v5.6.3"
187
188'''
189
190from ansible.module_utils.basic import AnsibleModule
191from ansible.module_utils.connection import Connection
192from ansible.module_utils.network.fortios.fortios import FortiOSHandler
193from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG
194
195
196def login(data, fos):
197    host = data['host']
198    username = data['username']
199    password = data['password']
200    ssl_verify = data['ssl_verify']
201
202    fos.debug('on')
203    if 'https' in data and not data['https']:
204        fos.https('off')
205    else:
206        fos.https('on')
207
208    fos.login(host, username, password, verify=ssl_verify)
209
210
211def filter_system_dns_server_data(json):
212    option_list = ['dnsfilter_profile', 'mode', 'name']
213    dictionary = {}
214
215    for attribute in option_list:
216        if attribute in json and json[attribute] is not None:
217            dictionary[attribute] = json[attribute]
218
219    return dictionary
220
221
222def underscore_to_hyphen(data):
223    if isinstance(data, list):
224        for elem in data:
225            elem = underscore_to_hyphen(elem)
226    elif isinstance(data, dict):
227        new_data = {}
228        for k, v in data.items():
229            new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
230        data = new_data
231
232    return data
233
234
235def system_dns_server(data, fos):
236    vdom = data['vdom']
237    state = data['state']
238    system_dns_server_data = data['system_dns_server']
239    filtered_data = underscore_to_hyphen(filter_system_dns_server_data(system_dns_server_data))
240
241    if state == "present":
242        return fos.set('system',
243                       'dns-server',
244                       data=filtered_data,
245                       vdom=vdom)
246
247    elif state == "absent":
248        return fos.delete('system',
249                          'dns-server',
250                          mkey=filtered_data['name'],
251                          vdom=vdom)
252
253
254def is_successful_status(status):
255    return status['status'] == "success" or \
256        status['http_method'] == "DELETE" and status['http_status'] == 404
257
258
259def fortios_system(data, fos):
260
261    if data['system_dns_server']:
262        resp = system_dns_server(data, fos)
263
264    return not is_successful_status(resp), \
265        resp['status'] == "success", \
266        resp
267
268
269def main():
270    fields = {
271        "host": {"required": False, "type": "str"},
272        "username": {"required": False, "type": "str"},
273        "password": {"required": False, "type": "str", "default": "", "no_log": True},
274        "vdom": {"required": False, "type": "str", "default": "root"},
275        "https": {"required": False, "type": "bool", "default": True},
276        "ssl_verify": {"required": False, "type": "bool", "default": True},
277        "state": {"required": True, "type": "str",
278                  "choices": ["present", "absent"]},
279        "system_dns_server": {
280            "required": False, "type": "dict", "default": None,
281            "options": {
282                "dnsfilter_profile": {"required": False, "type": "str"},
283                "mode": {"required": False, "type": "str",
284                         "choices": ["recursive", "non-recursive", "forward-only"]},
285                "name": {"required": True, "type": "str"}
286
287            }
288        }
289    }
290
291    module = AnsibleModule(argument_spec=fields,
292                           supports_check_mode=False)
293
294    # legacy_mode refers to using fortiosapi instead of HTTPAPI
295    legacy_mode = 'host' in module.params and module.params['host'] is not None and \
296                  'username' in module.params and module.params['username'] is not None and \
297                  'password' in module.params and module.params['password'] is not None
298
299    if not legacy_mode:
300        if module._socket_path:
301            connection = Connection(module._socket_path)
302            fos = FortiOSHandler(connection)
303
304            is_error, has_changed, result = fortios_system(module.params, fos)
305        else:
306            module.fail_json(**FAIL_SOCKET_MSG)
307    else:
308        try:
309            from fortiosapi import FortiOSAPI
310        except ImportError:
311            module.fail_json(msg="fortiosapi module is required")
312
313        fos = FortiOSAPI()
314
315        login(module.params, fos)
316        is_error, has_changed, result = fortios_system(module.params, fos)
317        fos.logout()
318
319    if not is_error:
320        module.exit_json(changed=has_changed, meta=result)
321    else:
322        module.fail_json(msg="Error in repo", meta=result)
323
324
325if __name__ == '__main__':
326    main()
327