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_firewall_vip64
27short_description: Configure IPv6 to IPv4 virtual IPs 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 firewall feature and vip64 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.8"
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        version_added: 2.9
76    state:
77        description:
78            - Indicates whether to create or remove the object.
79              This attribute was present already in previous version in a deeper level.
80              It has been moved out to this outer level.
81        type: str
82        required: false
83        choices:
84            - present
85            - absent
86        version_added: 2.9
87    firewall_vip64:
88        description:
89            - Configure IPv6 to IPv4 virtual IPs.
90        default: null
91        type: dict
92        suboptions:
93            state:
94                description:
95                    - B(Deprecated)
96                    - Starting with Ansible 2.9 we recommend using the top-level 'state' parameter.
97                    - HORIZONTALLINE
98                    - Indicates whether to create or remove the object.
99                type: str
100                required: false
101                choices:
102                    - present
103                    - absent
104            arp_reply:
105                description:
106                    - Enable ARP reply.
107                type: str
108                choices:
109                    - disable
110                    - enable
111            color:
112                description:
113                    - Color of icon on the GUI.
114                type: int
115            comment:
116                description:
117                    - Comment.
118                type: str
119            extip:
120                description:
121                    - Start-external-IP [-end-external-IP].
122                type: str
123            extport:
124                description:
125                    - External service port.
126                type: str
127            id:
128                description:
129                    - Custom defined id.
130                type: int
131            ldb_method:
132                description:
133                    - Load balance method.
134                type: str
135                choices:
136                    - static
137                    - round-robin
138                    - weighted
139                    - least-session
140                    - least-rtt
141                    - first-alive
142            mappedip:
143                description:
144                    - Start-mapped-IP [-end-mapped-IP].
145                type: str
146            mappedport:
147                description:
148                    - Mapped service port.
149                type: str
150            monitor:
151                description:
152                    - Health monitors.
153                type: list
154                suboptions:
155                    name:
156                        description:
157                            - Health monitor name. Source firewall.ldb-monitor.name.
158                        required: true
159                        type: str
160            name:
161                description:
162                    - VIP64 name.
163                required: true
164                type: str
165            portforward:
166                description:
167                    - Enable port forwarding.
168                type: str
169                choices:
170                    - disable
171                    - enable
172            protocol:
173                description:
174                    - Mapped port protocol.
175                type: str
176                choices:
177                    - tcp
178                    - udp
179            realservers:
180                description:
181                    - Real servers.
182                type: list
183                suboptions:
184                    client_ip:
185                        description:
186                            - Restrict server to a client IP in this range.
187                        type: str
188                    healthcheck:
189                        description:
190                            - Per server health check.
191                        type: str
192                        choices:
193                            - disable
194                            - enable
195                            - vip
196                    holddown_interval:
197                        description:
198                            - Hold down interval.
199                        type: int
200                    id:
201                        description:
202                            - Real server ID.
203                        required: true
204                        type: int
205                    ip:
206                        description:
207                            - Mapped server IP.
208                        type: str
209                    max_connections:
210                        description:
211                            - Maximum number of connections allowed to server.
212                        type: int
213                    monitor:
214                        description:
215                            - Health monitors. Source firewall.ldb-monitor.name.
216                        type: str
217                    port:
218                        description:
219                            - Mapped server port.
220                        type: int
221                    status:
222                        description:
223                            - Server administrative status.
224                        type: str
225                        choices:
226                            - active
227                            - standby
228                            - disable
229                    weight:
230                        description:
231                            - weight
232                        type: int
233            server_type:
234                description:
235                    - Server type.
236                type: str
237                choices:
238                    - http
239                    - tcp
240                    - udp
241                    - ip
242            src_filter:
243                description:
244                    - "Source IP6 filter (x:x:x:x:x:x:x:x/x)."
245                type: list
246                suboptions:
247                    range:
248                        description:
249                            - Src-filter range.
250                        required: true
251                        type: str
252            type:
253                description:
254                    - "VIP type: static NAT or server load balance."
255                type: str
256                choices:
257                    - static-nat
258                    - server-load-balance
259            uuid:
260                description:
261                    - Universally Unique Identifier (UUID; automatically assigned but can be manually reset).
262                type: str
263'''
264
265EXAMPLES = '''
266- hosts: localhost
267  vars:
268   host: "192.168.122.40"
269   username: "admin"
270   password: ""
271   vdom: "root"
272   ssl_verify: "False"
273  tasks:
274  - name: Configure IPv6 to IPv4 virtual IPs.
275    fortios_firewall_vip64:
276      host:  "{{ host }}"
277      username: "{{ username }}"
278      password: "{{ password }}"
279      vdom:  "{{ vdom }}"
280      https: "False"
281      state: "present"
282      firewall_vip64:
283        arp_reply: "disable"
284        color: "4"
285        comment: "Comment."
286        extip: "<your_own_value>"
287        extport: "<your_own_value>"
288        id:  "8"
289        ldb_method: "static"
290        mappedip: "<your_own_value>"
291        mappedport: "<your_own_value>"
292        monitor:
293         -
294            name: "default_name_13 (source firewall.ldb-monitor.name)"
295        name: "default_name_14"
296        portforward: "disable"
297        protocol: "tcp"
298        realservers:
299         -
300            client_ip: "<your_own_value>"
301            healthcheck: "disable"
302            holddown_interval: "20"
303            id:  "21"
304            ip: "<your_own_value>"
305            max_connections: "23"
306            monitor: "<your_own_value> (source firewall.ldb-monitor.name)"
307            port: "25"
308            status: "active"
309            weight: "27"
310        server_type: "http"
311        src_filter:
312         -
313            range: "<your_own_value>"
314        type: "static-nat"
315        uuid: "<your_own_value>"
316'''
317
318RETURN = '''
319build:
320  description: Build number of the fortigate image
321  returned: always
322  type: str
323  sample: '1547'
324http_method:
325  description: Last method used to provision the content into FortiGate
326  returned: always
327  type: str
328  sample: 'PUT'
329http_status:
330  description: Last result given by FortiGate on last operation applied
331  returned: always
332  type: str
333  sample: "200"
334mkey:
335  description: Master key (id) used in the last call to FortiGate
336  returned: success
337  type: str
338  sample: "id"
339name:
340  description: Name of the table used to fulfill the request
341  returned: always
342  type: str
343  sample: "urlfilter"
344path:
345  description: Path of the table used to fulfill the request
346  returned: always
347  type: str
348  sample: "webfilter"
349revision:
350  description: Internal revision number
351  returned: always
352  type: str
353  sample: "17.0.2.10658"
354serial:
355  description: Serial number of the unit
356  returned: always
357  type: str
358  sample: "FGVMEVYYQT3AB5352"
359status:
360  description: Indication of the operation's result
361  returned: always
362  type: str
363  sample: "success"
364vdom:
365  description: Virtual domain used
366  returned: always
367  type: str
368  sample: "root"
369version:
370  description: Version of the FortiGate
371  returned: always
372  type: str
373  sample: "v5.6.3"
374
375'''
376
377from ansible.module_utils.basic import AnsibleModule
378from ansible.module_utils.connection import Connection
379from ansible.module_utils.network.fortios.fortios import FortiOSHandler
380from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG
381
382
383def login(data, fos):
384    host = data['host']
385    username = data['username']
386    password = data['password']
387    ssl_verify = data['ssl_verify']
388
389    fos.debug('on')
390    if 'https' in data and not data['https']:
391        fos.https('off')
392    else:
393        fos.https('on')
394
395    fos.login(host, username, password, verify=ssl_verify)
396
397
398def filter_firewall_vip64_data(json):
399    option_list = ['arp_reply', 'color', 'comment',
400                   'extip', 'extport', 'id',
401                   'ldb_method', 'mappedip', 'mappedport',
402                   'monitor', 'name', 'portforward',
403                   'protocol', 'realservers', 'server_type',
404                   'src_filter', 'type', 'uuid']
405    dictionary = {}
406
407    for attribute in option_list:
408        if attribute in json and json[attribute] is not None:
409            dictionary[attribute] = json[attribute]
410
411    return dictionary
412
413
414def underscore_to_hyphen(data):
415    if isinstance(data, list):
416        for elem in data:
417            elem = underscore_to_hyphen(elem)
418    elif isinstance(data, dict):
419        new_data = {}
420        for k, v in data.items():
421            new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
422        data = new_data
423
424    return data
425
426
427def firewall_vip64(data, fos):
428    vdom = data['vdom']
429    if 'state' in data and data['state']:
430        state = data['state']
431    elif 'state' in data['firewall_vip64'] and data['firewall_vip64']:
432        state = data['firewall_vip64']['state']
433    else:
434        state = True
435    firewall_vip64_data = data['firewall_vip64']
436    filtered_data = underscore_to_hyphen(filter_firewall_vip64_data(firewall_vip64_data))
437
438    if state == "present":
439        return fos.set('firewall',
440                       'vip64',
441                       data=filtered_data,
442                       vdom=vdom)
443
444    elif state == "absent":
445        return fos.delete('firewall',
446                          'vip64',
447                          mkey=filtered_data['name'],
448                          vdom=vdom)
449
450
451def is_successful_status(status):
452    return status['status'] == "success" or \
453        status['http_method'] == "DELETE" and status['http_status'] == 404
454
455
456def fortios_firewall(data, fos):
457
458    if data['firewall_vip64']:
459        resp = firewall_vip64(data, fos)
460
461    return not is_successful_status(resp), \
462        resp['status'] == "success", \
463        resp
464
465
466def main():
467    fields = {
468        "host": {"required": False, "type": "str"},
469        "username": {"required": False, "type": "str"},
470        "password": {"required": False, "type": "str", "default": "", "no_log": True},
471        "vdom": {"required": False, "type": "str", "default": "root"},
472        "https": {"required": False, "type": "bool", "default": True},
473        "ssl_verify": {"required": False, "type": "bool", "default": True},
474        "state": {"required": False, "type": "str",
475                  "choices": ["present", "absent"]},
476        "firewall_vip64": {
477            "required": False, "type": "dict", "default": None,
478            "options": {
479                "state": {"required": False, "type": "str",
480                          "choices": ["present", "absent"]},
481                "arp_reply": {"required": False, "type": "str",
482                              "choices": ["disable", "enable"]},
483                "color": {"required": False, "type": "int"},
484                "comment": {"required": False, "type": "str"},
485                "extip": {"required": False, "type": "str"},
486                "extport": {"required": False, "type": "str"},
487                "id": {"required": False, "type": "int"},
488                "ldb_method": {"required": False, "type": "str",
489                               "choices": ["static", "round-robin", "weighted",
490                                           "least-session", "least-rtt", "first-alive"]},
491                "mappedip": {"required": False, "type": "str"},
492                "mappedport": {"required": False, "type": "str"},
493                "monitor": {"required": False, "type": "list",
494                            "options": {
495                                "name": {"required": True, "type": "str"}
496                            }},
497                "name": {"required": True, "type": "str"},
498                "portforward": {"required": False, "type": "str",
499                                "choices": ["disable", "enable"]},
500                "protocol": {"required": False, "type": "str",
501                             "choices": ["tcp", "udp"]},
502                "realservers": {"required": False, "type": "list",
503                                "options": {
504                                    "client_ip": {"required": False, "type": "str"},
505                                    "healthcheck": {"required": False, "type": "str",
506                                                    "choices": ["disable", "enable", "vip"]},
507                                    "holddown_interval": {"required": False, "type": "int"},
508                                    "id": {"required": True, "type": "int"},
509                                    "ip": {"required": False, "type": "str"},
510                                    "max_connections": {"required": False, "type": "int"},
511                                    "monitor": {"required": False, "type": "str"},
512                                    "port": {"required": False, "type": "int"},
513                                    "status": {"required": False, "type": "str",
514                                               "choices": ["active", "standby", "disable"]},
515                                    "weight": {"required": False, "type": "int"}
516                                }},
517                "server_type": {"required": False, "type": "str",
518                                "choices": ["http", "tcp", "udp",
519                                            "ip"]},
520                "src_filter": {"required": False, "type": "list",
521                               "options": {
522                                   "range": {"required": True, "type": "str"}
523                               }},
524                "type": {"required": False, "type": "str",
525                         "choices": ["static-nat", "server-load-balance"]},
526                "uuid": {"required": False, "type": "str"}
527
528            }
529        }
530    }
531
532    module = AnsibleModule(argument_spec=fields,
533                           supports_check_mode=False)
534
535    # legacy_mode refers to using fortiosapi instead of HTTPAPI
536    legacy_mode = 'host' in module.params and module.params['host'] is not None and \
537                  'username' in module.params and module.params['username'] is not None and \
538                  'password' in module.params and module.params['password'] is not None
539
540    if not legacy_mode:
541        if module._socket_path:
542            connection = Connection(module._socket_path)
543            fos = FortiOSHandler(connection)
544
545            is_error, has_changed, result = fortios_firewall(module.params, fos)
546        else:
547            module.fail_json(**FAIL_SOCKET_MSG)
548    else:
549        try:
550            from fortiosapi import FortiOSAPI
551        except ImportError:
552            module.fail_json(msg="fortiosapi module is required")
553
554        fos = FortiOSAPI()
555
556        login(module.params, fos)
557        is_error, has_changed, result = fortios_firewall(module.params, fos)
558        fos.logout()
559
560    if not is_error:
561        module.exit_json(changed=has_changed, meta=result)
562    else:
563        module.fail_json(msg="Error in repo", meta=result)
564
565
566if __name__ == '__main__':
567    main()
568