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