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_dpdk_cpus
27short_description: Configure CPUs enabled to run engines in each DPDK stage 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 dpdk feature and cpus 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    dpdk_cpus:
68        description:
69            - Configure CPUs enabled to run engines in each DPDK stage.
70        default: null
71        type: dict
72        suboptions:
73            ips_cpus:
74                description:
75                    - CPUs enabled to run DPDK IPS engines.
76                type: str
77            rx_cpus:
78                description:
79                    - CPUs enabled to run DPDK RX engines.
80                type: str
81            tx_cpus:
82                description:
83                    - CPUs enabled to run DPDK TX engines.
84                type: str
85            vnp_cpus:
86                description:
87                    - CPUs enabled to run DPDK VNP engines.
88                type: str
89'''
90
91EXAMPLES = '''
92- hosts: fortigates
93  collections:
94    - fortinet.fortios
95  connection: httpapi
96  vars:
97   vdom: "root"
98   ansible_httpapi_use_ssl: yes
99   ansible_httpapi_validate_certs: no
100   ansible_httpapi_port: 443
101  tasks:
102  - name: Configure CPUs enabled to run engines in each DPDK stage.
103    fortios_dpdk_cpus:
104      vdom:  "{{ vdom }}"
105      dpdk_cpus:
106        ips_cpus: "<your_own_value>"
107        rx_cpus: "<your_own_value>"
108        tx_cpus: "<your_own_value>"
109        vnp_cpus: "<your_own_value>"
110
111'''
112
113RETURN = '''
114build:
115  description: Build number of the fortigate image
116  returned: always
117  type: str
118  sample: '1547'
119http_method:
120  description: Last method used to provision the content into FortiGate
121  returned: always
122  type: str
123  sample: 'PUT'
124http_status:
125  description: Last result given by FortiGate on last operation applied
126  returned: always
127  type: str
128  sample: "200"
129mkey:
130  description: Master key (id) used in the last call to FortiGate
131  returned: success
132  type: str
133  sample: "id"
134name:
135  description: Name of the table used to fulfill the request
136  returned: always
137  type: str
138  sample: "urlfilter"
139path:
140  description: Path of the table used to fulfill the request
141  returned: always
142  type: str
143  sample: "webfilter"
144revision:
145  description: Internal revision number
146  returned: always
147  type: str
148  sample: "17.0.2.10658"
149serial:
150  description: Serial number of the unit
151  returned: always
152  type: str
153  sample: "FGVMEVYYQT3AB5352"
154status:
155  description: Indication of the operation's result
156  returned: always
157  type: str
158  sample: "success"
159vdom:
160  description: Virtual domain used
161  returned: always
162  type: str
163  sample: "root"
164version:
165  description: Version of the FortiGate
166  returned: always
167  type: str
168  sample: "v5.6.3"
169
170'''
171from ansible.module_utils.basic import AnsibleModule
172from ansible.module_utils.connection import Connection
173from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import FortiOSHandler
174from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_legacy_fortiosapi
175from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import schema_to_module_spec
176from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_schema_versioning
177from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG
178from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import is_same_comparison
179from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import serialize
180
181
182def filter_dpdk_cpus_data(json):
183    option_list = ['ips_cpus', 'rx_cpus', 'tx_cpus',
184                   'vnp_cpus']
185    dictionary = {}
186
187    for attribute in option_list:
188        if attribute in json and json[attribute] is not None:
189            dictionary[attribute] = json[attribute]
190
191    return dictionary
192
193
194def underscore_to_hyphen(data):
195    if isinstance(data, list):
196        for i, elem in enumerate(data):
197            data[i] = underscore_to_hyphen(elem)
198    elif isinstance(data, dict):
199        new_data = {}
200        for k, v in data.items():
201            new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
202        data = new_data
203
204    return data
205
206
207def dpdk_cpus(data, fos):
208    vdom = data['vdom']
209    dpdk_cpus_data = data['dpdk_cpus']
210    filtered_data = underscore_to_hyphen(filter_dpdk_cpus_data(dpdk_cpus_data))
211
212    return fos.set('dpdk',
213                   'cpus',
214                   data=filtered_data,
215                   vdom=vdom)
216
217
218def is_successful_status(status):
219    return status['status'] == "success" or \
220        status['http_method'] == "DELETE" and status['http_status'] == 404
221
222
223def fortios_dpdk(data, fos):
224
225    if data['dpdk_cpus']:
226        resp = dpdk_cpus(data, fos)
227    else:
228        fos._module.fail_json(msg='missing task body: %s' % ('dpdk_cpus'))
229
230    return not is_successful_status(resp), \
231        resp['status'] == "success" and \
232        (resp['revision_changed'] if 'revision_changed' in resp else True), \
233        resp
234
235
236versioned_schema = {
237    "type": "dict",
238    "children": {
239        "tx_cpus": {
240            "type": "string",
241            "revisions": {
242                "v7.0.0": True
243            }
244        },
245        "ips_cpus": {
246            "type": "string",
247            "revisions": {
248                "v7.0.0": True
249            }
250        },
251        "vnp_cpus": {
252            "type": "string",
253            "revisions": {
254                "v7.0.0": True
255            }
256        },
257        "rx_cpus": {
258            "type": "string",
259            "revisions": {
260                "v7.0.0": True
261            }
262        }
263    },
264    "revisions": {
265        "v7.0.0": True
266    }
267}
268
269
270def main():
271    module_spec = schema_to_module_spec(versioned_schema)
272    mkeyname = None
273    fields = {
274        "access_token": {"required": False, "type": "str", "no_log": True},
275        "enable_log": {"required": False, "type": bool},
276        "vdom": {"required": False, "type": "str", "default": "root"},
277        "dpdk_cpus": {
278            "required": False, "type": "dict", "default": None,
279            "options": {
280            }
281        }
282    }
283    for attribute_name in module_spec['options']:
284        fields["dpdk_cpus"]['options'][attribute_name] = module_spec['options'][attribute_name]
285        if mkeyname and mkeyname == attribute_name:
286            fields["dpdk_cpus"]['options'][attribute_name]['required'] = True
287
288    check_legacy_fortiosapi()
289    module = AnsibleModule(argument_spec=fields,
290                           supports_check_mode=False)
291
292    versions_check_result = None
293    if module._socket_path:
294        connection = Connection(module._socket_path)
295        if 'access_token' in module.params:
296            connection.set_option('access_token', module.params['access_token'])
297
298        if 'enable_log' in module.params:
299            connection.set_option('enable_log', module.params['enable_log'])
300        else:
301            connection.set_option('enable_log', False)
302        fos = FortiOSHandler(connection, module, mkeyname)
303        versions_check_result = check_schema_versioning(fos, versioned_schema, "dpdk_cpus")
304
305        is_error, has_changed, result = fortios_dpdk(module.params, fos)
306
307    else:
308        module.fail_json(**FAIL_SOCKET_MSG)
309
310    if versions_check_result and versions_check_result['matched'] is False:
311        module.warn("Ansible has detected version mismatch between FortOS system and your playbook, see more details by specifying option -vvv")
312
313    if not is_error:
314        if versions_check_result and versions_check_result['matched'] is False:
315            module.exit_json(changed=has_changed, version_check_warning=versions_check_result, meta=result)
316        else:
317            module.exit_json(changed=has_changed, meta=result)
318    else:
319        if versions_check_result and versions_check_result['matched'] is False:
320            module.fail_json(msg="Error in repo", version_check_warning=versions_check_result, meta=result)
321        else:
322            module.fail_json(msg="Error in repo", meta=result)
323
324
325if __name__ == '__main__':
326    main()
327