1#!/usr/bin/python 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# 18 19ANSIBLE_METADATA = {'metadata_version': '1.1', 20 'status': ['preview'], 21 'supported_by': 'community'} 22 23DOCUMENTATION = """ 24--- 25module: ce_vxlan_gateway 26version_added: "2.4" 27short_description: Manages gateway for the VXLAN network on HUAWEI CloudEngine devices. 28description: 29 - Configuring Centralized All-Active Gateways or Distributed Gateway for 30 the VXLAN Network on HUAWEI CloudEngine devices. 31author: QijunPan (@QijunPan) 32notes: 33 - Ensure All-Active Gateways or Distributed Gateway for the VXLAN Network can not configure at the same time. 34 - Recommended connection is C(network_cli). 35 - This module also works with C(local) connections for legacy playbooks. 36options: 37 dfs_id: 38 description: 39 - Specifies the ID of a DFS group. 40 The value must be 1. 41 dfs_source_ip: 42 description: 43 - Specifies the IPv4 address bound to a DFS group. 44 The value is in dotted decimal notation. 45 dfs_source_vpn: 46 description: 47 - Specifies the name of a VPN instance bound to a DFS group. 48 The value is a string of 1 to 31 case-sensitive characters without spaces. 49 If the character string is quoted by double quotation marks, the character string can contain spaces. 50 The value C(_public_) is reserved and cannot be used as the VPN instance name. 51 dfs_udp_port: 52 description: 53 - Specifies the UDP port number of the DFS group. 54 The value is an integer that ranges from 1025 to 65535. 55 dfs_all_active: 56 description: 57 - Creates all-active gateways. 58 choices: ['enable', 'disable'] 59 dfs_peer_ip: 60 description: 61 - Configure the IP address of an all-active gateway peer. 62 The value is in dotted decimal notation. 63 dfs_peer_vpn: 64 description: 65 - Specifies the name of the VPN instance that is associated with all-active gateway peer. 66 The value is a string of 1 to 31 case-sensitive characters, spaces not supported. 67 When double quotation marks are used around the string, spaces are allowed in the string. 68 The value C(_public_) is reserved and cannot be used as the VPN instance name. 69 vpn_instance: 70 description: 71 - Specifies the name of a VPN instance. 72 The value is a string of 1 to 31 case-sensitive characters, spaces not supported. 73 When double quotation marks are used around the string, spaces are allowed in the string. 74 The value C(_public_) is reserved and cannot be used as the VPN instance name. 75 vpn_vni: 76 description: 77 - Specifies a VNI ID. 78 Binds a VXLAN network identifier (VNI) to a virtual private network (VPN) instance. 79 The value is an integer ranging from 1 to 16000000. 80 vbdif_name: 81 description: 82 - Full name of VBDIF interface, i.e. Vbdif100. 83 vbdif_bind_vpn: 84 description: 85 - Specifies the name of the VPN instance that is associated with the interface. 86 The value is a string of 1 to 31 case-sensitive characters, spaces not supported. 87 When double quotation marks are used around the string, spaces are allowed in the string. 88 The value C(_public_) is reserved and cannot be used as the VPN instance name. 89 vbdif_mac: 90 description: 91 - Specifies a MAC address for a VBDIF interface. 92 The value is in the format of H-H-H. Each H is a 4-digit hexadecimal number, such as C(00e0) or C(fc01). 93 If an H contains less than four digits, 0s are added ahead. For example, C(e0) is equal to C(00e0). 94 A MAC address cannot be all 0s or 1s or a multicast MAC address. 95 arp_distribute_gateway: 96 description: 97 - Enable the distributed gateway function on VBDIF interface. 98 choices: ['enable','disable'] 99 arp_direct_route: 100 description: 101 - Enable VLINK direct route on VBDIF interface. 102 choices: ['enable','disable'] 103 state: 104 description: 105 - Determines whether the config should be present or not 106 on the device. 107 default: present 108 choices: ['present', 'absent'] 109""" 110 111EXAMPLES = ''' 112- name: vxlan gateway module test 113 hosts: ce128 114 connection: local 115 gather_facts: no 116 vars: 117 cli: 118 host: "{{ inventory_hostname }}" 119 port: "{{ ansible_ssh_port }}" 120 username: "{{ username }}" 121 password: "{{ password }}" 122 transport: cli 123 124 tasks: 125 126 - name: Configuring Centralized All-Active Gateways for the VXLAN Network 127 ce_vxlan_gateway: 128 dfs_id: 1 129 dfs_source_ip: 6.6.6.6 130 dfs_all_active: enable 131 dfs_peer_ip: 7.7.7.7 132 provider: "{{ cli }}" 133 - name: Bind the VPN instance to a Layer 3 gateway, enable distributed gateway, and configure host route advertisement. 134 ce_vxlan_gateway: 135 vbdif_name: Vbdif100 136 vbdif_bind_vpn: vpn1 137 arp_distribute_gateway: enable 138 arp_direct_route: enable 139 provider: "{{ cli }}" 140 - name: Assign a VNI to a VPN instance. 141 ce_vxlan_gateway: 142 vpn_instance: vpn1 143 vpn_vni: 100 144 provider: "{{ cli }}" 145''' 146 147RETURN = ''' 148proposed: 149 description: k/v pairs of parameters passed into module 150 returned: verbose mode 151 type: dict 152 sample: {"dfs_id": "1", "dfs_source_ip": "6.6.6.6", "dfs_all_active":"enable", "dfs_peer_ip": "7.7.7.7"} 153existing: 154 description: k/v pairs of existing configuration 155 returned: verbose mode 156 type: dict 157 sample: {"dfs_id": "1", "dfs_source_ip": null, "evn_peer_ip": [], "dfs_all_active": "disable"} 158end_state: 159 description: k/v pairs of configuration after module execution 160 returned: verbose mode 161 type: dict 162 sample: {"dfs_id": "1", "evn_source_ip": "6.6.6.6", "evn_source_vpn": null, 163 "evn_peers": [{"ip": "7.7.7.7", "vpn": ""}], "dfs_all_active": "enable"} 164updates: 165 description: commands sent to the device 166 returned: always 167 type: list 168 sample: ["dfs-group 1", 169 "source ip 6.6.6.6", 170 "active-active-gateway", 171 "peer 7.7.7.7"] 172changed: 173 description: check to see if a change was made on the device 174 returned: always 175 type: bool 176 sample: true 177''' 178 179import re 180from ansible.module_utils.basic import AnsibleModule 181from ansible.module_utils.network.cloudengine.ce import load_config 182from ansible.module_utils.network.cloudengine.ce import ce_argument_spec 183from ansible.module_utils.connection import exec_command 184 185 186def is_config_exist(cmp_cfg, test_cfg): 187 """is configuration exist?""" 188 189 if not cmp_cfg or not test_cfg: 190 return False 191 192 return bool(test_cfg in cmp_cfg) 193 194 195def is_valid_v4addr(addr): 196 """check is ipv4 addr""" 197 198 if not addr: 199 return False 200 201 if addr.count('.') == 3: 202 addr_list = addr.split('.') 203 if len(addr_list) != 4: 204 return False 205 for each_num in addr_list: 206 if not each_num.isdigit(): 207 return False 208 if int(each_num) > 255: 209 return False 210 return True 211 212 return False 213 214 215def mac_format(mac): 216 """convert mac format to xxxx-xxxx-xxxx""" 217 218 if not mac: 219 return None 220 221 if mac.count("-") != 2: 222 return None 223 224 addrs = mac.split("-") 225 for i in range(3): 226 if not addrs[i] or not addrs[i].isalnum(): 227 return None 228 if len(addrs[i]) < 1 or len(addrs[i]) > 4: 229 return None 230 try: 231 addrs[i] = int(addrs[i], 16) 232 except ValueError: 233 return None 234 235 try: 236 return "%04x-%04x-%04x" % (addrs[0], addrs[1], addrs[2]) 237 except ValueError: 238 return None 239 except TypeError: 240 return None 241 242 243def get_dfs_source_ip(config): 244 """get dfs source ip address""" 245 246 get = re.findall(r"source ip ([0-9]+.[0-9]+.[0-9]+.[0-9]+)", config) 247 if not get: 248 return None 249 else: 250 return get[0] 251 252 253def get_dfs_source_vpn(config): 254 """get dfs source ip vpn instance name""" 255 256 get = re.findall( 257 r"source ip [0-9]+.[0-9]+.[0-9]+.[0-9]+ vpn-instance (\S+)", config) 258 if not get: 259 return None 260 else: 261 return get[0] 262 263 264def get_dfs_udp_port(config): 265 """get dfs udp port""" 266 267 get = re.findall(r"udp port (\d+)", config) 268 if not get: 269 return None 270 else: 271 return get[0] 272 273 274def get_dfs_peers(config): 275 """get evn peer ip list""" 276 277 get = re.findall( 278 r"peer ([0-9]+.[0-9]+.[0-9]+.[0-9]+)\s?(vpn-instance)?\s?(\S*)", config) 279 if not get: 280 return None 281 else: 282 peers = list() 283 for item in get: 284 peers.append(dict(ip=item[0], vpn=item[2])) 285 return peers 286 287 288def get_ip_vpn(config): 289 """get ip vpn instance""" 290 291 get = re.findall(r"ip vpn-instance (\S+)", config) 292 if not get: 293 return None 294 else: 295 return get[0] 296 297 298def get_ip_vpn_vni(config): 299 """get ip vpn vxlan vni""" 300 301 get = re.findall(r"vxlan vni (\d+)", config) 302 if not get: 303 return None 304 else: 305 return get[0] 306 307 308def get_vbdif_vpn(config): 309 """get ip vpn name of interface vbdif""" 310 311 get = re.findall(r"ip binding vpn-instance (\S+)", config) 312 if not get: 313 return None 314 else: 315 return get[0] 316 317 318def get_vbdif_mac(config): 319 """get mac address of interface vbdif""" 320 321 get = re.findall( 322 r" mac-address ([0-9a-fA-F]{1,4}-[0-9a-fA-F]{1,4}-[0-9a-fA-F]{1,4})", config) 323 if not get: 324 return None 325 else: 326 return get[0] 327 328 329class VxlanGateway(object): 330 """ 331 Manages Gateway for the VXLAN Network. 332 """ 333 334 def __init__(self, argument_spec): 335 self.spec = argument_spec 336 self.module = None 337 self.init_module() 338 339 # module input info 340 self.dfs_id = self.module.params['dfs_id'] 341 self.dfs_source_ip = self.module.params['dfs_source_ip'] 342 self.dfs_source_vpn = self.module.params['dfs_source_vpn'] 343 self.dfs_udp_port = self.module.params['dfs_udp_port'] 344 self.dfs_all_active = self.module.params['dfs_all_active'] 345 self.dfs_peer_ip = self.module.params['dfs_peer_ip'] 346 self.dfs_peer_vpn = self.module.params['dfs_peer_vpn'] 347 self.vpn_instance = self.module.params['vpn_instance'] 348 self.vpn_vni = self.module.params['vpn_vni'] 349 self.vbdif_name = self.module.params['vbdif_name'] 350 self.vbdif_mac = self.module.params['vbdif_mac'] 351 self.vbdif_bind_vpn = self.module.params['vbdif_bind_vpn'] 352 self.arp_distribute_gateway = self.module.params['arp_distribute_gateway'] 353 self.arp_direct_route = self.module.params['arp_direct_route'] 354 self.state = self.module.params['state'] 355 356 # host info 357 self.host = self.module.params['host'] 358 self.username = self.module.params['username'] 359 self.port = self.module.params['port'] 360 361 # state 362 self.config = "" # current config 363 self.changed = False 364 self.updates_cmd = list() 365 self.commands = list() 366 self.results = dict() 367 self.proposed = dict() 368 self.existing = dict() 369 self.end_state = dict() 370 371 def init_module(self): 372 """init module""" 373 374 self.module = AnsibleModule( 375 argument_spec=self.spec, supports_check_mode=True) 376 377 def cli_load_config(self, commands): 378 """load config by cli""" 379 380 if not self.module.check_mode: 381 load_config(self.module, commands) 382 383 def get_config(self, flags=None): 384 """Retrieves the current config from the device or cache 385 """ 386 flags = [] if flags is None else flags 387 388 cmd = 'display current-configuration ' 389 cmd += ' '.join(flags) 390 cmd = cmd.strip() 391 392 rc, out, err = exec_command(self.module, cmd) 393 if rc != 0: 394 self.module.fail_json(msg=err) 395 cfg = str(out).strip() 396 397 return cfg 398 399 def get_current_config(self): 400 """get current configuration""" 401 402 flags = list() 403 exp = r" | ignore-case section include ^#\s+dfs-group" 404 if self.vpn_instance: 405 exp += r"|^#\s+ip vpn-instance %s" % self.vpn_instance 406 if self.vbdif_name: 407 exp += r"|^#\s+interface %s" % self.vbdif_name 408 flags.append(exp) 409 return self.get_config(flags) 410 411 def cli_add_command(self, command, undo=False): 412 """add command to self.update_cmd and self.commands""" 413 414 if undo and command.lower() not in ["quit", "return"]: 415 cmd = "undo " + command 416 else: 417 cmd = command 418 419 self.commands.append(cmd) # set to device 420 if command.lower() not in ["quit", "return"]: 421 self.updates_cmd.append(cmd) # show updates result 422 423 def config_dfs_group(self): 424 """manage Dynamic Fabric Service (DFS) group configuration""" 425 426 if not self.dfs_id: 427 return 428 429 dfs_view = False 430 view_cmd = "dfs-group %s" % self.dfs_id 431 exist = is_config_exist(self.config, view_cmd) 432 if self.state == "present" and not exist: 433 self.cli_add_command(view_cmd) 434 dfs_view = True 435 436 # undo dfs-group dfs-group-id 437 if self.state == "absent" and exist: 438 if not self.dfs_source_ip and not self.dfs_udp_port and not self.dfs_all_active and not self.dfs_peer_ip: 439 self.cli_add_command(view_cmd, undo=True) 440 return 441 442 # [undo] source ip ip-address [ vpn-instance vpn-instance-name ] 443 if self.dfs_source_ip: 444 cmd = "source ip %s" % self.dfs_source_ip 445 if self.dfs_source_vpn: 446 cmd += " vpn-instance %s" % self.dfs_source_vpn 447 exist = is_config_exist(self.config, cmd) 448 if self.state == "present" and not exist: 449 if not dfs_view: 450 self.cli_add_command(view_cmd) 451 dfs_view = True 452 self.cli_add_command(cmd) 453 if self.state == "absent" and exist: 454 if not dfs_view: 455 self.cli_add_command(view_cmd) 456 dfs_view = True 457 self.cli_add_command(cmd, undo=True) 458 459 # [undo] udp port port-number 460 if self.dfs_udp_port: 461 cmd = "udp port %s" % self.dfs_udp_port 462 exist = is_config_exist(self.config, cmd) 463 if self.state == "present" and not exist: 464 if not dfs_view: 465 self.cli_add_command(view_cmd) 466 dfs_view = True 467 self.cli_add_command(cmd) 468 elif self.state == "absent" and exist: 469 if not dfs_view: 470 self.cli_add_command(view_cmd) 471 dfs_view = True 472 self.cli_add_command(cmd, undo=True) 473 474 # [undo] active-active-gateway 475 # [undo]peer[ vpn-instance vpn-instance-name ] 476 aa_cmd = "active-active-gateway" 477 aa_exist = is_config_exist(self.config, aa_cmd) 478 aa_view = False 479 if self.dfs_all_active == "disable": 480 if aa_exist: 481 cmd = "peer %s" % self.dfs_peer_ip 482 if self.dfs_source_vpn: 483 cmd += " vpn-instance %s" % self.dfs_peer_vpn 484 exist = is_config_exist(self.config, cmd) 485 if self.state == "absent" and exist: 486 if not dfs_view: 487 self.cli_add_command(view_cmd) 488 dfs_view = True 489 self.cli_add_command(aa_cmd) 490 self.cli_add_command(cmd, undo=True) 491 self.cli_add_command("quit") 492 if not dfs_view: 493 self.cli_add_command(view_cmd) 494 dfs_view = True 495 self.cli_add_command(aa_cmd, undo=True) 496 elif self.dfs_all_active == "enable": 497 if not aa_exist: 498 if not dfs_view: 499 self.cli_add_command(view_cmd) 500 dfs_view = True 501 self.cli_add_command(aa_cmd) 502 aa_view = True 503 504 if self.dfs_peer_ip: 505 cmd = "peer %s" % self.dfs_peer_ip 506 if self.dfs_peer_vpn: 507 cmd += " vpn-instance %s" % self.dfs_peer_vpn 508 exist = is_config_exist(self.config, cmd) 509 510 if self.state == "present" and not exist: 511 if not dfs_view: 512 self.cli_add_command(view_cmd) 513 dfs_view = True 514 if not aa_view: 515 self.cli_add_command(aa_cmd) 516 self.cli_add_command(cmd) 517 self.cli_add_command("quit") 518 elif self.state == "absent" and exist: 519 if not dfs_view: 520 self.cli_add_command(view_cmd) 521 dfs_view = True 522 if not aa_view: 523 self.cli_add_command(aa_cmd) 524 self.cli_add_command(cmd, undo=True) 525 self.cli_add_command("quit") 526 else: # not input dfs_all_active 527 if aa_exist and self.dfs_peer_ip: 528 cmd = "peer %s" % self.dfs_peer_ip 529 if self.dfs_peer_vpn: 530 cmd += " vpn-instance %s" % self.dfs_peer_vpn 531 exist = is_config_exist(self.config, cmd) 532 if self.state == "present" and not exist: 533 if not dfs_view: 534 self.cli_add_command(view_cmd) 535 dfs_view = True 536 self.cli_add_command(aa_cmd) 537 self.cli_add_command(cmd) 538 self.cli_add_command("quit") 539 elif self.state == "absent" and exist: 540 if not dfs_view: 541 self.cli_add_command(view_cmd) 542 dfs_view = True 543 self.cli_add_command(aa_cmd) 544 self.cli_add_command(cmd, undo=True) 545 self.cli_add_command("quit") 546 else: 547 pass 548 elif not aa_exist and self.dfs_peer_ip and self.state == "present": 549 self.module.fail_json( 550 msg="Error: All-active gateways is not enable.") 551 else: 552 pass 553 554 if dfs_view: 555 self.cli_add_command("quit") 556 557 def config_ip_vpn(self): 558 """configure command at the ip vpn view""" 559 560 if not self.vpn_instance or not self.vpn_vni: 561 return 562 563 # ip vpn-instance vpn-instance-name 564 view_cmd = "ip vpn-instance %s" % self.vpn_instance 565 exist = is_config_exist(self.config, view_cmd) 566 if not exist: 567 self.module.fail_json( 568 msg="Error: ip vpn instance %s is not exist." % self.vpn_instance) 569 570 # [undo] vxlan vni vni-id 571 cmd = "vxlan vni %s" % self.vpn_vni 572 exist = is_config_exist(self.config, cmd) 573 if self.state == "present" and not exist: 574 self.cli_add_command(view_cmd) 575 self.cli_add_command(cmd) 576 self.cli_add_command("quit") 577 elif self.state == "absent" and exist: 578 self.cli_add_command(view_cmd) 579 self.cli_add_command(cmd, undo=True) 580 self.cli_add_command("quit") 581 582 def config_vbdif(self): 583 """configure command at the VBDIF interface view""" 584 585 if not self.vbdif_name: 586 return 587 588 vbdif_cmd = "interface %s" % self.vbdif_name.lower().capitalize() 589 exist = is_config_exist(self.config, vbdif_cmd) 590 591 if not exist: 592 self.module.fail_json( 593 msg="Error: Interface %s is not exist." % self.vbdif_name) 594 595 # interface vbdif bd-id 596 # [undo] ip binding vpn-instance vpn-instance-name 597 vbdif_view = False 598 if self.vbdif_bind_vpn: 599 cmd = "ip binding vpn-instance %s" % self.vbdif_bind_vpn 600 exist = is_config_exist(self.config, cmd) 601 602 if self.state == "present" and not exist: 603 if not vbdif_view: 604 self.cli_add_command(vbdif_cmd) 605 vbdif_view = True 606 self.cli_add_command(cmd) 607 elif self.state == "absent" and exist: 608 if not vbdif_view: 609 self.cli_add_command(vbdif_cmd) 610 vbdif_view = True 611 self.cli_add_command(cmd, undo=True) 612 613 # [undo] arp distribute-gateway enable 614 if self.arp_distribute_gateway: 615 cmd = "arp distribute-gateway enable" 616 exist = is_config_exist(self.config, cmd) 617 if self.arp_distribute_gateway == "enable" and not exist: 618 if not vbdif_view: 619 self.cli_add_command(vbdif_cmd) 620 vbdif_view = True 621 self.cli_add_command(cmd) 622 elif self.arp_distribute_gateway == "disable" and exist: 623 if not vbdif_view: 624 self.cli_add_command(vbdif_cmd) 625 vbdif_view = True 626 self.cli_add_command(cmd, undo=True) 627 628 # [undo] arp direct-route enable 629 if self.arp_direct_route: 630 cmd = "arp direct-route enable" 631 exist = is_config_exist(self.config, cmd) 632 if self.arp_direct_route == "enable" and not exist: 633 if not vbdif_view: 634 self.cli_add_command(vbdif_cmd) 635 vbdif_view = True 636 self.cli_add_command(cmd) 637 elif self.arp_direct_route == "disable" and exist: 638 if not vbdif_view: 639 self.cli_add_command(vbdif_cmd) 640 vbdif_view = True 641 self.cli_add_command(cmd, undo=True) 642 643 # mac-address mac-address 644 # undo mac-address 645 if self.vbdif_mac: 646 cmd = "mac-address %s" % self.vbdif_mac 647 exist = is_config_exist(self.config, cmd) 648 if self.state == "present" and not exist: 649 if not vbdif_view: 650 self.cli_add_command(vbdif_cmd) 651 vbdif_view = True 652 self.cli_add_command(cmd) 653 elif self.state == "absent" and exist: 654 if not vbdif_view: 655 self.cli_add_command(vbdif_cmd) 656 vbdif_view = True 657 self.cli_add_command("undo mac-address") 658 659 # quit 660 if vbdif_view: 661 self.cli_add_command("quit") 662 663 def is_valid_vbdif(self, ifname): 664 """check is interface vbdif""" 665 666 if not ifname.upper().startswith('VBDIF'): 667 return False 668 bdid = self.vbdif_name.replace(" ", "").upper().replace("VBDIF", "") 669 if not bdid.isdigit(): 670 return False 671 if int(bdid) < 1 or int(bdid) > 16777215: 672 return False 673 674 return True 675 676 def is_valid_ip_vpn(self, vpname): 677 """check ip vpn""" 678 679 if not vpname: 680 return False 681 682 if vpname == "_public_": 683 self.module.fail_json( 684 msg="Error: The value C(_public_) is reserved and cannot be used as the VPN instance name.") 685 686 if len(vpname) < 1 or len(vpname) > 31: 687 self.module.fail_json( 688 msg="Error: IP vpn name length is not in the range from 1 to 31.") 689 690 return True 691 692 def check_params(self): 693 """Check all input params""" 694 695 # dfs id check 696 if self.dfs_id: 697 if not self.dfs_id.isdigit(): 698 self.module.fail_json(msg="Error: DFS id is not digit.") 699 if int(self.dfs_id) != 1: 700 self.module.fail_json(msg="Error: DFS is not 1.") 701 702 # dfs_source_ip check 703 if self.dfs_source_ip: 704 if not is_valid_v4addr(self.dfs_source_ip): 705 self.module.fail_json(msg="Error: dfs_source_ip is invalid.") 706 # dfs_source_vpn check 707 if self.dfs_source_vpn and not self.is_valid_ip_vpn(self.dfs_source_vpn): 708 self.module.fail_json(msg="Error: dfs_source_vpn is invalid.") 709 710 # dfs_source_vpn and dfs_source_ip must set at the same time 711 if self.dfs_source_vpn and not self.dfs_source_ip: 712 self.module.fail_json( 713 msg="Error: dfs_source_vpn and dfs_source_ip must set at the same time.") 714 715 # dfs_udp_port check 716 if self.dfs_udp_port: 717 if not self.dfs_udp_port.isdigit(): 718 self.module.fail_json( 719 msg="Error: dfs_udp_port id is not digit.") 720 if int(self.dfs_udp_port) < 1025 or int(self.dfs_udp_port) > 65535: 721 self.module.fail_json( 722 msg="dfs_udp_port is not ranges from 1025 to 65535.") 723 724 # dfs_peer_ip check 725 if self.dfs_peer_ip: 726 if not is_valid_v4addr(self.dfs_peer_ip): 727 self.module.fail_json(msg="Error: dfs_peer_ip is invalid.") 728 # dfs_peer_vpn check 729 if self.dfs_peer_vpn and not self.is_valid_ip_vpn(self.dfs_peer_vpn): 730 self.module.fail_json(msg="Error: dfs_peer_vpn is invalid.") 731 732 # dfs_peer_vpn and dfs_peer_ip must set at the same time 733 if self.dfs_peer_vpn and not self.dfs_peer_ip: 734 self.module.fail_json( 735 msg="Error: dfs_peer_vpn and dfs_peer_ip must set at the same time.") 736 737 # vpn_instance check 738 if self.vpn_instance and not self.is_valid_ip_vpn(self.vpn_instance): 739 self.module.fail_json(msg="Error: vpn_instance is invalid.") 740 741 # vpn_vni check 742 if self.vpn_vni: 743 if not self.vpn_vni.isdigit(): 744 self.module.fail_json(msg="Error: vpn_vni id is not digit.") 745 if int(self.vpn_vni) < 1 or int(self.vpn_vni) > 16000000: 746 self.module.fail_json( 747 msg="vpn_vni is not ranges from 1 to 16000000.") 748 749 # vpn_instance and vpn_vni must set at the same time 750 if bool(self.vpn_instance) != bool(self.vpn_vni): 751 self.module.fail_json( 752 msg="Error: vpn_instance and vpn_vni must set at the same time.") 753 754 # vbdif_name check 755 if self.vbdif_name: 756 self.vbdif_name = self.vbdif_name.replace(" ", "").lower().capitalize() 757 if not self.is_valid_vbdif(self.vbdif_name): 758 self.module.fail_json(msg="Error: vbdif_name is invalid.") 759 760 # vbdif_mac check 761 if self.vbdif_mac: 762 mac = mac_format(self.vbdif_mac) 763 if not mac: 764 self.module.fail_json(msg="Error: vbdif_mac is invalid.") 765 self.vbdif_mac = mac 766 767 # vbdif_bind_vpn check 768 if self.vbdif_bind_vpn and not self.is_valid_ip_vpn(self.vbdif_bind_vpn): 769 self.module.fail_json(msg="Error: vbdif_bind_vpn is invalid.") 770 771 # All-Active Gateways or Distributed Gateway config can not set at the 772 # same time. 773 if self.dfs_id: 774 if self.vpn_vni or self.arp_distribute_gateway == "enable": 775 self.module.fail_json(msg="Error: All-Active Gateways or Distributed Gateway config " 776 "can not set at the same time.") 777 778 def get_proposed(self): 779 """get proposed info""" 780 781 if self.dfs_id: 782 self.proposed["dfs_id"] = self.dfs_id 783 self.proposed["dfs_source_ip"] = self.dfs_source_ip 784 self.proposed["dfs_source_vpn"] = self.dfs_source_vpn 785 self.proposed["dfs_udp_port"] = self.dfs_udp_port 786 self.proposed["dfs_all_active"] = self.dfs_all_active 787 self.proposed["dfs_peer_ip"] = self.dfs_peer_ip 788 self.proposed["dfs_peer_vpn"] = self.dfs_peer_vpn 789 790 if self.vpn_instance: 791 self.proposed["vpn_instance"] = self.vpn_instance 792 self.proposed["vpn_vni"] = self.vpn_vni 793 794 if self.vbdif_name: 795 self.proposed["vbdif_name"] = self.vbdif_name 796 self.proposed["vbdif_mac"] = self.vbdif_mac 797 self.proposed["vbdif_bind_vpn"] = self.vbdif_bind_vpn 798 self.proposed[ 799 "arp_distribute_gateway"] = self.arp_distribute_gateway 800 self.proposed["arp_direct_route"] = self.arp_direct_route 801 802 self.proposed["state"] = self.state 803 804 def get_existing(self): 805 """get existing info""" 806 807 if not self.config: 808 return 809 810 if is_config_exist(self.config, "dfs-group 1"): 811 self.existing["dfs_id"] = "1" 812 self.existing["dfs_source_ip"] = get_dfs_source_ip(self.config) 813 self.existing["dfs_source_vpn"] = get_dfs_source_vpn(self.config) 814 self.existing["dfs_udp_port"] = get_dfs_udp_port(self.config) 815 if is_config_exist(self.config, "active-active-gateway"): 816 self.existing["dfs_all_active"] = "enable" 817 self.existing["dfs_peers"] = get_dfs_peers(self.config) 818 else: 819 self.existing["dfs_all_active"] = "disable" 820 821 if self.vpn_instance: 822 self.existing["vpn_instance"] = get_ip_vpn(self.config) 823 self.existing["vpn_vni"] = get_ip_vpn_vni(self.config) 824 825 if self.vbdif_name: 826 self.existing["vbdif_name"] = self.vbdif_name 827 self.existing["vbdif_mac"] = get_vbdif_mac(self.config) 828 self.existing["vbdif_bind_vpn"] = get_vbdif_vpn(self.config) 829 if is_config_exist(self.config, "arp distribute-gateway enable"): 830 self.existing["arp_distribute_gateway"] = "enable" 831 else: 832 self.existing["arp_distribute_gateway"] = "disable" 833 if is_config_exist(self.config, "arp direct-route enable"): 834 self.existing["arp_direct_route"] = "enable" 835 else: 836 self.existing["arp_direct_route"] = "disable" 837 838 def get_end_state(self): 839 """get end state info""" 840 841 config = self.get_current_config() 842 if not config: 843 return 844 845 if is_config_exist(config, "dfs-group 1"): 846 self.end_state["dfs_id"] = "1" 847 self.end_state["dfs_source_ip"] = get_dfs_source_ip(config) 848 self.end_state["dfs_source_vpn"] = get_dfs_source_vpn(config) 849 self.end_state["dfs_udp_port"] = get_dfs_udp_port(config) 850 if is_config_exist(config, "active-active-gateway"): 851 self.end_state["dfs_all_active"] = "enable" 852 self.end_state["dfs_peers"] = get_dfs_peers(config) 853 else: 854 self.end_state["dfs_all_active"] = "disable" 855 856 if self.vpn_instance: 857 self.end_state["vpn_instance"] = get_ip_vpn(config) 858 self.end_state["vpn_vni"] = get_ip_vpn_vni(config) 859 860 if self.vbdif_name: 861 self.end_state["vbdif_name"] = self.vbdif_name 862 self.end_state["vbdif_mac"] = get_vbdif_mac(config) 863 self.end_state["vbdif_bind_vpn"] = get_vbdif_vpn(config) 864 if is_config_exist(config, "arp distribute-gateway enable"): 865 self.end_state["arp_distribute_gateway"] = "enable" 866 else: 867 self.end_state["arp_distribute_gateway"] = "disable" 868 if is_config_exist(config, "arp direct-route enable"): 869 self.end_state["arp_direct_route"] = "enable" 870 else: 871 self.end_state["arp_direct_route"] = "disable" 872 873 def work(self): 874 """worker""" 875 876 self.check_params() 877 self.config = self.get_current_config() 878 self.get_existing() 879 self.get_proposed() 880 881 # deal present or absent 882 if self.dfs_id: 883 self.config_dfs_group() 884 885 if self.vpn_instance: 886 self.config_ip_vpn() 887 888 if self.vbdif_name: 889 self.config_vbdif() 890 891 if self.commands: 892 self.cli_load_config(self.commands) 893 self.changed = True 894 895 self.get_end_state() 896 self.results['changed'] = self.changed 897 self.results['proposed'] = self.proposed 898 self.results['existing'] = self.existing 899 self.results['end_state'] = self.end_state 900 if self.changed: 901 self.results['updates'] = self.updates_cmd 902 else: 903 self.results['updates'] = list() 904 905 self.module.exit_json(**self.results) 906 907 908def main(): 909 """Module main""" 910 911 argument_spec = dict( 912 dfs_id=dict(required=False, type='str'), 913 dfs_source_ip=dict(required=False, type='str'), 914 dfs_source_vpn=dict(required=False, type='str'), 915 dfs_udp_port=dict(required=False, type='str'), 916 dfs_all_active=dict(required=False, type='str', 917 choices=['enable', 'disable']), 918 dfs_peer_ip=dict(required=False, type='str'), 919 dfs_peer_vpn=dict(required=False, type='str'), 920 vpn_instance=dict(required=False, type='str'), 921 vpn_vni=dict(required=False, type='str'), 922 vbdif_name=dict(required=False, type='str'), 923 vbdif_mac=dict(required=False, type='str'), 924 vbdif_bind_vpn=dict(required=False, type='str'), 925 arp_distribute_gateway=dict( 926 required=False, type='str', choices=['enable', 'disable']), 927 arp_direct_route=dict(required=False, type='str', 928 choices=['enable', 'disable']), 929 state=dict(required=False, default='present', 930 choices=['present', 'absent']) 931 ) 932 argument_spec.update(ce_argument_spec) 933 module = VxlanGateway(argument_spec) 934 module.work() 935 936 937if __name__ == '__main__': 938 main() 939