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_system_resource_limits
27short_description: Configure resource limits 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 resource_limits 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    system_resource_limits:
68        description:
69            - Configure resource limits.
70        default: null
71        type: dict
72        suboptions:
73            custom_service:
74                description:
75                    - Maximum number of firewall custom services.
76                type: int
77            dialup_tunnel:
78                description:
79                    - Maximum number of dial-up tunnels.
80                type: int
81            firewall_address:
82                description:
83                    - Maximum number of firewall addresses (IPv4, IPv6, multicast).
84                type: int
85            firewall_addrgrp:
86                description:
87                    - Maximum number of firewall address groups (IPv4, IPv6).
88                type: int
89            firewall_policy:
90                description:
91                    - Maximum number of firewall policies (IPv4, IPv6, policy46, policy64, DoS-policy4, DoS-policy6, multicast).
92                type: int
93            ipsec_phase1:
94                description:
95                    - Maximum number of VPN IPsec phase1 tunnels.
96                type: int
97            ipsec_phase1_interface:
98                description:
99                    - Maximum number of VPN IPsec phase1 interface tunnels.
100                type: int
101            ipsec_phase2:
102                description:
103                    - Maximum number of VPN IPsec phase2 tunnels.
104                type: int
105            ipsec_phase2_interface:
106                description:
107                    - Maximum number of VPN IPsec phase2 interface tunnels.
108                type: int
109            log_disk_quota:
110                description:
111                    - Log disk quota in MB.
112                type: int
113            onetime_schedule:
114                description:
115                    - Maximum number of firewall one-time schedules.
116                type: int
117            proxy:
118                description:
119                    - Maximum number of concurrent proxy users.
120                type: int
121            recurring_schedule:
122                description:
123                    - Maximum number of firewall recurring schedules.
124                type: int
125            service_group:
126                description:
127                    - Maximum number of firewall service groups.
128                type: int
129            session:
130                description:
131                    - Maximum number of sessions.
132                type: int
133            sslvpn:
134                description:
135                    - Maximum number of SSL-VPN.
136                type: int
137            user:
138                description:
139                    - Maximum number of local users.
140                type: int
141            user_group:
142                description:
143                    - Maximum number of user groups.
144                type: int
145'''
146
147EXAMPLES = '''
148- hosts: fortigates
149  collections:
150    - fortinet.fortios
151  connection: httpapi
152  vars:
153   vdom: "root"
154   ansible_httpapi_use_ssl: yes
155   ansible_httpapi_validate_certs: no
156   ansible_httpapi_port: 443
157  tasks:
158  - name: Configure resource limits.
159    fortios_system_resource_limits:
160      vdom:  "{{ vdom }}"
161      system_resource_limits:
162        custom_service: "3"
163        dialup_tunnel: "4"
164        firewall_address: "5"
165        firewall_addrgrp: "6"
166        firewall_policy: "7"
167        ipsec_phase1: "8"
168        ipsec_phase1_interface: "9"
169        ipsec_phase2: "10"
170        ipsec_phase2_interface: "11"
171        log_disk_quota: "12"
172        onetime_schedule: "13"
173        proxy: "14"
174        recurring_schedule: "15"
175        service_group: "16"
176        session: "17"
177        sslvpn: "18"
178        user: "19"
179        user_group: "20"
180
181'''
182
183RETURN = '''
184build:
185  description: Build number of the fortigate image
186  returned: always
187  type: str
188  sample: '1547'
189http_method:
190  description: Last method used to provision the content into FortiGate
191  returned: always
192  type: str
193  sample: 'PUT'
194http_status:
195  description: Last result given by FortiGate on last operation applied
196  returned: always
197  type: str
198  sample: "200"
199mkey:
200  description: Master key (id) used in the last call to FortiGate
201  returned: success
202  type: str
203  sample: "id"
204name:
205  description: Name of the table used to fulfill the request
206  returned: always
207  type: str
208  sample: "urlfilter"
209path:
210  description: Path of the table used to fulfill the request
211  returned: always
212  type: str
213  sample: "webfilter"
214revision:
215  description: Internal revision number
216  returned: always
217  type: str
218  sample: "17.0.2.10658"
219serial:
220  description: Serial number of the unit
221  returned: always
222  type: str
223  sample: "FGVMEVYYQT3AB5352"
224status:
225  description: Indication of the operation's result
226  returned: always
227  type: str
228  sample: "success"
229vdom:
230  description: Virtual domain used
231  returned: always
232  type: str
233  sample: "root"
234version:
235  description: Version of the FortiGate
236  returned: always
237  type: str
238  sample: "v5.6.3"
239
240'''
241from ansible.module_utils.basic import AnsibleModule
242from ansible.module_utils.connection import Connection
243from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import FortiOSHandler
244from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_legacy_fortiosapi
245from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import schema_to_module_spec
246from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_schema_versioning
247from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG
248from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import is_same_comparison
249from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import serialize
250
251
252def filter_system_resource_limits_data(json):
253    option_list = ['custom_service', 'dialup_tunnel', 'firewall_address',
254                   'firewall_addrgrp', 'firewall_policy', 'ipsec_phase1',
255                   'ipsec_phase1_interface', 'ipsec_phase2', 'ipsec_phase2_interface',
256                   'log_disk_quota', 'onetime_schedule', 'proxy',
257                   'recurring_schedule', 'service_group', 'session',
258                   'sslvpn', 'user', 'user_group']
259    dictionary = {}
260
261    for attribute in option_list:
262        if attribute in json and json[attribute] is not None:
263            dictionary[attribute] = json[attribute]
264
265    return dictionary
266
267
268def underscore_to_hyphen(data):
269    if isinstance(data, list):
270        for i, elem in enumerate(data):
271            data[i] = underscore_to_hyphen(elem)
272    elif isinstance(data, dict):
273        new_data = {}
274        for k, v in data.items():
275            new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
276        data = new_data
277
278    return data
279
280
281def system_resource_limits(data, fos):
282    vdom = data['vdom']
283    system_resource_limits_data = data['system_resource_limits']
284    filtered_data = underscore_to_hyphen(filter_system_resource_limits_data(system_resource_limits_data))
285
286    return fos.set('system',
287                   'resource-limits',
288                   data=filtered_data,
289                   vdom=vdom)
290
291
292def is_successful_status(status):
293    return status['status'] == "success" or \
294        status['http_method'] == "DELETE" and status['http_status'] == 404
295
296
297def fortios_system(data, fos):
298
299    if data['system_resource_limits']:
300        resp = system_resource_limits(data, fos)
301    else:
302        fos._module.fail_json(msg='missing task body: %s' % ('system_resource_limits'))
303
304    return not is_successful_status(resp), \
305        resp['status'] == "success" and \
306        (resp['revision_changed'] if 'revision_changed' in resp else True), \
307        resp
308
309
310versioned_schema = {
311    "type": "dict",
312    "children": {
313        "service_group": {
314            "type": "integer",
315            "revisions": {
316                "v6.0.0": True,
317                "v7.0.0": True,
318                "v6.0.5": True,
319                "v6.4.4": True,
320                "v6.4.0": True,
321                "v6.4.1": True,
322                "v6.2.0": True,
323                "v6.2.3": True,
324                "v6.2.5": True,
325                "v6.2.7": True,
326                "v6.0.11": True
327            }
328        },
329        "firewall_address": {
330            "type": "integer",
331            "revisions": {
332                "v6.0.0": True,
333                "v7.0.0": True,
334                "v6.0.5": True,
335                "v6.4.4": True,
336                "v6.4.0": True,
337                "v6.4.1": True,
338                "v6.2.0": True,
339                "v6.2.3": True,
340                "v6.2.5": True,
341                "v6.2.7": True,
342                "v6.0.11": True
343            }
344        },
345        "firewall_policy": {
346            "type": "integer",
347            "revisions": {
348                "v6.0.0": True,
349                "v7.0.0": True,
350                "v6.0.5": True,
351                "v6.4.4": True,
352                "v6.4.0": True,
353                "v6.4.1": True,
354                "v6.2.0": True,
355                "v6.2.3": True,
356                "v6.2.5": True,
357                "v6.2.7": True,
358                "v6.0.11": True
359            }
360        },
361        "ipsec_phase1": {
362            "type": "integer",
363            "revisions": {
364                "v6.0.0": True,
365                "v7.0.0": True,
366                "v6.0.5": True,
367                "v6.4.4": True,
368                "v6.4.0": True,
369                "v6.4.1": True,
370                "v6.2.0": True,
371                "v6.2.3": True,
372                "v6.2.5": True,
373                "v6.2.7": True,
374                "v6.0.11": True
375            }
376        },
377        "session": {
378            "type": "integer",
379            "revisions": {
380                "v6.0.0": True,
381                "v7.0.0": True,
382                "v6.0.5": True,
383                "v6.4.4": True,
384                "v6.4.0": True,
385                "v6.4.1": True,
386                "v6.2.0": True,
387                "v6.2.3": True,
388                "v6.2.5": True,
389                "v6.2.7": True,
390                "v6.0.11": True
391            }
392        },
393        "ipsec_phase1_interface": {
394            "type": "integer",
395            "revisions": {
396                "v6.0.0": True,
397                "v7.0.0": True,
398                "v6.0.5": True,
399                "v6.4.4": True,
400                "v6.4.0": True,
401                "v6.4.1": True,
402                "v6.2.0": True,
403                "v6.2.3": True,
404                "v6.2.5": True,
405                "v6.2.7": True,
406                "v6.0.11": True
407            }
408        },
409        "custom_service": {
410            "type": "integer",
411            "revisions": {
412                "v6.0.0": True,
413                "v7.0.0": True,
414                "v6.0.5": True,
415                "v6.4.4": True,
416                "v6.4.0": True,
417                "v6.4.1": True,
418                "v6.2.0": True,
419                "v6.2.3": True,
420                "v6.2.5": True,
421                "v6.2.7": True,
422                "v6.0.11": True
423            }
424        },
425        "ipsec_phase2": {
426            "type": "integer",
427            "revisions": {
428                "v6.0.0": True,
429                "v7.0.0": True,
430                "v6.0.5": True,
431                "v6.4.4": True,
432                "v6.4.0": True,
433                "v6.4.1": True,
434                "v6.2.0": True,
435                "v6.2.3": True,
436                "v6.2.5": True,
437                "v6.2.7": True,
438                "v6.0.11": True
439            }
440        },
441        "ipsec_phase2_interface": {
442            "type": "integer",
443            "revisions": {
444                "v6.0.0": True,
445                "v7.0.0": True,
446                "v6.0.5": True,
447                "v6.4.4": True,
448                "v6.4.0": True,
449                "v6.4.1": True,
450                "v6.2.0": True,
451                "v6.2.3": True,
452                "v6.2.5": True,
453                "v6.2.7": True,
454                "v6.0.11": True
455            }
456        },
457        "dialup_tunnel": {
458            "type": "integer",
459            "revisions": {
460                "v6.0.0": True,
461                "v7.0.0": True,
462                "v6.0.5": True,
463                "v6.4.4": True,
464                "v6.4.0": True,
465                "v6.4.1": True,
466                "v6.2.0": True,
467                "v6.2.3": True,
468                "v6.2.5": True,
469                "v6.2.7": True,
470                "v6.0.11": True
471            }
472        },
473        "user": {
474            "type": "integer",
475            "revisions": {
476                "v6.0.0": True,
477                "v7.0.0": True,
478                "v6.0.5": True,
479                "v6.4.4": True,
480                "v6.4.0": True,
481                "v6.4.1": True,
482                "v6.2.0": True,
483                "v6.2.3": True,
484                "v6.2.5": True,
485                "v6.2.7": True,
486                "v6.0.11": True
487            }
488        },
489        "user_group": {
490            "type": "integer",
491            "revisions": {
492                "v6.0.0": True,
493                "v7.0.0": True,
494                "v6.0.5": True,
495                "v6.4.4": True,
496                "v6.4.0": True,
497                "v6.4.1": True,
498                "v6.2.0": True,
499                "v6.2.3": True,
500                "v6.2.5": True,
501                "v6.2.7": True,
502                "v6.0.11": True
503            }
504        },
505        "sslvpn": {
506            "type": "integer",
507            "revisions": {
508                "v6.0.0": True,
509                "v7.0.0": True,
510                "v6.0.5": True,
511                "v6.4.4": True,
512                "v6.4.0": True,
513                "v6.4.1": True,
514                "v6.2.0": True,
515                "v6.2.3": True,
516                "v6.2.5": True,
517                "v6.2.7": True,
518                "v6.0.11": True
519            }
520        },
521        "onetime_schedule": {
522            "type": "integer",
523            "revisions": {
524                "v6.0.0": True,
525                "v7.0.0": True,
526                "v6.0.5": True,
527                "v6.4.4": True,
528                "v6.4.0": True,
529                "v6.4.1": True,
530                "v6.2.0": True,
531                "v6.2.3": True,
532                "v6.2.5": True,
533                "v6.2.7": True,
534                "v6.0.11": True
535            }
536        },
537        "firewall_addrgrp": {
538            "type": "integer",
539            "revisions": {
540                "v6.0.0": True,
541                "v7.0.0": True,
542                "v6.0.5": True,
543                "v6.4.4": True,
544                "v6.4.0": True,
545                "v6.4.1": True,
546                "v6.2.0": True,
547                "v6.2.3": True,
548                "v6.2.5": True,
549                "v6.2.7": True,
550                "v6.0.11": True
551            }
552        },
553        "proxy": {
554            "type": "integer",
555            "revisions": {
556                "v6.0.0": True,
557                "v7.0.0": True,
558                "v6.0.5": True,
559                "v6.4.4": True,
560                "v6.4.0": True,
561                "v6.4.1": True,
562                "v6.2.0": True,
563                "v6.2.3": True,
564                "v6.2.5": True,
565                "v6.2.7": True,
566                "v6.0.11": True
567            }
568        },
569        "recurring_schedule": {
570            "type": "integer",
571            "revisions": {
572                "v6.0.0": True,
573                "v7.0.0": True,
574                "v6.0.5": True,
575                "v6.4.4": True,
576                "v6.4.0": True,
577                "v6.4.1": True,
578                "v6.2.0": True,
579                "v6.2.3": True,
580                "v6.2.5": True,
581                "v6.2.7": True,
582                "v6.0.11": True
583            }
584        },
585        "log_disk_quota": {
586            "type": "integer",
587            "revisions": {
588                "v6.0.0": True,
589                "v7.0.0": True,
590                "v6.0.5": True,
591                "v6.4.4": True,
592                "v6.4.0": True,
593                "v6.4.1": True,
594                "v6.2.0": True,
595                "v6.2.3": True,
596                "v6.2.5": True,
597                "v6.2.7": True,
598                "v6.0.11": True
599            }
600        }
601    },
602    "revisions": {
603        "v6.0.0": True,
604        "v7.0.0": True,
605        "v6.0.5": True,
606        "v6.4.4": True,
607        "v6.4.0": True,
608        "v6.4.1": True,
609        "v6.2.0": True,
610        "v6.2.3": True,
611        "v6.2.5": True,
612        "v6.2.7": True,
613        "v6.0.11": True
614    }
615}
616
617
618def main():
619    module_spec = schema_to_module_spec(versioned_schema)
620    mkeyname = None
621    fields = {
622        "access_token": {"required": False, "type": "str", "no_log": True},
623        "enable_log": {"required": False, "type": bool},
624        "vdom": {"required": False, "type": "str", "default": "root"},
625        "system_resource_limits": {
626            "required": False, "type": "dict", "default": None,
627            "options": {
628            }
629        }
630    }
631    for attribute_name in module_spec['options']:
632        fields["system_resource_limits"]['options'][attribute_name] = module_spec['options'][attribute_name]
633        if mkeyname and mkeyname == attribute_name:
634            fields["system_resource_limits"]['options'][attribute_name]['required'] = True
635
636    check_legacy_fortiosapi()
637    module = AnsibleModule(argument_spec=fields,
638                           supports_check_mode=False)
639
640    versions_check_result = None
641    if module._socket_path:
642        connection = Connection(module._socket_path)
643        if 'access_token' in module.params:
644            connection.set_option('access_token', module.params['access_token'])
645
646        if 'enable_log' in module.params:
647            connection.set_option('enable_log', module.params['enable_log'])
648        else:
649            connection.set_option('enable_log', False)
650        fos = FortiOSHandler(connection, module, mkeyname)
651        versions_check_result = check_schema_versioning(fos, versioned_schema, "system_resource_limits")
652
653        is_error, has_changed, result = fortios_system(module.params, fos)
654
655    else:
656        module.fail_json(**FAIL_SOCKET_MSG)
657
658    if versions_check_result and versions_check_result['matched'] is False:
659        module.warn("Ansible has detected version mismatch between FortOS system and your playbook, see more details by specifying option -vvv")
660
661    if not is_error:
662        if versions_check_result and versions_check_result['matched'] is False:
663            module.exit_json(changed=has_changed, version_check_warning=versions_check_result, meta=result)
664        else:
665            module.exit_json(changed=has_changed, meta=result)
666    else:
667        if versions_check_result and versions_check_result['matched'] is False:
668            module.fail_json(msg="Error in repo", version_check_warning=versions_check_result, meta=result)
669        else:
670            module.fail_json(msg="Error in repo", meta=result)
671
672
673if __name__ == '__main__':
674    main()
675