1#!/usr/bin/python
2#
3# Ansible module to manage configuration on fortios devices
4# (c) 2016, Benjamin Jolivot <bjolivot@gmail.com>
5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6
7from __future__ import absolute_import, division, print_function
8__metaclass__ = type
9
10
11ANSIBLE_METADATA = {'metadata_version': '1.1',
12                    'status': ['preview'],
13                    'supported_by': 'community'}
14
15
16DOCUMENTATION = """
17---
18module: fortios_config
19version_added: "2.3"
20author: "Benjamin Jolivot (@bjolivot)"
21short_description: Manage config on Fortinet FortiOS firewall devices
22description:
23  - This module provides management of FortiOS Devices configuration.
24extends_documentation_fragment: fortios
25options:
26  src:
27    description:
28      - The I(src) argument provides a path to the configuration template
29        to load into the remote device.
30  filter:
31    description:
32      - Only for partial backup, you can restrict by giving expected configuration path (ex. firewall address).
33    default: ""
34requirements:
35  - pyFG
36"""
37
38EXAMPLES = """
39- name: Backup current config
40  fortios_config:
41    host: 192.168.0.254
42    username: admin
43    password: password
44    backup: yes
45
46- name: Backup only address objects
47  fortios_config:
48    host: 192.168.0.254
49    username: admin
50    password: password
51    backup: yes
52    backup_path: /tmp/forti_backup/
53    filter: "firewall address"
54
55- name: Update configuration from file
56  fortios_config:
57    host: 192.168.0.254
58    username: admin
59    password: password
60    src: new_configuration.conf.j2
61
62"""
63
64RETURN = """
65running_config:
66  description: full config string
67  returned: always
68  type: str
69change_string:
70  description: The commands really executed by the module
71  returned: only if config changed
72  type: str
73"""
74
75from ansible.module_utils.basic import AnsibleModule
76from ansible.module_utils.network.fortios.fortios import fortios_argument_spec, fortios_required_if
77from ansible.module_utils.network.fortios.fortios import backup
78
79# check for pyFG lib
80try:
81    from pyFG import FortiOS, FortiConfig
82    from pyFG.fortios import logger
83    from pyFG.exceptions import CommandExecutionException, FailedCommit, ForcedCommit
84    HAS_PYFG = True
85except Exception:
86    HAS_PYFG = False
87
88
89# some blocks don't support update, so remove them
90NOT_UPDATABLE_CONFIG_OBJECTS = [
91    "vpn certificate local",
92]
93
94
95def main():
96    argument_spec = dict(
97        src=dict(type='str', default=None),
98        filter=dict(type='str', default=""),
99    )
100
101    argument_spec.update(fortios_argument_spec)
102
103    required_if = fortios_required_if
104
105    module = AnsibleModule(
106        argument_spec=argument_spec,
107        supports_check_mode=True,
108        required_if=required_if,
109    )
110
111    result = dict(changed=False)
112
113    # fail if pyFG not present
114    if not HAS_PYFG:
115        module.fail_json(msg='Could not import the python library pyFG required by this module')
116
117    # define device
118    f = FortiOS(module.params['host'],
119                username=module.params['username'],
120                password=module.params['password'],
121                timeout=module.params['timeout'],
122                vdom=module.params['vdom'])
123
124    # connect
125    try:
126        f.open()
127    except Exception:
128        module.fail_json(msg='Error connecting device')
129
130    # get  config
131    try:
132        f.load_config(path=module.params['filter'])
133        result['running_config'] = f.running_config.to_text()
134
135    except Exception:
136        module.fail_json(msg='Error reading running config')
137
138    # backup config
139    if module.params['backup']:
140        backup(module, f.running_config.to_text())
141
142    # update config
143    if module.params['src'] is not None:
144        # store config in str
145        try:
146            conf_str = module.params['src']
147            f.load_config(in_candidate=True, config_text=conf_str)
148        except Exception:
149            module.fail_json(msg="Can't open configuration file, or configuration invalid")
150
151        # get updates lines
152        change_string = f.compare_config()
153
154        # remove not updatable parts
155        c = FortiConfig()
156        c.parse_config_output(change_string)
157
158        for o in NOT_UPDATABLE_CONFIG_OBJECTS:
159            c.del_block(o)
160
161        change_string = c.to_text()
162
163        if change_string != "":
164            result['change_string'] = change_string
165            result['changed'] = True
166
167        # Commit if not check mode
168        if module.check_mode is False and change_string != "":
169            try:
170                f.commit(change_string)
171            except CommandExecutionException as e:
172                module.fail_json(msg="Unable to execute command, check your args, the error was {0}".format(e.message))
173            except FailedCommit as e:
174                module.fail_json(msg="Unable to commit, check your args, the error was {0}".format(e.message))
175            except ForcedCommit as e:
176                module.fail_json(msg="Failed to force commit, check your args, the error was {0}".format(e.message))
177
178    module.exit_json(**result)
179
180
181if __name__ == '__main__':
182    main()
183