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