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_wccp
27short_description: Configure WCCP 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 wccp 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_wccp:
84        description:
85            - Configure WCCP.
86        default: null
87        type: dict
88        suboptions:
89            assignment_bucket_format:
90                description:
91                    - Assignment bucket format for the WCCP cache engine.
92                type: str
93                choices:
94                    - wccp-v2
95                    - cisco-implementation
96            assignment_dstaddr_mask:
97                description:
98                    - Assignment destination address mask.
99                type: str
100            assignment_method:
101                description:
102                    - Hash key assignment preference.
103                type: str
104                choices:
105                    - HASH
106                    - MASK
107                    - any
108            assignment_srcaddr_mask:
109                description:
110                    - Assignment source address mask.
111                type: str
112            assignment_weight:
113                description:
114                    - Assignment of hash weight/ratio for the WCCP cache engine.
115                type: int
116            authentication:
117                description:
118                    - Enable/disable MD5 authentication.
119                type: str
120                choices:
121                    - enable
122                    - disable
123            cache_engine_method:
124                description:
125                    - Method used to forward traffic to the routers or to return to the cache engine.
126                type: str
127                choices:
128                    - GRE
129                    - L2
130            cache_id:
131                description:
132                    - IP address known to all routers. If the addresses are the same, use the default 0.0.0.0.
133                type: str
134            forward_method:
135                description:
136                    - Method used to forward traffic to the cache servers.
137                type: str
138                choices:
139                    - GRE
140                    - L2
141                    - any
142            group_address:
143                description:
144                    - IP multicast address used by the cache routers. For the FortiGate to ignore multicast WCCP traffic, use the default 0.0.0.0.
145                type: str
146            password:
147                description:
148                    - Password for MD5 authentication.
149                type: str
150            ports:
151                description:
152                    - Service ports.
153                type: str
154            ports_defined:
155                description:
156                    - Match method.
157                type: str
158                choices:
159                    - source
160                    - destination
161            primary_hash:
162                description:
163                    - Hash method.
164                type: str
165                choices:
166                    - src-ip
167                    - dst-ip
168                    - src-port
169                    - dst-port
170            priority:
171                description:
172                    - Service priority.
173                type: int
174            protocol:
175                description:
176                    - Service protocol.
177                type: int
178            return_method:
179                description:
180                    -  Method used to decline a redirected packet and return it to the FortiGate.
181                type: str
182                choices:
183                    - GRE
184                    - L2
185                    - any
186            router_id:
187                description:
188                    - IP address known to all cache engines. If all cache engines connect to the same FortiGate interface, use the default 0.0.0.0.
189                type: str
190            router_list:
191                description:
192                    - IP addresses of one or more WCCP routers.
193                type: str
194            server_list:
195                description:
196                    - IP addresses and netmasks for up to four cache servers.
197                type: str
198            server_type:
199                description:
200                    - Cache server type.
201                type: str
202                choices:
203                    - forward
204                    - proxy
205            service_id:
206                description:
207                    - Service ID.
208                type: str
209            service_type:
210                description:
211                    - WCCP service type used by the cache server for logical interception and redirection of traffic.
212                type: str
213                choices:
214                    - auto
215                    - standard
216                    - dynamic
217'''
218
219EXAMPLES = '''
220- hosts: localhost
221  vars:
222   host: "192.168.122.40"
223   username: "admin"
224   password: ""
225   vdom: "root"
226   ssl_verify: "False"
227  tasks:
228  - name: Configure WCCP.
229    fortios_system_wccp:
230      host:  "{{ host }}"
231      username: "{{ username }}"
232      password: "{{ password }}"
233      vdom:  "{{ vdom }}"
234      https: "False"
235      state: "present"
236      system_wccp:
237        assignment_bucket_format: "wccp-v2"
238        assignment_dstaddr_mask: "<your_own_value>"
239        assignment_method: "HASH"
240        assignment_srcaddr_mask: "<your_own_value>"
241        assignment_weight: "7"
242        authentication: "enable"
243        cache_engine_method: "GRE"
244        cache_id: "<your_own_value>"
245        forward_method: "GRE"
246        group_address: "<your_own_value>"
247        password: "<your_own_value>"
248        ports: "<your_own_value>"
249        ports_defined: "source"
250        primary_hash: "src-ip"
251        priority: "17"
252        protocol: "18"
253        return_method: "GRE"
254        router_id: "<your_own_value>"
255        router_list: "<your_own_value>"
256        server_list: "<your_own_value>"
257        server_type: "forward"
258        service_id: "<your_own_value>"
259        service_type: "auto"
260'''
261
262RETURN = '''
263build:
264  description: Build number of the fortigate image
265  returned: always
266  type: str
267  sample: '1547'
268http_method:
269  description: Last method used to provision the content into FortiGate
270  returned: always
271  type: str
272  sample: 'PUT'
273http_status:
274  description: Last result given by FortiGate on last operation applied
275  returned: always
276  type: str
277  sample: "200"
278mkey:
279  description: Master key (id) used in the last call to FortiGate
280  returned: success
281  type: str
282  sample: "id"
283name:
284  description: Name of the table used to fulfill the request
285  returned: always
286  type: str
287  sample: "urlfilter"
288path:
289  description: Path of the table used to fulfill the request
290  returned: always
291  type: str
292  sample: "webfilter"
293revision:
294  description: Internal revision number
295  returned: always
296  type: str
297  sample: "17.0.2.10658"
298serial:
299  description: Serial number of the unit
300  returned: always
301  type: str
302  sample: "FGVMEVYYQT3AB5352"
303status:
304  description: Indication of the operation's result
305  returned: always
306  type: str
307  sample: "success"
308vdom:
309  description: Virtual domain used
310  returned: always
311  type: str
312  sample: "root"
313version:
314  description: Version of the FortiGate
315  returned: always
316  type: str
317  sample: "v5.6.3"
318
319'''
320
321from ansible.module_utils.basic import AnsibleModule
322from ansible.module_utils.connection import Connection
323from ansible.module_utils.network.fortios.fortios import FortiOSHandler
324from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG
325
326
327def login(data, fos):
328    host = data['host']
329    username = data['username']
330    password = data['password']
331    ssl_verify = data['ssl_verify']
332
333    fos.debug('on')
334    if 'https' in data and not data['https']:
335        fos.https('off')
336    else:
337        fos.https('on')
338
339    fos.login(host, username, password, verify=ssl_verify)
340
341
342def filter_system_wccp_data(json):
343    option_list = ['assignment_bucket_format', 'assignment_dstaddr_mask', 'assignment_method',
344                   'assignment_srcaddr_mask', 'assignment_weight', 'authentication',
345                   'cache_engine_method', 'cache_id', 'forward_method',
346                   'group_address', 'password', 'ports',
347                   'ports_defined', 'primary_hash', 'priority',
348                   'protocol', 'return_method', 'router_id',
349                   'router_list', 'server_list', 'server_type',
350                   'service_id', 'service_type']
351    dictionary = {}
352
353    for attribute in option_list:
354        if attribute in json and json[attribute] is not None:
355            dictionary[attribute] = json[attribute]
356
357    return dictionary
358
359
360def underscore_to_hyphen(data):
361    if isinstance(data, list):
362        for elem in data:
363            elem = underscore_to_hyphen(elem)
364    elif isinstance(data, dict):
365        new_data = {}
366        for k, v in data.items():
367            new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
368        data = new_data
369
370    return data
371
372
373def system_wccp(data, fos):
374    vdom = data['vdom']
375    state = data['state']
376    system_wccp_data = data['system_wccp']
377    filtered_data = underscore_to_hyphen(filter_system_wccp_data(system_wccp_data))
378
379    if state == "present":
380        return fos.set('system',
381                       'wccp',
382                       data=filtered_data,
383                       vdom=vdom)
384
385    elif state == "absent":
386        return fos.delete('system',
387                          'wccp',
388                          mkey=filtered_data['service-id'],
389                          vdom=vdom)
390
391
392def is_successful_status(status):
393    return status['status'] == "success" or \
394        status['http_method'] == "DELETE" and status['http_status'] == 404
395
396
397def fortios_system(data, fos):
398
399    if data['system_wccp']:
400        resp = system_wccp(data, fos)
401
402    return not is_successful_status(resp), \
403        resp['status'] == "success", \
404        resp
405
406
407def main():
408    fields = {
409        "host": {"required": False, "type": "str"},
410        "username": {"required": False, "type": "str"},
411        "password": {"required": False, "type": "str", "default": "", "no_log": True},
412        "vdom": {"required": False, "type": "str", "default": "root"},
413        "https": {"required": False, "type": "bool", "default": True},
414        "ssl_verify": {"required": False, "type": "bool", "default": True},
415        "state": {"required": True, "type": "str",
416                  "choices": ["present", "absent"]},
417        "system_wccp": {
418            "required": False, "type": "dict", "default": None,
419            "options": {
420                "assignment_bucket_format": {"required": False, "type": "str",
421                                             "choices": ["wccp-v2", "cisco-implementation"]},
422                "assignment_dstaddr_mask": {"required": False, "type": "str"},
423                "assignment_method": {"required": False, "type": "str",
424                                      "choices": ["HASH", "MASK", "any"]},
425                "assignment_srcaddr_mask": {"required": False, "type": "str"},
426                "assignment_weight": {"required": False, "type": "int"},
427                "authentication": {"required": False, "type": "str",
428                                   "choices": ["enable", "disable"]},
429                "cache_engine_method": {"required": False, "type": "str",
430                                        "choices": ["GRE", "L2"]},
431                "cache_id": {"required": False, "type": "str"},
432                "forward_method": {"required": False, "type": "str",
433                                   "choices": ["GRE", "L2", "any"]},
434                "group_address": {"required": False, "type": "str"},
435                "password": {"required": False, "type": "str", "no_log": True},
436                "ports": {"required": False, "type": "str"},
437                "ports_defined": {"required": False, "type": "str",
438                                  "choices": ["source", "destination"]},
439                "primary_hash": {"required": False, "type": "str",
440                                 "choices": ["src-ip", "dst-ip", "src-port",
441                                             "dst-port"]},
442                "priority": {"required": False, "type": "int"},
443                "protocol": {"required": False, "type": "int"},
444                "return_method": {"required": False, "type": "str",
445                                  "choices": ["GRE", "L2", "any"]},
446                "router_id": {"required": False, "type": "str"},
447                "router_list": {"required": False, "type": "str"},
448                "server_list": {"required": False, "type": "str"},
449                "server_type": {"required": False, "type": "str",
450                                "choices": ["forward", "proxy"]},
451                "service_id": {"required": False, "type": "str"},
452                "service_type": {"required": False, "type": "str",
453                                 "choices": ["auto", "standard", "dynamic"]}
454
455            }
456        }
457    }
458
459    module = AnsibleModule(argument_spec=fields,
460                           supports_check_mode=False)
461
462    # legacy_mode refers to using fortiosapi instead of HTTPAPI
463    legacy_mode = 'host' in module.params and module.params['host'] is not None and \
464                  'username' in module.params and module.params['username'] is not None and \
465                  'password' in module.params and module.params['password'] is not None
466
467    if not legacy_mode:
468        if module._socket_path:
469            connection = Connection(module._socket_path)
470            fos = FortiOSHandler(connection)
471
472            is_error, has_changed, result = fortios_system(module.params, fos)
473        else:
474            module.fail_json(**FAIL_SOCKET_MSG)
475    else:
476        try:
477            from fortiosapi import FortiOSAPI
478        except ImportError:
479            module.fail_json(msg="fortiosapi module is required")
480
481        fos = FortiOSAPI()
482
483        login(module.params, fos)
484        is_error, has_changed, result = fortios_system(module.params, fos)
485        fos.logout()
486
487    if not is_error:
488        module.exit_json(changed=has_changed, meta=result)
489    else:
490        module.fail_json(msg="Error in repo", meta=result)
491
492
493if __name__ == '__main__':
494    main()
495