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