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