1#!/usr/local/bin/python3.8 2# This file is part of Ansible 3# 4# Ansible is free software: you can redistribute it and/or modify 5# it under the terms of the GNU General Public License as published by 6# the Free Software Foundation, either version 3 of the License, or 7# (at your option) any later version. 8# 9# Ansible is distributed in the hope that it will be useful, 10# but WITHOUT ANY WARRANTY; without even the implied warranty of 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12# GNU General Public License for more details. 13# 14# You should have received a copy of the GNU General Public License 15# along with Ansible. If not, see <http://www.gnu.org/licenses/>. 16# 17from __future__ import (absolute_import, division, print_function) 18__metaclass__ = type 19 20 21DOCUMENTATION = ''' 22--- 23module: ce_static_route_bfd 24version_added: '0.2.0' 25short_description: Manages static route configuration on HUAWEI CloudEngine switches. 26description: 27 - Manages the static routes on HUAWEI CloudEngine switches. 28author: xuxiaowei0512 (@CloudEngine-Ansible) 29notes: 30 - This module requires the netconf system service be enabled on the remote device being managed. 31 - Recommended connection is C(netconf). 32 - This module also works with C(local) connections for legacy playbooks. 33 - If no vrf is supplied, vrf is set to default. 34 - If I(state=absent), the route configuration will be removed, regardless of the non-required parameters. 35options: 36 prefix: 37 description: 38 - Destination ip address of static route. 39 required: true 40 type: str 41 mask: 42 description: 43 - Destination ip mask of static route. 44 type: str 45 aftype: 46 description: 47 - Destination ip address family type of static route. 48 required: true 49 type: str 50 choices: ['v4','v6'] 51 next_hop: 52 description: 53 - Next hop address of static route. 54 type: str 55 nhp_interface: 56 description: 57 - Next hop interface full name of static route. 58 type: str 59 vrf: 60 description: 61 - VPN instance of destination ip address. 62 type: str 63 destvrf: 64 description: 65 - VPN instance of next hop ip address. 66 type: str 67 tag: 68 description: 69 - Route tag value (numeric). 70 type: int 71 description: 72 description: 73 - Name of the route. Used with the name parameter on the CLI. 74 type: str 75 pref: 76 description: 77 - Preference or administrative difference of route (range 1-255). 78 type: int 79 function_flag: 80 description: 81 - Used to distinguish between command line functions. 82 required: true 83 choices: ['globalBFD','singleBFD','dynamicBFD','staticBFD'] 84 type: str 85 min_tx_interval: 86 description: 87 - Set the minimum BFD session sending interval (range 50-1000). 88 type: int 89 min_rx_interval: 90 description: 91 - Set the minimum BFD receive interval (range 50-1000). 92 type: int 93 detect_multiplier: 94 description: 95 - Configure the BFD multiplier (range 3-50). 96 type: int 97 bfd_session_name: 98 description: 99 - bfd name (range 1-15). 100 type: str 101 commands: 102 description: 103 - Incoming command line is used to send sys,undo ip route-static default-bfd,commit. 104 type: list 105 state: 106 description: 107 - Specify desired state of the resource. 108 required: false 109 choices: ['present','absent'] 110 type: str 111 default: present 112''' 113 114EXAMPLES = ''' 115 #ip route-static bfd interface-type interface-number nexthop-address [ local-address address ] 116 #[ min-rx-interval min-rx-interval | min-tx-interval min-tx-interval | detect-multiplier multiplier ] 117 - name: Config an ip route-static bfd 10GE1/0/1 3.3.3.3 min-rx-interval 50 min-tx-interval 50 detect-multiplier 5 118 community.network.ce_static_route_bfd: 119 function_flag: 'singleBFD' 120 nhp_interface: 10GE1/0/1 121 next_hop: 3.3.3.3 122 min_tx_interval: 50 123 min_rx_interval: 50 124 detect_multiplier: 5 125 aftype: v4 126 state: present 127 128 #undo ip route-static bfd [ interface-type interface-number | vpn-instance vpn-instance-name ] nexthop-address 129 - name: Undo ip route-static bfd 10GE1/0/1 3.3.3.4 130 community.network.ce_static_route_bfd: 131 function_flag: 'singleBFD' 132 nhp_interface: 10GE1/0/1 133 next_hop: 3.3.3.4 134 aftype: v4 135 state: absent 136 137 #ip route-static default-bfd { min-rx-interval {min-rx-interval} | min-tx-interval {min-tx-interval} | detect-multiplier {multiplier}} 138 - name: Config an ip route-static default-bfd min-rx-interval 50 min-tx-interval 50 detect-multiplier 6 139 community.network.ce_static_route_bfd: 140 function_flag: 'globalBFD' 141 min_tx_interval: 50 142 min_rx_interval: 50 143 detect_multiplier: 6 144 aftype: v4 145 state: present 146 147 - name: Undo ip route-static default-bfd 148 community.network.ce_static_route_bfd: 149 function_flag: 'globalBFD' 150 aftype: v4 151 state: absent 152 commands: 'sys,undo ip route-static default-bfd,commit' 153 154 - name: Config an ipv4 static route 2.2.2.0/24 2.2.2.1 preference 1 tag 2 description test for staticBFD 155 community.network.ce_static_route_bfd: 156 function_flag: 'staticBFD' 157 prefix: 2.2.2.2 158 mask: 24 159 next_hop: 2.2.2.1 160 tag: 2 161 description: test 162 pref: 1 163 aftype: v4 164 bfd_session_name: btoa 165 state: present 166''' 167RETURN = ''' 168proposed: 169 description: k/v pairs of parameters passed into module 170 returned: always 171 type: dict 172 sample: {"function_flag": "staticBFD", "next_hop": "3.3.3.3", "pref": "100", 173 "prefix": "192.168.20.642", "mask": "24", "description": "testing", 174 "vrf": "_public_", "bfd_session_name": "btoa"} 175existing: 176 description: k/v pairs of existing switchport 177 returned: always 178 type: dict 179 sample: {"function_flag": "", "next_hop": "", "pref": "101", 180 "prefix": "192.168.20.0", "mask": "24", "description": "testing", 181 "tag" : "null", "bfd_session_name": "btoa"} 182end_state: 183 description: k/v pairs of switchport after module execution 184 returned: always 185 type: dict 186 sample: {"function_flag": "staticBFD", "next_hop": "3.3.3.3", "pref": "100", 187 "prefix": "192.168.20.0", "mask": "24", "description": "testing", 188 "tag" : "null", "bfd_session_name": "btoa"} 189updates: 190 description: command list sent to the device 191 returned: always 192 type: list 193 sample: ["ip route-static 192.168.20.0 255.255.255.0 3.3.3.3 preference 100 description testing"] 194changed: 195 description: check to see if a change was made on the device 196 returned: always 197 type: bool 198 sample: true 199''' 200 201from xml.etree import ElementTree 202from ansible.module_utils.basic import AnsibleModule 203from ansible.module_utils.six import string_types 204from ansible_collections.community.network.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config 205 206CE_NC_GET_STATIC_ROUTE_BFD_SESSIONNAME = """ 207<filter type="subtree"> 208 <staticrt xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0"> 209 <staticrtbase> 210 <srRoutes> 211 <srRoute> 212 <vrfName></vrfName> 213 <afType></afType> 214 <topologyName></topologyName> 215 <prefix></prefix> 216 <maskLength></maskLength> 217 <ifName></ifName> 218 <destVrfName></destVrfName> 219 <nexthop></nexthop> 220 <preference></preference> 221 <tag></tag> 222 <sessionName></sessionName> 223 <description></description> 224 </srRoute> 225 </srRoutes> 226 </staticrtbase> 227 </staticrt> 228</filter> 229""" 230# bfd enable 231CE_NC_GET_STATIC_ROUTE_BFD_ENABLE = """ 232<filter type="subtree"> 233 <staticrt xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0"> 234 <staticrtbase> 235 <srRoutes> 236 <srRoute> 237 <vrfName></vrfName> 238 <afType></afType> 239 <topologyName></topologyName> 240 <prefix></prefix> 241 <maskLength></maskLength> 242 <ifName></ifName> 243 <destVrfName></destVrfName> 244 <nexthop></nexthop> 245 <preference></preference> 246 <tag></tag> 247 <bfdEnable></bfdEnable> 248 <description></description> 249 </srRoute> 250 </srRoutes> 251 </staticrtbase> 252 </staticrt> 253 </filter> 254""" 255 256CE_NC_GET_STATIC_ROUTE_BFD_ABSENT = """ 257<filter type="subtree"> 258 <staticrt xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0"> 259 <staticrtbase> 260 <srBfdParas> 261 <srBfdPara> 262 <afType>%s</afType> 263 <ifName>%s</ifName> 264 <destVrfName>%s</destVrfName> 265 <nexthop>%s</nexthop> 266 </srBfdPara> 267 </srBfdParas> 268 </staticrtbase> 269 </staticrt> 270</filter> 271""" 272 273CE_NC_GET_STATIC_ROUTE_BFD = """ 274<filter type="subtree"> 275 <staticrt xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0"> 276 <staticrtbase> 277 <srBfdParas> 278 <srBfdPara> 279 <afType>%s</afType> 280 <ifName>%s</ifName> 281 <destVrfName>%s</destVrfName> 282 <nexthop>%s</nexthop> 283 <localAddress></localAddress> 284 <minTxInterval></minTxInterval> 285 <minRxInterval></minRxInterval> 286 <multiplier></multiplier> 287 </srBfdPara> 288 </srBfdParas> 289 </staticrtbase> 290 </staticrt> 291</filter> 292""" 293CE_NC_GET_STATIC_ROUTE_IPV4_GLOBAL_BFD = """ 294<filter type="subtree"> 295 <staticrt xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0"> 296 <staticrtbase> 297 <srIPv4StaticSite> 298 <minTxInterval></minTxInterval> 299 <minRxInterval></minRxInterval> 300 <multiplier></multiplier> 301 </srIPv4StaticSite> 302 </staticrtbase> 303 </staticrt> 304</filter> 305""" 306CE_NC_GET_STATIC_ROUTE_ABSENT = """ 307<filter type="subtree"> 308 <staticrt xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0"> 309 <staticrtbase> 310 <srRoutes> 311 <srRoute> 312 <vrfName></vrfName> 313 <afType></afType> 314 <topologyName></topologyName> 315 <prefix></prefix> 316 <maskLength></maskLength> 317 <ifName></ifName> 318 <destVrfName></destVrfName> 319 <nexthop></nexthop> 320 </srRoute> 321 </srRoutes> 322 </staticrtbase> 323 </staticrt> 324 </filter> 325""" 326 327CE_NC_DELETE_STATIC_ROUTE_SINGLEBFD = """ 328<staticrt xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0"> 329 <staticrtbase> 330 <srBfdParas> 331 <srBfdPara operation="delete"> 332 <afType>%s</afType> 333 <ifName>%s</ifName> 334 <destVrfName>%s</destVrfName> 335 <nexthop>%s</nexthop> 336 </srBfdPara> 337 </srBfdParas> 338 </staticrtbase> 339 </staticrt> 340""" 341CE_NC_SET_STATIC_ROUTE_SINGLEBFD = """ 342<staticrt xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0"> 343 <staticrtbase> 344 <srBfdParas> 345 <srBfdPara operation="merge"> 346 <afType>%s</afType> 347 <ifName>%s</ifName> 348 <destVrfName>%s</destVrfName> 349 <nexthop>%s</nexthop>%s%s%s%s 350 </srBfdPara> 351 </srBfdParas> 352 </staticrtbase> 353 </staticrt> 354 355""" 356CE_NC_SET_STATIC_ROUTE_SINGLEBFD_LOCALADRESS = """ 357<localAddress>%s</localAddress> 358""" 359CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MINTX = """ 360<minTxInterval>%s</minTxInterval> 361""" 362CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MINRX = """ 363<minRxInterval>%s</minRxInterval> 364""" 365CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MUL = """ 366<multiplier>%s</multiplier> 367""" 368CE_NC_SET_IPV4_STATIC_ROUTE_GLOBALBFD = """ 369<staticrt xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0"> 370 <staticrtbase> 371 <srIPv4StaticSite operation="merge"> 372 %s%s%s 373 </srIPv4StaticSite> 374 </staticrtbase> 375</staticrt> 376""" 377 378CE_NC_SET_STATIC_ROUTE = """ 379<staticrt xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0"> 380 <staticrtbase> 381 <srRoutes> 382 <srRoute operation="merge"> 383 <vrfName>%s</vrfName> 384 <afType>%s</afType> 385 <topologyName>base</topologyName> 386 <prefix>%s</prefix> 387 <maskLength>%s</maskLength> 388 <ifName>%s</ifName> 389 <destVrfName>%s</destVrfName> 390 <nexthop>%s</nexthop>%s%s%s%s 391 </srRoute> 392 </srRoutes> 393 </staticrtbase> 394 </staticrt> 395""" 396CE_NC_SET_DESCRIPTION = """ 397<description>%s</description> 398""" 399 400CE_NC_SET_PREFERENCE = """ 401<preference>%s</preference> 402""" 403 404CE_NC_SET_TAG = """ 405<tag>%s</tag> 406""" 407CE_NC_SET_BFDSESSIONNAME = """ 408<sessionName>%s</sessionName> 409""" 410CE_NC_SET_BFDENABLE = """ 411<bfdEnable>true</bfdEnable> 412""" 413CE_NC_DELETE_STATIC_ROUTE = """ 414<staticrt xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0"> 415 <staticrtbase> 416 <srRoutes> 417 <srRoute operation="delete"> 418 <vrfName>%s</vrfName> 419 <afType>%s</afType> 420 <topologyName>base</topologyName> 421 <prefix>%s</prefix> 422 <maskLength>%s</maskLength> 423 <ifName>%s</ifName> 424 <destVrfName>%s</destVrfName> 425 <nexthop>%s</nexthop> 426 </srRoute> 427 </srRoutes> 428 </staticrtbase> 429 </staticrt> 430""" 431 432 433def build_config_xml(xmlstr): 434 """build config xml""" 435 436 return '<config> ' + xmlstr + ' </config>' 437 438 439def is_valid_v4addr(addr): 440 """check if ipv4 addr is valid""" 441 if addr.find('.') != -1: 442 addr_list = addr.split('.') 443 if len(addr_list) != 4: 444 return False 445 for each_num in addr_list: 446 447 if not each_num.isdigit(): 448 return False 449 if int(each_num) > 255: 450 return False 451 return True 452 return False 453 454 455def is_valid_v6addr(addr): 456 """check if ipv6 addr is valid""" 457 if addr.find(':') != -1: 458 addr_list = addr.split(':') 459 if len(addr_list) > 6: 460 return False 461 if addr_list[1] == "": 462 return False 463 return True 464 return False 465 466 467def is_valid_tag(tag): 468 """check if the tag is valid""" 469 470 if int(tag) < 1 or int(tag) > 4294967295: 471 return False 472 return True 473 474 475def is_valid_bdf_interval(interval): 476 """check if the min_tx_interva,min-rx-interval is valid""" 477 478 if interval < 50 or interval > 1000: 479 return False 480 return True 481 482 483def is_valid_bdf_multiplier(multiplier): 484 """check if the detect_multiplier is valid""" 485 486 if multiplier < 3 or multiplier > 50: 487 return False 488 return True 489 490 491def is_valid_bdf_session_name(session_name): 492 """check if the bfd_session_name is valid""" 493 if session_name.find(' ') != -1: 494 return False 495 if len(session_name) < 1 or len(session_name) > 15: 496 return False 497 return True 498 499 500def is_valid_preference(pref): 501 """check if the preference is valid""" 502 503 if int(pref) > 0 and int(pref) < 256: 504 return True 505 return False 506 507 508def is_valid_description(description): 509 """check if the description is valid""" 510 if description.find('?') != -1: 511 return False 512 if len(description) < 1 or len(description) > 255: 513 return False 514 return True 515 516 517def compare_command(commands): 518 """check if the commands is valid""" 519 if len(commands) < 3: 520 return True 521 if commands[0] != 'sys' or commands[1] != 'undo ip route-static default-bfd' \ 522 or commands[2] != 'commit': 523 return True 524 525 526def get_to_lines(stdout): 527 """data conversion""" 528 lines = list() 529 for item in stdout: 530 if isinstance(item, string_types): 531 item = str(item).split('\n') 532 lines.append(item) 533 return lines 534 535 536def get_change_state(oldvalue, newvalue, change): 537 """get change state""" 538 if newvalue is not None: 539 if oldvalue != str(newvalue): 540 change = True 541 else: 542 if oldvalue != newvalue: 543 change = True 544 return change 545 546 547def get_xml(xml, value): 548 """operate xml""" 549 if value is None: 550 value = '' 551 tempxml = xml % value 552 return tempxml 553 554 555class StaticRouteBFD(object): 556 """static route module""" 557 558 def __init__(self, argument_spec): 559 self.spec = argument_spec 560 self.module = None 561 self._initmodule_() 562 563 # static route info 564 self.function_flag = self.module.params['function_flag'] 565 self.aftype = self.module.params['aftype'] 566 self.state = self.module.params['state'] 567 if self.aftype == "v4": 568 self.version = "ipv4unicast" 569 else: 570 self.version = "ipv6unicast" 571 if self.function_flag != 'globalBFD': 572 self.nhp_interface = self.module.params['nhp_interface'] 573 if self.nhp_interface is None: 574 self.nhp_interface = "Invalid0" 575 576 self.destvrf = self.module.params['destvrf'] 577 if self.destvrf is None: 578 self.destvrf = "_public_" 579 580 self.next_hop = self.module.params['next_hop'] 581 self.prefix = self.module.params['prefix'] 582 583 if self.function_flag != 'globalBFD' and self.function_flag != 'singleBFD': 584 self.mask = self.module.params['mask'] 585 self.tag = self.module.params['tag'] 586 self.description = self.module.params['description'] 587 self.pref = self.module.params['pref'] 588 if self.pref is None: 589 self.pref = 60 590 # vpn instance info 591 self.vrf = self.module.params['vrf'] 592 if self.vrf is None: 593 self.vrf = "_public_" 594 # bfd session name 595 self.bfd_session_name = self.module.params['bfd_session_name'] 596 597 if self.function_flag == 'globalBFD' or self.function_flag == 'singleBFD': 598 self.min_tx_interval = self.module.params['min_tx_interval'] 599 self.min_rx_interval = self.module.params['min_rx_interval'] 600 self.detect_multiplier = self.module.params['detect_multiplier'] 601 if self.function_flag == 'globalBFD' and self.state == 'absent': 602 self.commands = self.module.params['commands'] 603 # state 604 self.changed = False 605 self.updates_cmd = list() 606 self.results = dict() 607 self.proposed = dict() 608 self.existing = dict() 609 self.end_state = dict() 610 611 self.static_routes_info = dict() 612 613 def _initmodule_(self): 614 """init module""" 615 616 self.module = AnsibleModule( 617 argument_spec=self.spec, supports_check_mode=False) 618 619 def _checkresponse_(self, xml_str, xml_name): 620 """check if response message is already succeed.""" 621 622 if "<ok/>" not in xml_str: 623 self.module.fail_json(msg='Error: %s failed.' % xml_name) 624 625 def _convertlentomask_(self, masklen): 626 """convert mask length to ip address mask, i.e. 24 to 255.255.255.0""" 627 628 mask_int = ["0"] * 4 629 length = int(masklen) 630 631 if length > 32: 632 self.module.fail_json(msg='IPv4 ipaddress mask length is invalid') 633 if length < 8: 634 mask_int[0] = str(int((0xFF << (8 - length % 8)) & 0xFF)) 635 if length >= 8: 636 mask_int[0] = '255' 637 mask_int[1] = str(int((0xFF << (16 - (length % 16))) & 0xFF)) 638 if length >= 16: 639 mask_int[1] = '255' 640 mask_int[2] = str(int((0xFF << (24 - (length % 24))) & 0xFF)) 641 if length >= 24: 642 mask_int[2] = '255' 643 mask_int[3] = str(int((0xFF << (32 - (length % 32))) & 0xFF)) 644 if length == 32: 645 mask_int[3] = '255' 646 647 return '.'.join(mask_int) 648 649 def _convertipprefix_(self): 650 """convert prefix to real value i.e. 2.2.2.2/24 to 2.2.2.0/24""" 651 if self.function_flag == 'singleBFD': 652 if self.aftype == "v4": 653 if self.prefix.find('.') == -1: 654 return False 655 addr_list = self.prefix.split('.') 656 length = len(addr_list) 657 if length > 4: 658 return False 659 for each_num in addr_list: 660 if not each_num.isdigit(): 661 return False 662 if int(each_num) > 255: 663 return False 664 return True 665 else: 666 if self.prefix.find(':') == -1: 667 return False 668 else: 669 if self.aftype == "v4": 670 if self.prefix.find('.') == -1: 671 return False 672 if self.mask == '32': 673 self.prefix = self.prefix 674 return True 675 if self.mask == '0': 676 self.prefix = '0.0.0.0' 677 return True 678 addr_list = self.prefix.split('.') 679 length = len(addr_list) 680 if length > 4: 681 return False 682 for each_num in addr_list: 683 if not each_num.isdigit(): 684 return False 685 if int(each_num) > 255: 686 return False 687 byte_len = 8 688 ip_len = int(self.mask) // byte_len 689 ip_bit = int(self.mask) % byte_len 690 else: 691 if self.prefix.find(':') == -1: 692 return False 693 if self.mask == '128': 694 self.prefix = self.prefix 695 return True 696 if self.mask == '0': 697 self.prefix = '::' 698 return True 699 addr_list = self.prefix.split(':') 700 length = len(addr_list) 701 if length > 6: 702 return False 703 byte_len = 16 704 ip_len = int(self.mask) // byte_len 705 ip_bit = int(self.mask) % byte_len 706 707 if self.aftype == "v4": 708 for i in range(ip_len + 1, length): 709 addr_list[i] = 0 710 else: 711 for i in range(length - ip_len, length): 712 addr_list[i] = 0 713 for j in range(0, byte_len - ip_bit): 714 if self.aftype == "v4": 715 addr_list[ip_len] = int(addr_list[ip_len]) & (0 << j) 716 else: 717 if addr_list[length - ip_len - 1] == "": 718 continue 719 addr_list[length - ip_len - 720 1] = '0x%s' % addr_list[length - ip_len - 1] 721 addr_list[length - ip_len - 722 1] = int(addr_list[length - ip_len - 1], 16) & (0 << j) 723 724 if self.aftype == "v4": 725 self.prefix = '%s.%s.%s.%s' % (addr_list[0], addr_list[1], addr_list[2], addr_list[3]) 726 return True 727 if self.aftype == "v6": 728 ipv6_addr_str = "" 729 for num in range(0, length - ip_len): 730 ipv6_addr_str += '%s:' % addr_list[num] 731 self.prefix = ipv6_addr_str 732 733 return True 734 735 def set_update_cmd_globalbfd(self): 736 """set globalBFD update command""" 737 if not self.changed: 738 return 739 if self.state == "present": 740 self.updates_cmd.append('ip route-static default-bfd') 741 if self.min_tx_interval: 742 self.updates_cmd.append(' min-rx-interval %s' % (self.min_tx_interval)) 743 if self.min_rx_interval: 744 self.updates_cmd.append(' min-tx-interval %s' % (self.min_rx_interval)) 745 if self.detect_multiplier: 746 self.updates_cmd.append(' detect-multiplier %s' % (self.detect_multiplier)) 747 else: 748 self.updates_cmd.append('undo ip route-static default-bfd') 749 750 def set_update_cmd_singlebfd(self): 751 """set singleBFD update command""" 752 if not self.changed: 753 return 754 if self.next_hop is None: 755 next_hop = '' 756 else: 757 next_hop = self.next_hop 758 759 if self.destvrf == "_public_": 760 destvrf = '' 761 else: 762 destvrf = self.destvrf 763 764 if self.nhp_interface == "Invalid0": 765 nhp_interface = '' 766 else: 767 nhp_interface = self.nhp_interface 768 if self.prefix == "0.0.0.0": 769 prefix = '' 770 else: 771 prefix = self.prefix 772 if self.state == "present": 773 if nhp_interface: 774 self.updates_cmd.append('ip route-static bfd %s %s' % (nhp_interface, next_hop)) 775 elif destvrf: 776 self.updates_cmd.append('ip route-static bfd vpn-instance %s %s' % (destvrf, next_hop)) 777 else: 778 self.updates_cmd.append('ip route-static bfd %s' % (next_hop)) 779 if prefix: 780 self.updates_cmd.append(' local-address %s' % (self.prefix)) 781 if self.min_tx_interval: 782 self.updates_cmd.append(' min-rx-interval %s' % (self.min_tx_interval)) 783 if self.min_rx_interval: 784 self.updates_cmd.append(' min-tx-interval %s' % (self.min_rx_interval)) 785 if self.detect_multiplier: 786 self.updates_cmd.append(' detect-multiplier %s' % (self.detect_multiplier)) 787 else: 788 if nhp_interface: 789 self.updates_cmd.append('undo ip route-static bfd %s %s' % (nhp_interface, next_hop)) 790 elif destvrf: 791 self.updates_cmd.append('undo ip route-static bfd vpn-instance %s %s' % (destvrf, next_hop)) 792 else: 793 self.updates_cmd.append('undo ip route-static bfd %s' % (next_hop)) 794 795 def set_update_cmd(self): 796 """set update command""" 797 if not self.changed: 798 return 799 800 if self.aftype == "v4": 801 maskstr = self._convertlentomask_(self.mask) 802 else: 803 maskstr = self.mask 804 static_bfd_flag = True 805 if self.bfd_session_name: 806 static_bfd_flag = False 807 if self.next_hop is None: 808 next_hop = '' 809 else: 810 next_hop = self.next_hop 811 if self.vrf == "_public_": 812 vrf = '' 813 else: 814 vrf = self.vrf 815 if self.destvrf == "_public_": 816 destvrf = '' 817 else: 818 destvrf = self.destvrf 819 if self.nhp_interface == "Invalid0": 820 nhp_interface = '' 821 else: 822 nhp_interface = self.nhp_interface 823 if self.state == "present": 824 if self.vrf != "_public_": 825 if self.destvrf != "_public_": 826 self.updates_cmd.append('ip route-static vpn-instance %s %s %s vpn-instance %s %s' 827 % (vrf, self.prefix, maskstr, destvrf, next_hop)) 828 else: 829 self.updates_cmd.append('ip route-static vpn-instance %s %s %s %s %s' 830 % (vrf, self.prefix, maskstr, nhp_interface, next_hop)) 831 elif self.destvrf != "_public_": 832 self.updates_cmd.append('ip route-static %s %s vpn-instance %s %s' 833 % (self.prefix, maskstr, self.destvrf, next_hop)) 834 else: 835 self.updates_cmd.append('ip route-static %s %s %s %s' 836 % (self.prefix, maskstr, nhp_interface, next_hop)) 837 if self.pref != 60: 838 self.updates_cmd.append(' preference %s' % (self.pref)) 839 if self.tag: 840 self.updates_cmd.append(' tag %s' % (self.tag)) 841 if not static_bfd_flag: 842 self.updates_cmd.append(' track bfd-session %s' % (self.bfd_session_name)) 843 else: 844 self.updates_cmd.append(' bfd enable') 845 if self.description: 846 self.updates_cmd.append(' description %s' % (self.description)) 847 848 if self.state == "absent": 849 if self.vrf != "_public_": 850 if self.destvrf != "_public_": 851 self.updates_cmd.append('undo ip route-static vpn-instance %s %s %s vpn-instance %s %s' 852 % (vrf, self.prefix, maskstr, destvrf, next_hop)) 853 else: 854 self.updates_cmd.append('undo ip route-static vpn-instance %s %s %s %s %s' 855 % (vrf, self.prefix, maskstr, nhp_interface, next_hop)) 856 elif self.destvrf != "_public_": 857 self.updates_cmd.append('undo ip route-static %s %s vpn-instance %s %s' 858 % (self.prefix, maskstr, self.destvrf, self.next_hop)) 859 else: 860 self.updates_cmd.append('undo ip route-static %s %s %s %s' 861 % (self.prefix, maskstr, nhp_interface, next_hop)) 862 863 def operate_static_route_globalbfd(self): 864 """set globalbfd update command""" 865 min_tx_interval = self.min_tx_interval 866 min_rx_interval = self.min_rx_interval 867 multiplier = self.detect_multiplier 868 min_tx_interval_xml = """\n""" 869 min_rx_interval_xml = """\n""" 870 multiplier_xml = """\n""" 871 if self.state == "present": 872 if min_tx_interval is not None: 873 min_tx_interval_xml = CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MINTX % min_tx_interval 874 if min_rx_interval is not None: 875 min_rx_interval_xml = CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MINRX % min_rx_interval 876 if multiplier is not None: 877 multiplier_xml = CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MUL % multiplier 878 879 configxmlstr = CE_NC_SET_IPV4_STATIC_ROUTE_GLOBALBFD % ( 880 min_tx_interval_xml, min_rx_interval_xml, multiplier_xml) 881 conf_str = build_config_xml(configxmlstr) 882 recv_xml = set_nc_config(self.module, conf_str) 883 self._checkresponse_(recv_xml, "OPERATE_STATIC_ROUTE_globalBFD") 884 885 if self.state == "absent" and self.commands: 886 min_tx_interval_xml = CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MINTX % 1000 887 min_rx_interval_xml = CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MINRX % 1000 888 multiplier_xml = CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MUL % 3 889 890 configxmlstr = CE_NC_SET_IPV4_STATIC_ROUTE_GLOBALBFD % ( 891 min_tx_interval_xml, min_rx_interval_xml, multiplier_xml) 892 conf_str = build_config_xml(configxmlstr) 893 recv_xml = set_nc_config(self.module, conf_str) 894 self._checkresponse_(recv_xml, "OPERATE_STATIC_ROUTE_globalBFD") 895 896 def operate_static_route_singlebfd(self, version, prefix, nhp_interface, next_hop, destvrf, state): 897 """operate ipv4 static route singleBFD""" 898 min_tx_interval = self.min_tx_interval 899 min_rx_interval = self.min_rx_interval 900 multiplier = self.detect_multiplier 901 min_tx_interval_xml = """\n""" 902 min_rx_interval_xml = """\n""" 903 multiplier_xml = """\n""" 904 local_address_xml = """\n""" 905 if next_hop is None: 906 next_hop = '0.0.0.0' 907 908 if destvrf is None: 909 dest_vpn_instance = "_public_" 910 else: 911 dest_vpn_instance = destvrf 912 913 if nhp_interface is None: 914 nhp_interface = "Invalid0" 915 916 if min_tx_interval is not None: 917 min_tx_interval_xml = CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MINTX % min_tx_interval 918 if min_rx_interval is not None: 919 min_rx_interval_xml = CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MINRX % min_rx_interval 920 if multiplier is not None: 921 multiplier_xml = CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MUL % multiplier 922 923 if prefix is not None: 924 local_address_xml = CE_NC_SET_STATIC_ROUTE_SINGLEBFD_LOCALADRESS % prefix 925 926 if state == "present": 927 configxmlstr = CE_NC_SET_STATIC_ROUTE_SINGLEBFD % ( 928 version, nhp_interface, dest_vpn_instance, 929 next_hop, local_address_xml, min_tx_interval_xml, 930 min_rx_interval_xml, multiplier_xml) 931 932 else: 933 configxmlstr = CE_NC_DELETE_STATIC_ROUTE_SINGLEBFD % ( 934 version, nhp_interface, dest_vpn_instance, next_hop) 935 936 conf_str = build_config_xml(configxmlstr) 937 938 recv_xml = set_nc_config(self.module, conf_str) 939 self._checkresponse_(recv_xml, "OPERATE_STATIC_ROUTE_singleBFD") 940 941 def operate_static_route(self, version, prefix, mask, nhp_interface, next_hop, vrf, destvrf, state): 942 """operate ipv4 static route""" 943 description_xml = """\n""" 944 preference_xml = """\n""" 945 tag_xml = """\n""" 946 bfd_xml = """\n""" 947 if next_hop is None: 948 next_hop = '0.0.0.0' 949 if nhp_interface is None: 950 nhp_interface = "Invalid0" 951 952 if vrf is None: 953 vpn_instance = "_public_" 954 else: 955 vpn_instance = vrf 956 957 if destvrf is None: 958 dest_vpn_instance = "_public_" 959 else: 960 dest_vpn_instance = destvrf 961 962 description_xml = get_xml(CE_NC_SET_DESCRIPTION, self.description) 963 964 preference_xml = get_xml(CE_NC_SET_PREFERENCE, self.pref) 965 966 tag_xml = get_xml(CE_NC_SET_TAG, self.tag) 967 968 if self.function_flag == 'staticBFD': 969 if self.bfd_session_name: 970 bfd_xml = CE_NC_SET_BFDSESSIONNAME % self.bfd_session_name 971 else: 972 bfd_xml = CE_NC_SET_BFDENABLE 973 if state == "present": 974 configxmlstr = CE_NC_SET_STATIC_ROUTE % ( 975 vpn_instance, version, prefix, mask, nhp_interface, 976 dest_vpn_instance, next_hop, description_xml, preference_xml, tag_xml, bfd_xml) 977 978 else: 979 configxmlstr = CE_NC_DELETE_STATIC_ROUTE % ( 980 vpn_instance, version, prefix, mask, nhp_interface, dest_vpn_instance, next_hop) 981 982 conf_str = build_config_xml(configxmlstr) 983 recv_xml = set_nc_config(self.module, conf_str) 984 self._checkresponse_(recv_xml, "OPERATE_STATIC_ROUTE") 985 986 def get_change_state_global_bfd(self): 987 """get ipv4 global bfd change state""" 988 989 self.get_global_bfd(self.state) 990 change = False 991 if self.state == "present": 992 if self.static_routes_info["sroute_global_bfd"]: 993 for static_route in self.static_routes_info["sroute_global_bfd"]: 994 if static_route is not None: 995 if self.min_tx_interval is not None: 996 if int(static_route["minTxInterval"]) != self.min_tx_interval: 997 change = True 998 if self.min_rx_interval is not None: 999 if int(static_route["minRxInterval"]) != self.min_rx_interval: 1000 change = True 1001 if self.detect_multiplier is not None: 1002 if int(static_route["multiplier"]) != self.detect_multiplier: 1003 change = True 1004 return change 1005 else: 1006 continue 1007 else: 1008 change = True 1009 else: 1010 if self.commands: 1011 if self.static_routes_info["sroute_global_bfd"]: 1012 for static_route in self.static_routes_info["sroute_global_bfd"]: 1013 if static_route is not None: 1014 if int(static_route["minTxInterval"]) != 1000 or \ 1015 int(static_route["minRxInterval"]) != 1000 or \ 1016 int(static_route["multiplier"]) != 3: 1017 change = True 1018 return change 1019 1020 def get_global_bfd(self, state): 1021 """get ipv4 global bfd""" 1022 1023 self.static_routes_info["sroute_global_bfd"] = list() 1024 1025 getglobalbfdxmlstr = None 1026 if self.aftype == 'v4': 1027 getglobalbfdxmlstr = CE_NC_GET_STATIC_ROUTE_IPV4_GLOBAL_BFD 1028 1029 if getglobalbfdxmlstr is not None: 1030 xml_global_bfd_str = get_nc_config(self.module, getglobalbfdxmlstr) 1031 1032 if 'data/' in xml_global_bfd_str: 1033 return 1034 1035 xml_global_bfd_str = xml_global_bfd_str.replace('\r', '').replace('\n', ''). \ 1036 replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', ""). \ 1037 replace('xmlns="http://www.huawei.com/netconf/vrp"', "") 1038 root = ElementTree.fromstring(xml_global_bfd_str) 1039 static_routes_global_bfd = root.findall( 1040 "staticrt/staticrtbase/srIPv4StaticSite") 1041 1042 if static_routes_global_bfd: 1043 for static_route in static_routes_global_bfd: 1044 static_info = dict() 1045 for static_ele in static_route: 1046 if static_ele.tag == "minTxInterval": 1047 if static_ele.text is not None: 1048 static_info["minTxInterval"] = static_ele.text 1049 if static_ele.tag == "minRxInterval": 1050 if static_ele.text is not None: 1051 static_info["minRxInterval"] = static_ele.text 1052 if static_ele.tag == "multiplier": 1053 if static_ele.text is not None: 1054 static_info["multiplier"] = static_ele.text 1055 1056 self.static_routes_info["sroute_global_bfd"].append(static_info) 1057 1058 def get_change_state_single_bfd(self): 1059 """get ipv4 single bfd change state""" 1060 1061 self.get_single_bfd(self.state) 1062 change = False 1063 version = self.version 1064 if self.state == 'present': 1065 if self.static_routes_info["sroute_single_bfd"]: 1066 for static_route in self.static_routes_info["sroute_single_bfd"]: 1067 if static_route is not None and static_route['afType'] == version: 1068 if self.nhp_interface: 1069 if static_route["ifName"].lower() != self.nhp_interface.lower(): 1070 change = True 1071 if self.destvrf: 1072 if static_route["destVrfName"].lower() != self.destvrf.lower(): 1073 change = True 1074 if self.next_hop: 1075 if static_route["nexthop"].lower() != self.next_hop.lower(): 1076 change = True 1077 if self.prefix: 1078 if static_route["localAddress"].lower() != self.prefix.lower(): 1079 change = True 1080 if self.min_tx_interval: 1081 if int(static_route["minTxInterval"]) != self.min_tx_interval: 1082 change = True 1083 if self.min_rx_interval: 1084 if int(static_route["minRxInterval"]) != self.min_rx_interval: 1085 change = True 1086 if self.detect_multiplier: 1087 if int(static_route["multiplier"]) != self.detect_multiplier: 1088 change = True 1089 return change 1090 1091 else: 1092 continue 1093 else: 1094 change = True 1095 else: 1096 for static_route in self.static_routes_info["sroute_single_bfd"]: 1097 # undo ip route-static bfd [ interface-type interface-number | 1098 # vpn-instance vpn-instance-name ] nexthop-address 1099 1100 if static_route["ifName"] and self.nhp_interface: 1101 if static_route["ifName"].lower() == self.nhp_interface.lower() \ 1102 and static_route["nexthop"].lower() == self.next_hop.lower() \ 1103 and static_route["afType"] == version: 1104 change = True 1105 return change 1106 1107 if static_route["destVrfName"] and self.destvrf: 1108 if static_route["destVrfName"].lower() == self.destvrf.lower() \ 1109 and static_route["nexthop"].lower() == self.next_hop.lower() \ 1110 and static_route["afType"] == version: 1111 change = True 1112 return change 1113 1114 if static_route["nexthop"] and self.next_hop: 1115 if static_route["nexthop"].lower() == self.next_hop.lower() \ 1116 and static_route["afType"] == version: 1117 change = True 1118 return change 1119 else: 1120 continue 1121 change = False 1122 return change 1123 1124 def get_single_bfd(self, state): 1125 """get ipv4 sigle bfd""" 1126 self.static_routes_info["sroute_single_bfd"] = list() 1127 if self.aftype == "v4": 1128 version = "ipv4unicast" 1129 else: 1130 version = "ipv6unicast" 1131 if state == 'absent': 1132 getbfdxmlstr = CE_NC_GET_STATIC_ROUTE_BFD_ABSENT % ( 1133 version, self.nhp_interface, self.destvrf, self.next_hop) 1134 else: 1135 getbfdxmlstr = CE_NC_GET_STATIC_ROUTE_BFD % ( 1136 version, self.nhp_interface, self.destvrf, self.next_hop) 1137 xml_bfd_str = get_nc_config(self.module, getbfdxmlstr) 1138 1139 if 'data/' in xml_bfd_str: 1140 return 1141 xml_bfd_str = xml_bfd_str.replace('\r', '').replace('\n', ''). \ 1142 replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', ""). \ 1143 replace('xmlns="http://www.huawei.com/netconf/vrp"', "") 1144 root = ElementTree.fromstring(xml_bfd_str) 1145 static_routes_bfd = root.findall( 1146 "staticrt/staticrtbase/srBfdParas/srBfdPara") 1147 if static_routes_bfd: 1148 for static_route in static_routes_bfd: 1149 static_info = dict() 1150 for static_ele in static_route: 1151 if static_ele.tag in ["afType", "destVrfName", "nexthop", "ifName"]: 1152 static_info[static_ele.tag] = static_ele.text 1153 if static_ele.tag == "localAddress": 1154 if static_ele.text is not None: 1155 static_info["localAddress"] = static_ele.text 1156 else: 1157 static_info["localAddress"] = "None" 1158 if static_ele.tag == "minTxInterval": 1159 if static_ele.text is not None: 1160 static_info["minTxInterval"] = static_ele.text 1161 if static_ele.tag == "minRxInterval": 1162 if static_ele.text is not None: 1163 static_info["minRxInterval"] = static_ele.text 1164 if static_ele.tag == "multiplier": 1165 if static_ele.text is not None: 1166 static_info["multiplier"] = static_ele.text 1167 self.static_routes_info["sroute_single_bfd"].append(static_info) 1168 1169 def get_static_route(self, state): 1170 """get ipv4 static route about BFD""" 1171 self.static_routes_info["sroute"] = list() 1172 # Increase the parameter used to distinguish whether the incoming bfdSessionName 1173 static_bfd_flag = True 1174 if self.bfd_session_name: 1175 static_bfd_flag = False 1176 1177 if state == 'absent': 1178 getxmlstr = CE_NC_GET_STATIC_ROUTE_ABSENT 1179 else: 1180 # self.static_bfd_flag is true 1181 if static_bfd_flag: 1182 getxmlstr = CE_NC_GET_STATIC_ROUTE_BFD_ENABLE 1183 1184 else: 1185 getxmlstr = CE_NC_GET_STATIC_ROUTE_BFD_SESSIONNAME 1186 xml_str = get_nc_config(self.module, getxmlstr) 1187 if 'data/' in xml_str: 1188 return 1189 xml_str = xml_str.replace('\r', '').replace('\n', ''). \ 1190 replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', ""). \ 1191 replace('xmlns="http://www.huawei.com/netconf/vrp"', "") 1192 root = ElementTree.fromstring(xml_str) 1193 static_routes = root.findall( 1194 "staticrt/staticrtbase/srRoutes/srRoute") 1195 1196 if static_routes: 1197 for static_route in static_routes: 1198 static_info = dict() 1199 for static_ele in static_route: 1200 if static_ele.tag in ["vrfName", "afType", "topologyName", 1201 "prefix", "maskLength", "destVrfName", 1202 "nexthop", "ifName", "preference", "description"]: 1203 static_info[static_ele.tag] = static_ele.text 1204 if static_ele.tag == "tag": 1205 if static_ele.text is not None: 1206 static_info["tag"] = static_ele.text 1207 else: 1208 static_info["tag"] = "None" 1209 if static_bfd_flag: 1210 if static_ele.tag == "bfdEnable": 1211 if static_ele.text is not None: 1212 static_info["bfdEnable"] = static_ele.text 1213 else: 1214 static_info["bfdEnable"] = "None" 1215 else: 1216 if static_ele.tag == "sessionName": 1217 if static_ele.text is not None: 1218 static_info["sessionName"] = static_ele.text 1219 else: 1220 static_info["sessionName"] = "None" 1221 self.static_routes_info["sroute"].append(static_info) 1222 1223 def _checkparams_(self): 1224 """check all input params""" 1225 if self.function_flag == 'singleBFD': 1226 if not self.next_hop: 1227 self.module.fail_json(msg='Error: missing required argument: next_hop.') 1228 if self.state != 'absent': 1229 if self.nhp_interface == "Invalid0" and (not self.prefix or self.prefix == '0.0.0.0'): 1230 self.module.fail_json(msg='Error: If a nhp_interface is not configured, ' 1231 'the prefix must be configured.') 1232 1233 if self.function_flag != 'globalBFD': 1234 if self.function_flag == 'dynamicBFD' or self.function_flag == 'staticBFD': 1235 if not self.mask: 1236 self.module.fail_json(msg='Error: missing required argument: mask.') 1237 # check prefix and mask 1238 if not self.mask.isdigit(): 1239 self.module.fail_json(msg='Error: Mask is invalid.') 1240 if self.function_flag != 'singleBFD' or (self.function_flag == 'singleBFD' and self.destvrf != "_public_"): 1241 if not self.prefix: 1242 self.module.fail_json(msg='Error: missing required argument: prefix.') 1243 # convert prefix 1244 if not self._convertipprefix_(): 1245 self.module.fail_json(msg='Error: The %s is not a valid address' % self.prefix) 1246 1247 if self.nhp_interface != "Invalid0" and self.destvrf != "_public_": 1248 self.module.fail_json(msg='Error: Destination vrf dose not support next hop is interface.') 1249 1250 if not self.next_hop and self.nhp_interface == "Invalid0": 1251 self.module.fail_json(msg='Error: one of the following is required: next_hop,nhp_interface.') 1252 1253 if self.function_flag == 'dynamicBFD' or self.function_flag == 'staticBFD': 1254 # description check 1255 if self.description: 1256 if not is_valid_description(self.description): 1257 self.module.fail_json( 1258 msg='Error: Dsecription length should be 1 - 35, and can not contain "?".') 1259 # tag check 1260 if self.tag is not None: 1261 if not is_valid_tag(self.tag): 1262 self.module.fail_json( 1263 msg='Error: Tag should be integer 1 - 4294967295.') 1264 # preference check 1265 if self.pref is not None: 1266 if not is_valid_preference(self.pref): 1267 self.module.fail_json( 1268 msg='Error: Preference should be integer 1 - 255.') 1269 1270 if self.function_flag == 'staticBFD': 1271 if self.bfd_session_name: 1272 if not is_valid_bdf_session_name(self.bfd_session_name): 1273 self.module.fail_json( 1274 msg='Error: bfd_session_name length should be 1 - 15, and can not contain Space.') 1275 1276 # ipv4 check 1277 if self.aftype == "v4": 1278 if self.function_flag == 'dynamicBFD' or self.function_flag == 'staticBFD': 1279 if int(self.mask) > 32 or int(self.mask) < 0: 1280 self.module.fail_json( 1281 msg='Error: Ipv4 mask must be an integer between 1 and 32.') 1282 # next_hop check 1283 if self.function_flag != 'globalBFD': 1284 if self.next_hop: 1285 if not is_valid_v4addr(self.next_hop): 1286 self.module.fail_json( 1287 msg='Error: The %s is not a valid address.' % self.next_hop) 1288 # ipv6 check 1289 if self.aftype == "v6": 1290 if self.function_flag == 'dynamicBFD' or self.function_flag == 'staticBFD': 1291 if int(self.mask) > 128 or int(self.mask) < 0: 1292 self.module.fail_json( 1293 msg='Error: Ipv6 mask must be an integer between 1 and 128.') 1294 if self.function_flag != 'globalBFD': 1295 if self.next_hop: 1296 if not is_valid_v6addr(self.next_hop): 1297 self.module.fail_json( 1298 msg='Error: The %s is not a valid address.' % self.next_hop) 1299 1300 if self.function_flag == 'globalBFD' or self.function_flag == 'singleBFD': 1301 # BFD prarams 1302 if self.min_tx_interval: 1303 if not is_valid_bdf_interval(self.min_tx_interval): 1304 self.module.fail_json( 1305 msg='Error: min_tx_interval should be integer 50 - 1000.') 1306 if self.min_rx_interval: 1307 if not is_valid_bdf_interval(self.min_rx_interval): 1308 self.module.fail_json( 1309 msg='Error: min_rx_interval should be integer 50 - 1000.') 1310 if self.detect_multiplier: 1311 if not is_valid_bdf_multiplier(self.detect_multiplier): 1312 self.module.fail_json( 1313 msg='Error: detect_multiplier should be integer 3 - 50.') 1314 1315 if self.function_flag == 'globalBFD': 1316 if self.state != 'absent': 1317 if not self.min_tx_interval and not self.min_rx_interval and not self.detect_multiplier: 1318 self.module.fail_json( 1319 msg='Error: one of the following is required: min_tx_interval,' 1320 'detect_multiplier,min_rx_interval.') 1321 else: 1322 if not self.commands: 1323 self.module.fail_json( 1324 msg='Error: missing required argument: command.') 1325 if compare_command(self.commands): 1326 self.module.fail_json( 1327 msg='Error: The command %s line is incorrect.' % (',').join(self.commands)) 1328 1329 def set_ip_static_route_globalbfd(self): 1330 """set ip static route globalBFD""" 1331 if not self.changed: 1332 return 1333 if self.aftype == "v4": 1334 self.operate_static_route_globalbfd() 1335 1336 def set_ip_static_route_singlebfd(self): 1337 """set ip static route singleBFD""" 1338 if not self.changed: 1339 return 1340 version = None 1341 if self.aftype == "v4": 1342 version = "ipv4unicast" 1343 else: 1344 version = "ipv6unicast" 1345 self.operate_static_route_singlebfd(version, self.prefix, self.nhp_interface, 1346 self.next_hop, self.destvrf, self.state) 1347 1348 def set_ip_static_route(self): 1349 """set ip static route""" 1350 if not self.changed: 1351 return 1352 version = None 1353 if self.aftype == "v4": 1354 version = "ipv4unicast" 1355 else: 1356 version = "ipv6unicast" 1357 self.operate_static_route(version, self.prefix, self.mask, self.nhp_interface, 1358 self.next_hop, self.vrf, self.destvrf, self.state) 1359 1360 def is_prefix_exist(self, static_route, version): 1361 """is prefix mask nex_thop exist""" 1362 if static_route is None: 1363 return False 1364 if self.next_hop and self.nhp_interface: 1365 return static_route["prefix"].lower() == self.prefix.lower() \ 1366 and static_route["maskLength"] == self.mask \ 1367 and static_route["afType"] == version \ 1368 and static_route["ifName"].lower() == self.nhp_interface.lower() \ 1369 and static_route["nexthop"].lower() == self.next_hop.lower() 1370 1371 if self.next_hop and not self.nhp_interface: 1372 return static_route["prefix"].lower() == self.prefix.lower() \ 1373 and static_route["maskLength"] == self.mask \ 1374 and static_route["afType"] == version \ 1375 and static_route["nexthop"].lower() == self.next_hop.lower() 1376 1377 if not self.next_hop and self.nhp_interface: 1378 return static_route["prefix"].lower() == self.prefix.lower() \ 1379 and static_route["maskLength"] == self.mask \ 1380 and static_route["afType"] == version \ 1381 and static_route["ifName"].lower() == self.nhp_interface.lower() 1382 1383 def get_ip_static_route(self): 1384 """get ip static route""" 1385 change = False 1386 version = self.version 1387 self.get_static_route(self.state) 1388 change_list = list() 1389 if self.state == 'present': 1390 for static_route in self.static_routes_info["sroute"]: 1391 if self.is_prefix_exist(static_route, self.version): 1392 info_dict = dict() 1393 exist_dict = dict() 1394 if self.vrf: 1395 info_dict["vrfName"] = self.vrf 1396 exist_dict["vrfName"] = static_route["vrfName"] 1397 if self.destvrf: 1398 info_dict["destVrfName"] = self.destvrf 1399 exist_dict["destVrfName"] = static_route["destVrfName"] 1400 if self.description: 1401 info_dict["description"] = self.description 1402 exist_dict["description"] = static_route["description"] 1403 if self.tag: 1404 info_dict["tag"] = self.tag 1405 exist_dict["tag"] = static_route["tag"] 1406 if self.pref: 1407 info_dict["preference"] = str(self.pref) 1408 exist_dict["preference"] = static_route["preference"] 1409 if self.nhp_interface: 1410 if self.nhp_interface.lower() == "invalid0": 1411 info_dict["ifName"] = "Invalid0" 1412 else: 1413 info_dict["ifName"] = "Invalid0" 1414 exist_dict["ifName"] = static_route["ifName"] 1415 if self.next_hop: 1416 info_dict["nexthop"] = self.next_hop 1417 exist_dict["nexthop"] = static_route["nexthop"] 1418 1419 if self.bfd_session_name: 1420 info_dict["bfdEnable"] = 'true' 1421 1422 else: 1423 info_dict["bfdEnable"] = 'false' 1424 exist_dict["bfdEnable"] = static_route["bfdEnable"] 1425 1426 if exist_dict != info_dict: 1427 change = True 1428 else: 1429 change = False 1430 change_list.append(change) 1431 1432 if False in change_list: 1433 change = False 1434 else: 1435 change = True 1436 return change 1437 1438 else: 1439 for static_route in self.static_routes_info["sroute"]: 1440 if static_route["nexthop"] and self.next_hop: 1441 if static_route["prefix"].lower() == self.prefix.lower() \ 1442 and static_route["maskLength"] == self.mask \ 1443 and static_route["nexthop"].lower() == self.next_hop.lower() \ 1444 and static_route["afType"] == version: 1445 change = True 1446 return change 1447 if static_route["ifName"] and self.nhp_interface: 1448 if static_route["prefix"].lower() == self.prefix.lower() \ 1449 and static_route["maskLength"] == self.mask \ 1450 and static_route["ifName"].lower() == self.nhp_interface.lower() \ 1451 and static_route["afType"] == version: 1452 change = True 1453 return change 1454 else: 1455 continue 1456 change = False 1457 return change 1458 1459 def get_proposed(self): 1460 """get proposed information""" 1461 self.proposed['afType'] = self.aftype 1462 self.proposed['state'] = self.state 1463 if self.function_flag != 'globalBFD': 1464 self.proposed['ifName'] = self.nhp_interface 1465 self.proposed['destVrfName'] = self.destvrf 1466 self.proposed['next_hop'] = self.next_hop 1467 1468 if self.function_flag == 'singleBFD': 1469 if self.prefix: 1470 self.proposed['localAddress'] = self.prefix 1471 1472 if self.function_flag == 'globalBFD' or self.function_flag == 'singleBFD': 1473 self.proposed['minTxInterval'] = self.min_tx_interval 1474 self.proposed['minRxInterval'] = self.min_rx_interval 1475 self.proposed['multiplier'] = self.detect_multiplier 1476 1477 if self.function_flag != 'globalBFD' and self.function_flag != 'singleBFD': 1478 self.proposed['prefix'] = self.prefix 1479 self.proposed['mask'] = self.mask 1480 self.proposed['vrfName'] = self.vrf 1481 if self.tag: 1482 self.proposed['tag'] = self.tag 1483 if self.description: 1484 self.proposed['description'] = self.description 1485 if self.pref is None: 1486 self.proposed['preference'] = 60 1487 else: 1488 self.proposed['preference'] = self.pref 1489 1490 static_bfd_flag = True 1491 if self.bfd_session_name: 1492 static_bfd_flag = False 1493 if not static_bfd_flag: 1494 self.proposed['sessionName'] = self.bfd_session_name 1495 else: 1496 self.proposed['bfdEnable'] = 'true' 1497 1498 def get_existing(self): 1499 """get existing information""" 1500 # globalBFD 1501 if self.function_flag == 'globalBFD': 1502 change = self.get_change_state_global_bfd() 1503 self.existing['sroute_global_bfd'] = self.static_routes_info["sroute_global_bfd"] 1504 # singleBFD 1505 elif self.function_flag == 'singleBFD': 1506 change = self.get_change_state_single_bfd() 1507 self.existing['sroute_single_bfd'] = self.static_routes_info["sroute_single_bfd"] 1508 # dynamicBFD / staticBFD 1509 else: 1510 change = self.get_ip_static_route() 1511 self.existing['static_sroute'] = self.static_routes_info["sroute"] 1512 self.changed = bool(change) 1513 1514 def get_end_state(self): 1515 """get end state information""" 1516 1517 # globalBFD 1518 if self.function_flag == 'globalBFD': 1519 self.get_global_bfd(self.state) 1520 self.end_state['sroute_global_bfd'] = self.static_routes_info["sroute_global_bfd"] 1521 # singleBFD 1522 elif self.function_flag == 'singleBFD': 1523 self.static_routes_info["sroute_single_bfd"] = list() 1524 self.get_single_bfd(self.state) 1525 self.end_state['sroute_single_bfd'] = self.static_routes_info["sroute_single_bfd"] 1526 # dynamicBFD / staticBFD 1527 else: 1528 self.get_static_route(self.state) 1529 self.end_state['static_sroute'] = self.static_routes_info["sroute"] 1530 1531 def work(self): 1532 """worker""" 1533 self._checkparams_() 1534 self.get_existing() 1535 self.get_proposed() 1536 1537 if self.function_flag == 'globalBFD': 1538 self.set_ip_static_route_globalbfd() 1539 self.set_update_cmd_globalbfd() 1540 elif self.function_flag == 'singleBFD': 1541 self.set_ip_static_route_singlebfd() 1542 self.set_update_cmd_singlebfd() 1543 else: 1544 self.set_ip_static_route() 1545 self.set_update_cmd() 1546 1547 self.get_end_state() 1548 if self.existing == self.end_state: 1549 self.changed = False 1550 self.results['changed'] = self.changed 1551 self.results['proposed'] = self.proposed 1552 self.results['existing'] = self.existing 1553 self.results['end_state'] = self.end_state 1554 if self.changed: 1555 self.results['updates'] = self.updates_cmd 1556 else: 1557 self.results['updates'] = list() 1558 1559 self.module.exit_json(**self.results) 1560 1561 1562def main(): 1563 """main""" 1564 1565 argument_spec = dict( 1566 prefix=dict(type='str'), 1567 mask=dict(type='str'), 1568 aftype=dict(choices=['v4', 'v6'], required=True), 1569 next_hop=dict(type='str'), 1570 nhp_interface=dict(type='str'), 1571 vrf=dict(type='str'), 1572 destvrf=dict(type='str'), 1573 tag=dict(type='int'), 1574 description=dict(type='str'), 1575 pref=dict(type='int'), 1576 # bfd 1577 function_flag=dict(required=True, choices=['globalBFD', 'singleBFD', 'dynamicBFD', 'staticBFD']), 1578 min_tx_interval=dict(type='int'), 1579 min_rx_interval=dict(type='int'), 1580 detect_multiplier=dict(type='int'), 1581 # bfd session name 1582 bfd_session_name=dict(type='str'), 1583 commands=dict(type='list', required=False), 1584 state=dict(choices=['absent', 'present'], default='present', required=False), 1585 ) 1586 interface = StaticRouteBFD(argument_spec) 1587 interface.work() 1588 1589 1590if __name__ == '__main__': 1591 main() 1592