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_gtp_apn 27short_description: Configure APN for GTP 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 gtp feature and apn 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 state: 68 description: 69 - Indicates whether to create or remove the object. 70 type: str 71 required: true 72 choices: 73 - present 74 - absent 75 gtp_apn: 76 description: 77 - Configure APN for GTP. 78 default: null 79 type: dict 80 suboptions: 81 apn: 82 description: 83 - APN value. 84 type: str 85 name: 86 description: 87 - APN name. 88 required: true 89 type: str 90''' 91 92EXAMPLES = ''' 93- hosts: fortigates 94 collections: 95 - fortinet.fortios 96 connection: httpapi 97 vars: 98 vdom: "root" 99 ansible_httpapi_use_ssl: yes 100 ansible_httpapi_validate_certs: no 101 ansible_httpapi_port: 443 102 tasks: 103 - name: Configure APN for GTP. 104 fortios_gtp_apn: 105 vdom: "{{ vdom }}" 106 state: "present" 107 access_token: "<your_own_value>" 108 gtp_apn: 109 apn: "<your_own_value>" 110 name: "default_name_4" 111 112''' 113 114RETURN = ''' 115build: 116 description: Build number of the fortigate image 117 returned: always 118 type: str 119 sample: '1547' 120http_method: 121 description: Last method used to provision the content into FortiGate 122 returned: always 123 type: str 124 sample: 'PUT' 125http_status: 126 description: Last result given by FortiGate on last operation applied 127 returned: always 128 type: str 129 sample: "200" 130mkey: 131 description: Master key (id) used in the last call to FortiGate 132 returned: success 133 type: str 134 sample: "id" 135name: 136 description: Name of the table used to fulfill the request 137 returned: always 138 type: str 139 sample: "urlfilter" 140path: 141 description: Path of the table used to fulfill the request 142 returned: always 143 type: str 144 sample: "webfilter" 145revision: 146 description: Internal revision number 147 returned: always 148 type: str 149 sample: "17.0.2.10658" 150serial: 151 description: Serial number of the unit 152 returned: always 153 type: str 154 sample: "FGVMEVYYQT3AB5352" 155status: 156 description: Indication of the operation's result 157 returned: always 158 type: str 159 sample: "success" 160vdom: 161 description: Virtual domain used 162 returned: always 163 type: str 164 sample: "root" 165version: 166 description: Version of the FortiGate 167 returned: always 168 type: str 169 sample: "v5.6.3" 170 171''' 172from ansible.module_utils.basic import AnsibleModule 173from ansible.module_utils.connection import Connection 174from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import FortiOSHandler 175from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_legacy_fortiosapi 176from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import schema_to_module_spec 177from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_schema_versioning 178from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG 179from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import is_same_comparison 180from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import serialize 181 182 183def filter_gtp_apn_data(json): 184 option_list = ['apn', 'name'] 185 dictionary = {} 186 187 for attribute in option_list: 188 if attribute in json and json[attribute] is not None: 189 dictionary[attribute] = json[attribute] 190 191 return dictionary 192 193 194def underscore_to_hyphen(data): 195 if isinstance(data, list): 196 for i, elem in enumerate(data): 197 data[i] = underscore_to_hyphen(elem) 198 elif isinstance(data, dict): 199 new_data = {} 200 for k, v in data.items(): 201 new_data[k.replace('_', '-')] = underscore_to_hyphen(v) 202 data = new_data 203 204 return data 205 206 207def gtp_apn(data, fos, check_mode=False): 208 209 vdom = data['vdom'] 210 211 state = data['state'] 212 213 gtp_apn_data = data['gtp_apn'] 214 filtered_data = underscore_to_hyphen(filter_gtp_apn_data(gtp_apn_data)) 215 216 # check_mode starts from here 217 if check_mode: 218 mkey = fos.get_mkey('system', 'interface', filtered_data, vdom=vdom) 219 current_data = fos.get('system', 'interface', vdom=vdom, mkey=mkey) 220 is_existed = current_data and current_data.get('http_status') == 200 \ 221 and isinstance(current_data.get('results'), list) \ 222 and len(current_data['results']) > 0 223 224 # 2. if it exists and the state is 'present' then compare current settings with desired 225 if state == 'present' or state is True: 226 if mkey is None: 227 return False, True, filtered_data 228 229 # if mkey exists then compare each other 230 # record exits and they're matched or not 231 if is_existed: 232 is_same = is_same_comparison( 233 serialize(current_data['results'][0]), serialize(filtered_data)) 234 return False, not is_same, filtered_data 235 236 # record does not exist 237 return False, True, filtered_data 238 239 if state == 'absent': 240 if mkey is None: 241 return False, False, filtered_data 242 243 if is_existed: 244 return False, True, filtered_data 245 return False, False, filtered_data 246 247 return True, False, {'reason: ': 'Must provide state parameter'} 248 249 if state == "present" or state is True: 250 return fos.set('gtp', 251 'apn', 252 data=filtered_data, 253 vdom=vdom) 254 255 elif state == "absent": 256 return fos.delete('gtp', 257 'apn', 258 mkey=filtered_data['name'], 259 vdom=vdom) 260 else: 261 fos._module.fail_json(msg='state must be present or absent!') 262 263 264def is_successful_status(status): 265 return status['status'] == "success" or \ 266 status['http_method'] == "DELETE" and status['http_status'] == 404 267 268 269def fortios_gtp(data, fos, check_mode): 270 271 if data['gtp_apn']: 272 resp = gtp_apn(data, fos, check_mode) 273 else: 274 fos._module.fail_json(msg='missing task body: %s' % ('gtp_apn')) 275 if check_mode: 276 return resp 277 return not is_successful_status(resp), \ 278 resp['status'] == "success" and \ 279 (resp['revision_changed'] if 'revision_changed' in resp else True), \ 280 resp 281 282 283versioned_schema = { 284 "type": "list", 285 "children": { 286 "apn": { 287 "type": "string", 288 "revisions": { 289 "v6.0.0": True, 290 "v7.0.0": True, 291 "v6.0.5": True, 292 "v6.4.4": True, 293 "v6.4.0": True, 294 "v6.4.1": True, 295 "v6.2.0": True, 296 "v6.2.3": True, 297 "v6.2.5": True, 298 "v6.2.7": True, 299 "v6.0.11": True 300 } 301 }, 302 "name": { 303 "type": "string", 304 "revisions": { 305 "v6.0.0": True, 306 "v7.0.0": True, 307 "v6.0.5": True, 308 "v6.4.4": True, 309 "v6.4.0": True, 310 "v6.4.1": True, 311 "v6.2.0": True, 312 "v6.2.3": True, 313 "v6.2.5": True, 314 "v6.2.7": True, 315 "v6.0.11": True 316 } 317 } 318 }, 319 "revisions": { 320 "v6.0.0": True, 321 "v7.0.0": True, 322 "v6.0.5": True, 323 "v6.4.4": True, 324 "v6.4.0": True, 325 "v6.4.1": True, 326 "v6.2.0": True, 327 "v6.2.3": True, 328 "v6.2.5": True, 329 "v6.2.7": True, 330 "v6.0.11": True 331 } 332} 333 334 335def main(): 336 module_spec = schema_to_module_spec(versioned_schema) 337 mkeyname = 'name' 338 fields = { 339 "access_token": {"required": False, "type": "str", "no_log": True}, 340 "enable_log": {"required": False, "type": bool}, 341 "vdom": {"required": False, "type": "str", "default": "root"}, 342 "state": {"required": True, "type": "str", 343 "choices": ["present", "absent"]}, 344 "gtp_apn": { 345 "required": False, "type": "dict", "default": None, 346 "options": { 347 } 348 } 349 } 350 for attribute_name in module_spec['options']: 351 fields["gtp_apn"]['options'][attribute_name] = module_spec['options'][attribute_name] 352 if mkeyname and mkeyname == attribute_name: 353 fields["gtp_apn"]['options'][attribute_name]['required'] = True 354 355 check_legacy_fortiosapi() 356 module = AnsibleModule(argument_spec=fields, 357 supports_check_mode=True) 358 359 versions_check_result = None 360 if module._socket_path: 361 connection = Connection(module._socket_path) 362 if 'access_token' in module.params: 363 connection.set_option('access_token', module.params['access_token']) 364 365 if 'enable_log' in module.params: 366 connection.set_option('enable_log', module.params['enable_log']) 367 else: 368 connection.set_option('enable_log', False) 369 fos = FortiOSHandler(connection, module, mkeyname) 370 versions_check_result = check_schema_versioning(fos, versioned_schema, "gtp_apn") 371 372 is_error, has_changed, result = fortios_gtp(module.params, fos, module.check_mode) 373 374 else: 375 module.fail_json(**FAIL_SOCKET_MSG) 376 377 if versions_check_result and versions_check_result['matched'] is False: 378 module.warn("Ansible has detected version mismatch between FortOS system and your playbook, see more details by specifying option -vvv") 379 380 if not is_error: 381 if versions_check_result and versions_check_result['matched'] is False: 382 module.exit_json(changed=has_changed, version_check_warning=versions_check_result, meta=result) 383 else: 384 module.exit_json(changed=has_changed, meta=result) 385 else: 386 if versions_check_result and versions_check_result['matched'] is False: 387 module.fail_json(msg="Error in repo", version_check_warning=versions_check_result, meta=result) 388 else: 389 module.fail_json(msg="Error in repo", meta=result) 390 391 392if __name__ == '__main__': 393 main() 394