1#!/usr/local/bin/python3.8 2# 3# This file is part of Ansible 4# 5# Ansible 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# Ansible 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 Ansible. If not, see <http://www.gnu.org/licenses/>. 17# 18from __future__ import (absolute_import, division, print_function) 19__metaclass__ = type 20 21DOCUMENTATION = ''' 22--- 23module: ce_netstream_aging 24short_description: Manages timeout mode of NetStream on HUAWEI CloudEngine switches. 25description: 26 - Manages timeout mode of NetStream on HUAWEI CloudEngine switches. 27author: YangYang (@QijunPan) 28notes: 29 - Recommended connection is C(network_cli). 30 - This module also works with C(local) connections for legacy playbooks. 31options: 32 timeout_interval: 33 description: 34 - Netstream timeout interval. 35 If is active type the interval is 1-60. 36 If is inactive ,the interval is 5-600. 37 default: 30 38 type: 39 description: 40 - Specifies the packet type of netstream timeout active interval. 41 choices: ['ip', 'vxlan'] 42 state: 43 description: 44 - Specify desired state of the resource. 45 choices: ['present', 'absent'] 46 default: present 47 timeout_type: 48 description: 49 - Netstream timeout type. 50 choices: ['active', 'inactive', 'tcp-session', 'manual'] 51 manual_slot: 52 description: 53 - Specifies the slot number of netstream manual timeout. 54''' 55 56EXAMPLES = ''' 57- name: Netstream aging module test 58 hosts: cloudengine 59 connection: local 60 gather_facts: no 61 vars: 62 cli: 63 host: "{{ inventory_hostname }}" 64 port: "{{ ansible_ssh_port }}" 65 username: "{{ username }}" 66 password: "{{ password }}" 67 transport: cli 68 69 tasks: 70 71 - name: Configure netstream ip timeout active interval , the interval is 40 minutes. 72 community.network.ce_netstream_aging: 73 timeout_interval: 40 74 type: ip 75 timeout_type: active 76 state: present 77 provider: "{{ cli }}" 78 79 - name: Configure netstream vxlan timeout active interval , the interval is 40 minutes. 80 community.network.ce_netstream_aging: 81 timeout_interval: 40 82 type: vxlan 83 timeout_type: active 84 active_state: present 85 provider: "{{ cli }}" 86 87 - name: Delete netstream ip timeout active interval , set the ip timeout interval to 30 minutes. 88 community.network.ce_netstream_aging: 89 type: ip 90 timeout_type: active 91 state: absent 92 provider: "{{ cli }}" 93 94 - name: Delete netstream vxlan timeout active interval , set the vxlan timeout interval to 30 minutes. 95 community.network.ce_netstream_aging: 96 type: vxlan 97 timeout_type: active 98 state: absent 99 provider: "{{ cli }}" 100 101 - name: Enable netstream ip tcp session timeout. 102 community.network.ce_netstream_aging: 103 type: ip 104 timeout_type: tcp-session 105 state: present 106 provider: "{{ cli }}" 107 108 - name: Enable netstream vxlan tcp session timeout. 109 community.network.ce_netstream_aging: 110 type: vxlan 111 timeout_type: tcp-session 112 state: present 113 provider: "{{ cli }}" 114 115 - name: Disable netstream ip tcp session timeout. 116 community.network.ce_netstream_aging: 117 type: ip 118 timeout_type: tcp-session 119 state: absent 120 provider: "{{ cli }}" 121 122 - name: Disable netstream vxlan tcp session timeout. 123 community.network.ce_netstream_aging: 124 type: vxlan 125 timeout_type: tcp-session 126 state: absent 127 provider: "{{ cli }}" 128''' 129 130RETURN = ''' 131proposed: 132 description: k/v pairs of parameters passed into module 133 returned: verbose mode 134 type: dict 135 sample: {"timeout_interval": "40", 136 "type": "ip", 137 "state": "absent", 138 "timeout_type": active} 139existing: 140 description: k/v pairs of existing configuration 141 returned: verbose mode 142 type: dict 143 sample: {"active_timeout": [ 144 { 145 "ip": "40", 146 "vxlan": 30 147 } 148 ], 149 "inactive_timeout": [ 150 { 151 "ip": 30, 152 "vxlan": 30 153 } 154 ], 155 "tcp_timeout": [ 156 { 157 "ip": "disable", 158 "vxlan": "disable" 159 } 160 ]} 161end_state: 162 description: k/v pairs of configuration after module execution 163 returned: verbose mode 164 type: dict 165 sample: {"active_timeout": [ 166 { 167 "ip": 30, 168 "vxlan": 30 169 } 170 ], 171 "inactive_timeout": [ 172 { 173 "ip": 30, 174 "vxlan": 30 175 } 176 ], 177 "tcp_timeout": [ 178 { 179 "ip": "disable", 180 "vxlan": "disable" 181 } 182 ]} 183updates: 184 description: commands sent to the device 185 returned: always 186 type: list 187 sample: ["undo netstream timeout ip active 40"] 188changed: 189 description: check to see if a change was made on the device 190 returned: always 191 type: bool 192 sample: true 193''' 194 195import re 196from ansible.module_utils.basic import AnsibleModule 197from ansible_collections.community.network.plugins.module_utils.network.cloudengine.ce import exec_command, load_config 198from ansible_collections.community.network.plugins.module_utils.network.cloudengine.ce import ce_argument_spec 199 200 201class NetStreamAging(object): 202 """ 203 Manages netstream aging. 204 """ 205 206 def __init__(self, argument_spec): 207 self.spec = argument_spec 208 self.module = None 209 self.init_module() 210 211 # module input info 212 self.timeout_interval = self.module.params['timeout_interval'] 213 self.type = self.module.params['type'] 214 self.state = self.module.params['state'] 215 self.timeout_type = self.module.params['timeout_type'] 216 self.manual_slot = self.module.params['manual_slot'] 217 218 # host info 219 self.host = self.module.params['host'] 220 self.username = self.module.params['username'] 221 self.port = self.module.params['port'] 222 223 # state 224 self.changed = False 225 self.updates_cmd = list() 226 self.commands = list() 227 self.results = dict() 228 self.proposed = dict() 229 self.existing = dict() 230 self.end_state = dict() 231 232 # local parameters 233 self.existing["active_timeout"] = list() 234 self.existing["inactive_timeout"] = list() 235 self.existing["tcp_timeout"] = list() 236 self.end_state["active_timeout"] = list() 237 self.end_state["inactive_timeout"] = list() 238 self.end_state["tcp_timeout"] = list() 239 self.active_changed = False 240 self.inactive_changed = False 241 self.tcp_changed = False 242 243 def init_module(self): 244 """init module""" 245 246 self.module = AnsibleModule(argument_spec=self.spec, supports_check_mode=True) 247 248 def cli_load_config(self, commands): 249 """load config by cli""" 250 251 if not self.module.check_mode: 252 load_config(self.module, commands) 253 254 def cli_add_command(self, command, undo=False): 255 """add command to self.update_cmd and self.commands""" 256 257 if undo and command.lower() not in ["quit", "return"]: 258 cmd = "undo " + command 259 else: 260 cmd = command 261 262 self.commands.append(cmd) 263 if command.lower() not in ["quit", "return"]: 264 self.updates_cmd.append(cmd) 265 266 def get_exist_timer_out_para(self): 267 """Get exist netstream timeout parameters""" 268 269 active_tmp = dict() 270 inactive_tmp = dict() 271 tcp_tmp = dict() 272 active_tmp["ip"] = "30" 273 active_tmp["vxlan"] = "30" 274 inactive_tmp["ip"] = "30" 275 inactive_tmp["vxlan"] = "30" 276 tcp_tmp["ip"] = "absent" 277 tcp_tmp["vxlan"] = "absent" 278 279 cmd = "display current-configuration | include ^netstream timeout" 280 rc, out, err = exec_command(self.module, cmd) 281 if rc != 0: 282 self.module.fail_json(msg=err) 283 config = str(out).strip() 284 if config: 285 config = config.lstrip() 286 config_list = config.split('\n') 287 for config_mem in config_list: 288 config_mem = config_mem.lstrip() 289 config_mem_list = config_mem.split(' ') 290 if len(config_mem_list) > 4 and config_mem_list[2] == "ip": 291 if config_mem_list[3] == "active": 292 active_tmp["ip"] = config_mem_list[4] 293 if config_mem_list[3] == "inactive": 294 inactive_tmp["ip"] = config_mem_list[4] 295 if config_mem_list[3] == "tcp-session": 296 tcp_tmp["ip"] = "present" 297 if len(config_mem_list) > 4 and config_mem_list[2] == "vxlan": 298 if config_mem_list[4] == "active": 299 active_tmp["vxlan"] = config_mem_list[5] 300 if config_mem_list[4] == "inactive": 301 inactive_tmp["vxlan"] = config_mem_list[5] 302 if config_mem_list[4] == "tcp-session": 303 tcp_tmp["vxlan"] = "present" 304 self.existing["active_timeout"].append(active_tmp) 305 self.existing["inactive_timeout"].append(inactive_tmp) 306 self.existing["tcp_timeout"].append(tcp_tmp) 307 308 def get_end_timer_out_para(self): 309 """Get end netstream timeout parameters""" 310 311 active_tmp = dict() 312 inactive_tmp = dict() 313 tcp_tmp = dict() 314 active_tmp["ip"] = "30" 315 active_tmp["vxlan"] = "30" 316 inactive_tmp["ip"] = "30" 317 inactive_tmp["vxlan"] = "30" 318 tcp_tmp["ip"] = "absent" 319 tcp_tmp["vxlan"] = "absent" 320 cmd = "display current-configuration | include ^netstream timeout" 321 rc, out, err = exec_command(self.module, cmd) 322 if rc != 0: 323 self.module.fail_json(msg=err) 324 config = str(out).strip() 325 if config: 326 config = config.lstrip() 327 config_list = config.split('\n') 328 for config_mem in config_list: 329 config_mem = config_mem.lstrip() 330 config_mem_list = config_mem.split(' ') 331 if len(config_mem_list) > 4 and config_mem_list[2] == "ip": 332 if config_mem_list[3] == "active": 333 active_tmp["ip"] = config_mem_list[4] 334 if config_mem_list[3] == "inactive": 335 inactive_tmp["ip"] = config_mem_list[4] 336 if config_mem_list[3] == "tcp-session": 337 tcp_tmp["ip"] = "present" 338 if len(config_mem_list) > 4 and config_mem_list[2] == "vxlan": 339 if config_mem_list[4] == "active": 340 active_tmp["vxlan"] = config_mem_list[5] 341 if config_mem_list[4] == "inactive": 342 inactive_tmp["vxlan"] = config_mem_list[5] 343 if config_mem_list[4] == "tcp-session": 344 tcp_tmp["vxlan"] = "present" 345 self.end_state["active_timeout"].append(active_tmp) 346 self.end_state["inactive_timeout"].append(inactive_tmp) 347 self.end_state["tcp_timeout"].append(tcp_tmp) 348 349 def check_params(self): 350 """Check all input params""" 351 352 # interval check 353 if not str(self.timeout_interval).isdigit(): 354 self.module.fail_json( 355 msg='Error: Timeout interval should be numerical.') 356 if self.timeout_type == "active": 357 if int(self.timeout_interval) < 1 or int(self.timeout_interval) > 60: 358 self.module.fail_json( 359 msg="Error: Active interval should between 1 - 60 minutes.") 360 if self.timeout_type == "inactive": 361 if int(self.timeout_interval) < 5 or int(self.timeout_interval) > 600: 362 self.module.fail_json( 363 msg="Error: Inactive interval should between 5 - 600 seconds.") 364 if self.timeout_type == "manual": 365 if not self.manual_slot: 366 self.module.fail_json( 367 msg="Error: If use manual timeout mode,slot number is needed.") 368 if re.match(r'^\d+(\/\d*)?$', self.manual_slot) is None: 369 self.module.fail_json( 370 msg='Error: Slot number should be numerical.') 371 372 def get_proposed(self): 373 """get proposed info""" 374 375 if self.timeout_interval: 376 self.proposed["timeout_interval"] = self.timeout_interval 377 if self.timeout_type: 378 self.proposed["timeout_type"] = self.timeout_type 379 if self.type: 380 self.proposed["type"] = self.type 381 if self.state: 382 self.proposed["state"] = self.state 383 if self.manual_slot: 384 self.proposed["manual_slot"] = self.manual_slot 385 386 def get_existing(self): 387 """get existing info""" 388 active_tmp = dict() 389 inactive_tmp = dict() 390 tcp_tmp = dict() 391 392 self.get_exist_timer_out_para() 393 394 if self.timeout_type == "active": 395 for active_tmp in self.existing["active_timeout"]: 396 if self.state == "present": 397 if str(active_tmp[self.type]) != self.timeout_interval: 398 self.active_changed = True 399 else: 400 if self.timeout_interval != "30": 401 if str(active_tmp[self.type]) != "30": 402 if str(active_tmp[self.type]) != self.timeout_interval: 403 self.module.fail_json( 404 msg='Error: The specified active interval do not exist.') 405 if str(active_tmp[self.type]) != "30": 406 self.timeout_interval = active_tmp[self.type] 407 self.active_changed = True 408 if self.timeout_type == "inactive": 409 for inactive_tmp in self.existing["inactive_timeout"]: 410 if self.state == "present": 411 if str(inactive_tmp[self.type]) != self.timeout_interval: 412 self.inactive_changed = True 413 else: 414 if self.timeout_interval != "30": 415 if str(inactive_tmp[self.type]) != "30": 416 if str(inactive_tmp[self.type]) != self.timeout_interval: 417 self.module.fail_json( 418 msg='Error: The specified inactive interval do not exist.') 419 if str(inactive_tmp[self.type]) != "30": 420 self.timeout_interval = inactive_tmp[self.type] 421 self.inactive_changed = True 422 if self.timeout_type == "tcp-session": 423 for tcp_tmp in self.existing["tcp_timeout"]: 424 if str(tcp_tmp[self.type]) != self.state: 425 self.tcp_changed = True 426 427 def operate_time_out(self): 428 """configure timeout parameters""" 429 430 cmd = "" 431 if self.timeout_type == "manual": 432 if self.type == "ip": 433 self.cli_add_command("quit") 434 cmd = "reset netstream cache ip slot %s" % self.manual_slot 435 self.cli_add_command(cmd) 436 elif self.type == "vxlan": 437 self.cli_add_command("quit") 438 cmd = "reset netstream cache vxlan inner-ip slot %s" % self.manual_slot 439 self.cli_add_command(cmd) 440 441 if not self.active_changed and not self.inactive_changed and not self.tcp_changed: 442 if self.commands: 443 self.cli_load_config(self.commands) 444 self.changed = True 445 return 446 447 if self.active_changed or self.inactive_changed: 448 if self.type == "ip": 449 cmd = "netstream timeout ip %s %s" % (self.timeout_type, self.timeout_interval) 450 elif self.type == "vxlan": 451 cmd = "netstream timeout vxlan inner-ip %s %s" % (self.timeout_type, self.timeout_interval) 452 if self.state == "absent": 453 self.cli_add_command(cmd, undo=True) 454 else: 455 self.cli_add_command(cmd) 456 if self.timeout_type == "tcp-session" and self.tcp_changed: 457 if self.type == "ip": 458 if self.state == "present": 459 cmd = "netstream timeout ip tcp-session" 460 else: 461 cmd = "undo netstream timeout ip tcp-session" 462 463 elif self.type == "vxlan": 464 if self.state == "present": 465 cmd = "netstream timeout vxlan inner-ip tcp-session" 466 else: 467 cmd = "undo netstream timeout vxlan inner-ip tcp-session" 468 self.cli_add_command(cmd) 469 if self.commands: 470 self.cli_load_config(self.commands) 471 self.changed = True 472 473 def get_end_state(self): 474 """get end state info""" 475 476 self.get_end_timer_out_para() 477 478 def work(self): 479 """worker""" 480 481 self.check_params() 482 self.get_existing() 483 self.get_proposed() 484 self.operate_time_out() 485 self.get_end_state() 486 if self.existing == self.end_state: 487 self.changed = False 488 self.results['changed'] = self.changed 489 self.results['proposed'] = self.proposed 490 self.results['existing'] = self.existing 491 self.results['end_state'] = self.end_state 492 if self.changed: 493 self.results['updates'] = self.updates_cmd 494 else: 495 self.results['updates'] = list() 496 497 self.module.exit_json(**self.results) 498 499 500def main(): 501 """Module main""" 502 503 argument_spec = dict( 504 timeout_interval=dict(required=False, type='str', default='30'), 505 type=dict(required=False, choices=['ip', 'vxlan']), 506 state=dict(required=False, choices=['present', 'absent'], default='present'), 507 timeout_type=dict(required=False, choices=['active', 'inactive', 'tcp-session', 'manual']), 508 manual_slot=dict(required=False, type='str'), 509 ) 510 argument_spec.update(ce_argument_spec) 511 module = NetStreamAging(argument_spec) 512 module.work() 513 514 515if __name__ == '__main__': 516 main() 517