1# 2# -*- coding: utf-8 -*- 3# Copyright 2019 Red Hat 4# GNU General Public License v3.0+ 5# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 6""" 7The sonic_bgp_af class 8It is in this file where the current configuration (as dict) 9is compared to the provided configuration (as dict) and the command set 10necessary to bring the current configuration to it's desired end-state is 11created 12""" 13from __future__ import absolute_import, division, print_function 14__metaclass__ = type 15 16try: 17 from urllib import quote 18except ImportError: 19 from urllib.parse import quote 20 21from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( 22 ConfigBase, 23) 24from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( 25 to_list, 26 search_obj_in_list, 27 remove_empties 28) 29from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts 30from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( 31 to_request, 32 edit_config 33) 34from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( 35 dict_to_set, 36 update_states, 37 get_diff, 38 remove_empties_from_list, 39) 40from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import to_request 41from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils import ( 42 validate_bgps, 43) 44from ansible.module_utils.connection import ConnectionError 45 46PATCH = 'patch' 47DELETE = 'delete' 48TEST_KEYS = [ 49 {'config': {'vrf_name': '', 'bgp_as': ''}}, 50 {'afis': {'afi': '', 'safi': ''}}, 51 {'redistribute': {'protocol': ''}}, 52 {'advertise_prefix': {'afi': '', 'safi': ''}}, 53] 54 55 56class Bgp_af(ConfigBase): 57 """ 58 The sonic_bgp_af class 59 """ 60 61 gather_subset = [ 62 '!all', 63 '!min', 64 ] 65 66 gather_network_resources = [ 67 'bgp_af', 68 ] 69 70 network_instance_path = '/data/openconfig-network-instance:network-instances/network-instance' 71 protocol_bgp_path = 'protocols/protocol=BGP,bgp/bgp' 72 l2vpn_evpn_config_path = 'l2vpn-evpn/openconfig-bgp-evpn-ext:config' 73 afi_safi_path = 'global/afi-safis/afi-safi' 74 table_connection_path = 'table-connections/table-connection' 75 76 def __init__(self, module): 77 super(Bgp_af, self).__init__(module) 78 79 def get_bgp_af_facts(self): 80 """ Get the 'facts' (the current configuration) 81 82 :rtype: A dictionary 83 :returns: The current configuration as a dictionary 84 """ 85 facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) 86 bgp_af_facts = facts['ansible_network_resources'].get('bgp_af') 87 if not bgp_af_facts: 88 bgp_af_facts = [] 89 return bgp_af_facts 90 91 def execute_module(self): 92 """ Execute the module 93 94 :rtype: A dictionary 95 :returns: The result from module execution 96 """ 97 result = {'changed': False} 98 warnings = list() 99 existing_bgp_af_facts = self.get_bgp_af_facts() 100 commands, requests = self.set_config(existing_bgp_af_facts) 101 if commands and len(requests) > 0: 102 if not self._module.check_mode: 103 try: 104 edit_config(self._module, to_request(self._module, requests)) 105 except ConnectionError as exc: 106 self._module.fail_json(msg=str(exc), code=exc.code) 107 result['changed'] = True 108 result['commands'] = commands 109 110 changed_bgp_af_facts = self.get_bgp_af_facts() 111 112 result['before'] = existing_bgp_af_facts 113 if result['changed']: 114 result['after'] = changed_bgp_af_facts 115 116 result['warnings'] = warnings 117 return result 118 119 def set_config(self, existing_bgp_af_facts): 120 """ Collect the configuration from the args passed to the module, 121 collect the current configuration (as a dict from facts) 122 123 :rtype: A list 124 :returns: the commands necessary to migrate the current configuration 125 to the desired configuration 126 """ 127 want = self._module.params['config'] 128 have = existing_bgp_af_facts 129 resp = self.set_state(want, have) 130 return to_list(resp) 131 132 def set_state(self, want, have): 133 """ Select the appropriate function based on the state provided 134 135 :param want: the desired configuration as a dictionary 136 :param have: the current configuration as a dictionary 137 :rtype: A list 138 :returns: the commands necessary to migrate the current configuration 139 to the desired configuration 140 """ 141 commands = [] 142 requests = [] 143 state = self._module.params['state'] 144 145 diff = get_diff(want, have, TEST_KEYS) 146 147 if state == 'overridden': 148 commands, requests = self._state_overridden(want, have, diff) 149 elif state == 'deleted': 150 commands, requests = self._state_deleted(want, have, diff) 151 elif state == 'merged': 152 commands, requests = self._state_merged(want, have, diff) 153 elif state == 'replaced': 154 commands, requests = self._state_replaced(want, have, diff) 155 return commands, requests 156 157 def _state_merged(self, want, have, diff): 158 """ The command generator when state is merged 159 160 :param want: the additive configuration as a dictionary 161 :param obj_in_have: the current configuration as a dictionary 162 :rtype: A list 163 :returns: the commands necessary to merge the provided into 164 the current configuration 165 """ 166 commands = diff 167 validate_bgps(self._module, commands, have) 168 requests = self.get_modify_bgp_af_requests(commands, have) 169 if commands and len(requests) > 0: 170 commands = update_states(commands, "merged") 171 else: 172 commands = [] 173 174 return commands, requests 175 176 def _state_deleted(self, want, have, diff): 177 """ The command generator when state is deleted 178 179 :param want: the objects from which the configuration should be removed 180 :param obj_in_have: the current configuration as a dictionary 181 :rtype: A list 182 :returns: the commands necessary to remove the current configuration 183 of the provided objects 184 """ 185 # if want is none, then delete all the bgp_afs 186 is_delete_all = False 187 if not want: 188 commands = have 189 is_delete_all = True 190 else: 191 commands = want 192 193 requests = self.get_delete_bgp_af_requests(commands, have, is_delete_all) 194 195 if commands and len(requests) > 0: 196 commands = update_states(commands, "deleted") 197 else: 198 commands = [] 199 return commands, requests 200 201 def get_modify_address_family_request(self, vrf_name, conf_afi, conf_safi): 202 afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper() 203 url = '%s=%s/%s/global' % (self.network_instance_path, vrf_name, self.protocol_bgp_path) 204 afi_safi_load = {'afi-safi-name': ("openconfig-bgp-types:%s" % (afi_safi))} 205 afi_safis_load = {'afi-safis': {'afi-safi': [afi_safi_load]}} 206 pay_load = {'openconfig-network-instance:global': afi_safis_load} 207 208 return({"path": url, "method": PATCH, "data": pay_load}) 209 210 def get_modify_advertise_request(self, vrf_name, conf_afi, conf_safi, conf_addr_fam): 211 request = None 212 conf_adv_all_vni = conf_addr_fam.get('advertise_all_vni', None) 213 conf_adv_default_gw = conf_addr_fam.get('advertise_default_gw', None) 214 conf_advt_list = conf_addr_fam.get('advertise_prefix', None) 215 afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper() 216 evpn_cfg = {} 217 if conf_adv_all_vni: 218 evpn_cfg['advertise-all-vni'] = conf_adv_all_vni 219 220 if conf_adv_default_gw: 221 evpn_cfg['advertise-default-gw'] = conf_adv_default_gw 222 223 adv_prefix_cfg = [] 224 if conf_advt_list is not None: 225 for adv_prefix in conf_advt_list: 226 adv_prefix_cfg.append("openconfig-bgp-types:" + ("%s_%s" % (adv_prefix['afi'], adv_prefix['safi'])).upper()) 227 if adv_prefix_cfg: 228 evpn_cfg['advertise-list'] = adv_prefix_cfg 229 230 if evpn_cfg: 231 url = '%s=%s/%s/global' % (self.network_instance_path, vrf_name, self.protocol_bgp_path) 232 afi_safi_load = {'afi-safi-name': ("openconfig-bgp-types:%s" % (afi_safi))} 233 afi_safi_load['l2vpn-evpn'] = {'openconfig-bgp-evpn-ext:config': evpn_cfg} 234 afi_safis_load = {'afi-safis': {'afi-safi': [afi_safi_load]}} 235 pay_load = {'openconfig-network-instance:global': afi_safis_load} 236 request = {"path": url, "method": PATCH, "data": pay_load} 237 238 return request 239 240 def get_modify_redistribute_requests(self, vrf_name, conf_afi, conf_safi, conf_redis_arr): 241 requests = [] 242 url = "%s=%s/table-connections" % (self.network_instance_path, vrf_name) 243 cfgs = [] 244 for conf_redis in conf_redis_arr: 245 conf_metric = conf_redis.get('metric', None) 246 if conf_metric is not None: 247 conf_metric = float(conf_redis['metric']) 248 249 afi_cfg = "openconfig-types:%s" % (conf_afi.upper()) 250 cfg_data = {'address-family': afi_cfg} 251 cfg_data['dst-protocol'] = "openconfig-policy-types:BGP" 252 conf_protocol = conf_redis['protocol'].upper() 253 if conf_protocol == 'CONNECTED': 254 conf_protocol = "DIRECTLY_CONNECTED" 255 cfg_data['src-protocol'] = "openconfig-policy-types:%s" % (conf_protocol) 256 cfg_data['config'] = {'address-family': afi_cfg} 257 if conf_metric is not None: 258 cfg_data['config']['openconfig-network-instance-ext:metric'] = conf_metric 259 260 conf_route_map = conf_redis.get('route_map', None) 261 if conf_route_map: 262 cfg_data['config']['import-policy'] = [conf_route_map] 263 264 cfgs.append(cfg_data) 265 266 if cfgs: 267 pay_load = {'openconfig-network-instance:table-connections': {'table-connection': cfgs}} 268 requests.append({"path": url, "method": PATCH, "data": pay_load}) 269 return requests 270 271 def get_modify_max_path_request(self, vrf_name, conf_afi, conf_safi, conf_max_path): 272 request = None 273 afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper() 274 url = '%s=%s/%s/' % (self.network_instance_path, vrf_name, self.protocol_bgp_path) 275 url += '%s=%s/use-multiple-paths' % (self.afi_safi_path, afi_safi) 276 conf_ebgp = conf_max_path.get('ebgp', None) 277 conf_ibgp = conf_max_path.get('ibgp', None) 278 max_path_load = {} 279 if conf_ebgp: 280 max_path_load['ebgp'] = {'config': {'maximum-paths': conf_ebgp}} 281 if conf_ibgp: 282 max_path_load['ibgp'] = {'config': {'maximum-paths': conf_ibgp}} 283 284 pay_load = {} 285 if max_path_load: 286 pay_load['openconfig-network-instance:use-multiple-paths'] = max_path_load 287 288 request = {"path": url, "method": PATCH, "data": pay_load} 289 return request 290 291 def get_modify_network_request(self, vrf_name, conf_afi, conf_safi, conf_network): 292 request = None 293 afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper() 294 url = '%s=%s/%s/' % (self.network_instance_path, vrf_name, self.protocol_bgp_path) 295 url += '%s=%s/openconfig-bgp-ext:network-config' % (self.afi_safi_path, afi_safi) 296 network_payload = [] 297 for each in conf_network: 298 payload = {} 299 payload = {'config': {'prefix': each}, 'prefix': each} 300 network_payload.append(payload) 301 if network_payload: 302 new_payload = {'openconfig-bgp-ext:network-config': {'network': network_payload}} 303 304 request = {"path": url, "method": PATCH, "data": new_payload} 305 return request 306 307 def get_modify_dampening_request(self, vrf_name, conf_afi, conf_safi, conf_dampening): 308 request = None 309 afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper() 310 url = '%s=%s/%s/' % (self.network_instance_path, vrf_name, self.protocol_bgp_path) 311 url += '%s=%s/openconfig-bgp-ext:route-flap-damping' % (self.afi_safi_path, afi_safi) 312 damp_payload = {'openconfig-bgp-ext:route-flap-damping': {'config': {'enabled': conf_dampening}}} 313 if damp_payload: 314 request = {"path": url, "method": PATCH, "data": damp_payload} 315 return request 316 317 def get_modify_single_af_request(self, vrf_name, conf_afi, conf_safi, conf_addr_fam): 318 requests = [] 319 320 requests.append(self.get_modify_address_family_request(vrf_name, conf_afi, conf_safi)) 321 if conf_afi == 'ipv4' and conf_safi == 'unicast': 322 conf_dampening = conf_addr_fam.get('dampening', None) 323 if conf_dampening: 324 request = self.get_modify_dampening_request(vrf_name, conf_afi, conf_safi, conf_dampening) 325 if request: 326 requests.append(request) 327 if conf_afi in ['ipv4', 'ipv6'] and conf_safi == 'unicast': 328 conf_redis_arr = conf_addr_fam.get('redistribute', []) 329 if conf_redis_arr: 330 requests.extend(self.get_modify_redistribute_requests(vrf_name, conf_afi, conf_safi, conf_redis_arr)) 331 conf_max_path = conf_addr_fam.get('max_path', None) 332 if conf_max_path: 333 request = self.get_modify_max_path_request(vrf_name, conf_afi, conf_safi, conf_max_path) 334 if request: 335 requests.append(request) 336 conf_network = conf_addr_fam.get('network', []) 337 if conf_network: 338 request = self.get_modify_network_request(vrf_name, conf_afi, conf_safi, conf_network) 339 if request: 340 requests.append(request) 341 elif conf_afi == "l2vpn" and conf_safi == 'evpn': 342 adv_req = self.get_modify_advertise_request(vrf_name, conf_afi, conf_safi, conf_addr_fam) 343 if adv_req: 344 requests.append(adv_req) 345 346 return requests 347 348 def get_modify_all_af_requests(self, conf_addr_fams, vrf_name): 349 requests = [] 350 for conf_addr_fam in conf_addr_fams: 351 conf_afi = conf_addr_fam.get('afi', None) 352 conf_safi = conf_addr_fam.get('safi', None) 353 if conf_afi and conf_safi: 354 requests.extend(self.get_modify_single_af_request(vrf_name, conf_afi, conf_safi, conf_addr_fam)) 355 return requests 356 357 def get_modify_requests(self, conf, match, vrf_name): 358 requests = [] 359 payload = {} 360 conf_addr_fams = conf.get('address_family', None) 361 if conf_addr_fams: 362 conf_addr_fams = conf_addr_fams.get('afis', []) 363 364 mat_addr_fams = [] 365 if match: 366 mat_addr_fams = match.get('address_family', None) 367 if mat_addr_fams: 368 mat_addr_fams = mat_addr_fams.get('afis', []) 369 370 if conf_addr_fams and not mat_addr_fams: 371 requests.extend(self.get_modify_all_af_requests(conf_addr_fams, vrf_name)) 372 else: 373 for conf_addr_fam in conf_addr_fams: 374 conf_afi = conf_addr_fam.get('afi', None) 375 conf_safi = conf_addr_fam.get('safi', None) 376 377 if conf_afi is None or conf_safi is None: 378 continue 379 380 mat_addr_fam = next((e_addr_fam for e_addr_fam in mat_addr_fams if (e_addr_fam['afi'] == conf_afi and e_addr_fam['safi'] == conf_safi)), None) 381 382 if mat_addr_fam is None: 383 requests.extend(self.get_modify_single_af_request(vrf_name, conf_afi, conf_safi, conf_addr_fam)) 384 continue 385 386 if conf_afi == 'ipv4' and conf_safi == 'unicast': 387 conf_dampening = conf_addr_fam.get('dampening', None) 388 if conf_dampening: 389 request = self.get_modify_dampening_request(vrf_name, conf_afi, conf_safi, conf_dampening) 390 if request: 391 requests.append(request) 392 393 if conf_afi == "l2vpn" and conf_safi == "evpn": 394 adv_req = self.get_modify_advertise_request(vrf_name, conf_afi, conf_safi, conf_addr_fam) 395 if adv_req: 396 requests.append(adv_req) 397 elif conf_afi in ["ipv4", "ipv6"] and conf_safi == "unicast": 398 conf_redis_arr = conf_addr_fam.get('redistribute', []) 399 conf_max_path = conf_addr_fam.get('max_path', None) 400 conf_network = conf_addr_fam.get('network', []) 401 if not conf_redis_arr and not conf_max_path and not conf_network: 402 continue 403 404 url = "%s=%s/table-connections" % (self.network_instance_path, vrf_name) 405 pay_loads = [] 406 modify_redis_arr = [] 407 for conf_redis in conf_redis_arr: 408 conf_metric = conf_redis.get('metric', None) 409 if conf_metric is not None: 410 conf_metric = float(conf_redis['metric']) 411 412 conf_route_map = conf_redis.get('route_map', None) 413 414 have_redis_arr = mat_addr_fam.get('redistribute', []) 415 have_redis = None 416 have_route_map = None 417 # Check the route_map, if existing route_map is different from required route_map, delete the existing route map 418 if conf_route_map and have_redis_arr: 419 have_redis = next((redis_cfg for redis_cfg in have_redis_arr if conf_redis['protocol'] == redis_cfg['protocol']), None) 420 if have_redis: 421 have_route_map = have_redis.get('route_map', None) 422 if have_route_map and have_route_map != conf_route_map: 423 requests.append(self.get_delete_route_map_request(vrf_name, conf_afi, have_redis, have_route_map)) 424 425 modify_redis = {} 426 if conf_metric is not None: 427 modify_redis['metric'] = conf_metric 428 if conf_route_map: 429 modify_redis['route_map'] = conf_route_map 430 431 if modify_redis: 432 modify_redis['protocol'] = conf_redis['protocol'] 433 modify_redis_arr.append(modify_redis) 434 435 if modify_redis_arr: 436 requests.extend(self.get_modify_redistribute_requests(vrf_name, conf_afi, conf_safi, modify_redis_arr)) 437 if conf_max_path: 438 max_path_req = self.get_modify_max_path_request(vrf_name, conf_afi, conf_safi, conf_max_path) 439 if max_path_req: 440 requests.append(max_path_req) 441 442 if conf_network: 443 network_req = self.get_modify_network_request(vrf_name, conf_afi, conf_safi, conf_network) 444 if network_req: 445 requests.append(network_req) 446 447 return requests 448 449 def get_modify_bgp_af_requests(self, commands, have): 450 requests = [] 451 if not commands: 452 return requests 453 454 # Create URL and payload 455 for conf in commands: 456 vrf_name = conf['vrf_name'] 457 as_val = conf['bgp_as'] 458 459 match = next((cfg for cfg in have if (cfg['vrf_name'] == vrf_name and (cfg['bgp_as'] == as_val))), None) 460 modify_reqs = self.get_modify_requests(conf, match, vrf_name) 461 if modify_reqs: 462 requests.extend(modify_reqs) 463 464 return requests 465 466 def get_delete_advertise_list_request(self, vrf_name, conf_afi, conf_safi, conf_advt_list=None, mat_advt_list=None): 467 requests = [] 468 afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper() 469 url = '%s=%s/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path) 470 url += '/%s=%s/%s/advertise-list' % (self.afi_safi_path, afi_safi, self.l2vpn_evpn_config_path) 471 472 if conf_advt_list and mat_advt_list: 473 del_adv_list = [] 474 existing_list_len = len(mat_advt_list) 475 for adv in conf_advt_list: 476 diff = get_diff({'advertise_prefix': [adv]}, {'advertise_prefix': mat_advt_list}, [{'advertise_prefix': {'afi': '', 'safi': ''}}]) 477 if not diff: 478 del_adv_list.append(adv) 479 del_adv_list_len = len(del_adv_list) 480 if existing_list_len > 0 and existing_list_len == del_adv_list_len: 481 requests.append({"path": url, "method": DELETE}) 482 else: 483 for del_adv in del_adv_list: 484 del_afi_safi = ("=%s_%s" % (del_adv['afi'], del_adv['safi'])).upper() 485 url += del_afi_safi 486 requests.append({"path": url, "method": DELETE}) 487 else: 488 requests.append({"path": url, "method": DELETE}) 489 490 return requests 491 492 def get_delete_advertise_default_gw_request(self, vrf_name, conf_afi, conf_safi): 493 afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper() 494 url = '%s=%s/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path) 495 url += '/%s=%s/%s/advertise-default-gw' % (self.afi_safi_path, afi_safi, self.l2vpn_evpn_config_path) 496 497 return({"path": url, "method": DELETE}) 498 499 def get_delete_dampening_request(self, vrf_name, conf_afi, conf_safi): 500 afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper() 501 url = '%s=%s/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path) 502 url += '/%s=%s/openconfig-bgp-ext:route-flap-damping/config/enabled' % (self.afi_safi_path, afi_safi) 503 504 return({"path": url, "method": DELETE}) 505 506 def get_delete_advertise_all_vni_request(self, vrf_name, conf_afi, conf_safi): 507 afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper() 508 url = '%s=%s/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path) 509 url += '/%s=%s/%s/advertise-all-vni' % (self.afi_safi_path, afi_safi, self.l2vpn_evpn_config_path) 510 511 return({"path": url, "method": DELETE}) 512 513 def get_delete_address_family_request(self, vrf_name, conf_afi, conf_safi): 514 request = None 515 516 if conf_afi != "l2vpn": 517 afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper() 518 url = '%s=%s/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path) 519 url += '/%s=openconfig-bgp-types:%s' % (self.afi_safi_path, afi_safi) 520 request = {"path": url, "method": DELETE} 521 522 return request 523 524 def get_delete_single_bgp_af_request(self, conf, is_delete_all, match=None): 525 requests = [] 526 vrf_name = conf['vrf_name'] 527 528 conf_addr_fams = conf.get('address_family', None) 529 if conf_addr_fams is None: 530 return requests 531 532 conf_addr_fams = conf_addr_fams.get('afis', []) 533 534 if match and not conf_addr_fams: 535 conf_addr_fams = match.get('address_family', None) 536 if conf_addr_fams: 537 conf_addr_fams = conf_addr_fams.get('afis', []) 538 conf_addr_fams = [{'afi': af['afi'], 'safi': af['safi']} for af in conf_addr_fams] 539 540 if not conf_addr_fams: 541 return requests 542 543 for conf_addr_fam in conf_addr_fams: 544 conf_afi = conf_addr_fam.get('afi', None) 545 conf_safi = conf_addr_fam.get('safi', None) 546 if not conf_afi or not conf_safi: 547 continue 548 conf_redis_arr = conf_addr_fam.get('redistribute', []) 549 conf_adv_all_vni = conf_addr_fam.get('advertise_all_vni', None) 550 conf_adv_default_gw = conf_addr_fam.get('advertise_default_gw', None) 551 conf_advt_list = conf_addr_fam.get('advertise_prefix', []) 552 conf_max_path = conf_addr_fam.get('max_path', None) 553 conf_dampening = conf_addr_fam.get('dampening', None) 554 conf_network = conf_addr_fam.get('network', []) 555 if is_delete_all: 556 if conf_adv_all_vni: 557 requests.append(self.get_delete_advertise_all_vni_request(vrf_name, conf_afi, conf_safi)) 558 if conf_dampening: 559 requests.append(self.get_delete_dampening_request(vrf_name, conf_afi, conf_safi)) 560 if conf_network: 561 requests.extend(self.get_delete_network_request(vrf_name, conf_afi, conf_safi, conf_network, is_delete_all, None)) 562 if conf_adv_default_gw: 563 requests.append(self.get_delete_advertise_default_gw_request(vrf_name, conf_afi, conf_safi)) 564 if conf_advt_list: 565 requests.extend(self.get_delete_advertise_list_request(vrf_name, conf_afi, conf_safi)) 566 if conf_redis_arr: 567 requests.extend(self.get_delete_redistribute_requests(vrf_name, conf_afi, conf_safi, conf_redis_arr, is_delete_all, None)) 568 if conf_max_path: 569 requests.extend(self.get_delete_max_path_requests(vrf_name, conf_afi, conf_safi, conf_max_path, is_delete_all, None)) 570 addr_family_del_req = self.get_delete_address_family_request(vrf_name, conf_afi, conf_safi) 571 if addr_family_del_req: 572 requests.append(addr_family_del_req) 573 elif match: 574 match_addr_fams = match.get('address_family', None) 575 if match_addr_fams: 576 match_addr_fams = match_addr_fams.get('afis', []) 577 if not match_addr_fams: 578 continue 579 for match_addr_fam in match_addr_fams: 580 mat_afi = match_addr_fam.get('afi', None) 581 mat_safi = match_addr_fam.get('safi', None) 582 if mat_afi and mat_safi and mat_afi == conf_afi and mat_safi == conf_safi: 583 mat_advt_all_vni = match_addr_fam.get('advertise_all_vni', None) 584 mat_redis_arr = match_addr_fam.get('redistribute', []) 585 mat_advt_defaut_gw = match_addr_fam.get('advertise_default_gw', None) 586 mat_advt_list = match_addr_fam.get('advertise_prefix', []) 587 mat_max_path = match_addr_fam.get('max_path', None) 588 mat_dampening = match_addr_fam.get('dampening', None) 589 mat_network = match_addr_fam.get('network', []) 590 if (conf_adv_all_vni is None and not conf_redis_arr and conf_adv_default_gw is None 591 and not conf_advt_list and not conf_max_path and conf_dampening is None and not conf_network): 592 if mat_advt_all_vni is not None: 593 requests.append(self.get_delete_advertise_all_vni_request(vrf_name, conf_afi, conf_safi)) 594 if mat_dampening is not None: 595 requests.append(self.get_delete_dampening_request(vrf_name, conf_afi, conf_safi)) 596 if mat_advt_defaut_gw: 597 requests.append(self.get_delete_advertise_default_gw_request(vrf_name, conf_afi, conf_safi)) 598 if mat_advt_list: 599 requests.extend(self.get_delete_advertise_list_request(vrf_name, conf_afi, conf_safi)) 600 if mat_redis_arr: 601 requests.extend(self.get_delete_redistribute_requests(vrf_name, conf_afi, conf_safi, mat_redis_arr, False, mat_redis_arr)) 602 if mat_max_path: 603 requests.extend(self.get_delete_max_path_requests(vrf_name, conf_afi, conf_safi, mat_max_path, is_delete_all, mat_max_path)) 604 if mat_network: 605 requests.extend(self.get_delete_network_request(vrf_name, conf_afi, conf_safi, mat_network, False, mat_network)) 606 addr_family_del_req = self.get_delete_address_family_request(vrf_name, conf_afi, conf_safi) 607 if addr_family_del_req: 608 requests.append(addr_family_del_req) 609 else: 610 if conf_adv_all_vni and mat_advt_all_vni: 611 requests.append(self.get_delete_advertise_all_vni_request(vrf_name, conf_afi, conf_safi)) 612 if conf_dampening and mat_dampening: 613 requests.append(self.get_delete_dampening_request(vrf_name, conf_afi, conf_safi)) 614 if conf_adv_default_gw and mat_advt_defaut_gw: 615 requests.append(self.get_delete_advertise_default_gw_request(vrf_name, conf_afi, conf_safi)) 616 if conf_advt_list is not None and mat_advt_list is not None: 617 requests.extend(self.get_delete_advertise_list_request(vrf_name, conf_afi, conf_safi, conf_advt_list, mat_advt_list)) 618 if conf_redis_arr and mat_redis_arr: 619 requests.extend(self.get_delete_redistribute_requests(vrf_name, conf_afi, conf_safi, conf_redis_arr, False, mat_redis_arr)) 620 if conf_max_path and mat_max_path: 621 requests.extend(self.get_delete_max_path_requests(vrf_name, conf_afi, conf_safi, conf_max_path, is_delete_all, mat_max_path)) 622 if conf_network and mat_network: 623 requests.extend(self.get_delete_network_request(vrf_name, conf_afi, conf_safi, conf_network, False, mat_network)) 624 break 625 626 return requests 627 628 def get_delete_network_request(self, vrf_name, conf_afi, conf_safi, conf_network, is_delete_all, mat_network): 629 requests = [] 630 afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper() 631 url = '%s=%s/%s/' % (self.network_instance_path, vrf_name, self.protocol_bgp_path) 632 url += '%s=%s/openconfig-bgp-ext:network-config/network=' % (self.afi_safi_path, afi_safi) 633 mat_list = [] 634 for conf in conf_network: 635 if mat_network: 636 mat_prefix = next((pre for pre in mat_network if pre == conf), None) 637 if mat_prefix: 638 mat_list.append(mat_prefix) 639 if not is_delete_all and mat_list: 640 for each in mat_list: 641 tmp = each.replace('/', '%2f') 642 requests.append({'path': url + tmp, 'method': DELETE}) 643 elif is_delete_all: 644 for each in conf_network: 645 tmp = each.replace('/', '%2f') 646 requests.append({'path': url + tmp, 'method': DELETE}) 647 return requests 648 649 def get_delete_max_path_requests(self, vrf_name, conf_afi, conf_safi, conf_max_path, is_delete_all, mat_max_path): 650 requests = [] 651 afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper() 652 url = '%s=%s/%s/' % (self.network_instance_path, vrf_name, self.protocol_bgp_path) 653 url += '%s=%s/use-multiple-paths/' % (self.afi_safi_path, afi_safi) 654 655 conf_ebgp = conf_max_path.get('ebgp', None) 656 conf_ibgp = conf_max_path.get('ibgp', None) 657 mat_ebgp = None 658 mat_ibgp = None 659 if mat_max_path: 660 mat_ebgp = mat_max_path.get('ebgp', None) 661 mat_ibgp = mat_max_path.get('ibgp', None) 662 663 if (conf_ebgp and mat_ebgp) or is_delete_all: 664 requests.append({'path': url + 'ebgp', 'method': DELETE}) 665 if (conf_ibgp and mat_ibgp) or is_delete_all: 666 requests.append({'path': url + 'ibgp', 'method': DELETE}) 667 668 return requests 669 670 def get_delete_route_map_request(self, vrf_name, conf_afi, conf_redis, conf_route_map): 671 addr_family = "openconfig-types:%s" % (conf_afi.upper()) 672 conf_protocol = conf_redis['protocol'].upper() 673 if conf_protocol == 'CONNECTED': 674 conf_protocol = "DIRECTLY_CONNECTED" 675 src_protocol = "openconfig-policy-types:%s" % (conf_protocol) 676 dst_protocol = "openconfig-policy-types:BGP" 677 url = '%s=%s/%s=' % (self.network_instance_path, vrf_name, self.table_connection_path) 678 url += '%s,%s,%s/config/import-policy=%s' % (src_protocol, dst_protocol, addr_family, conf_route_map) 679 return({'path': url, 'method': DELETE}) 680 681 def get_delete_redistribute_requests(self, vrf_name, conf_afi, conf_safi, conf_redis_arr, is_delete_all, mat_redis_arr): 682 requests = [] 683 for conf_redis in conf_redis_arr: 684 addr_family = "openconfig-types:%s" % (conf_afi.upper()) 685 conf_protocol = conf_redis['protocol'].upper() 686 687 ext_metric_flag = False 688 ext_route_flag = False 689 mat_redis = None 690 mat_metric = None 691 mat_route_map = None 692 if not is_delete_all: 693 mat_redis = next((redis_cfg for redis_cfg in mat_redis_arr if redis_cfg['protocol'].upper() == conf_protocol), None) 694 if mat_redis: 695 mat_metric = mat_redis.get('metric', None) 696 mat_route_map = mat_redis.get('route_map', None) 697 if mat_metric: 698 ext_metric_flag = True 699 if mat_route_map: 700 ext_route_flag = True 701 702 if conf_protocol == 'CONNECTED': 703 conf_protocol = "DIRECTLY_CONNECTED" 704 705 src_protocol = "openconfig-policy-types:%s" % (conf_protocol) 706 dst_protocol = "openconfig-policy-types:BGP" 707 708 conf_route_map = conf_redis.get('route_map', None) 709 conf_metric = conf_redis.get('metric', None) 710 if conf_metric is not None: 711 conf_metric = float(conf_redis['metric']) 712 713 url = '%s=%s/%s=' % (self.network_instance_path, vrf_name, self.table_connection_path) 714 715 new_metric_flag = conf_metric is not None 716 new_route_flag = conf_route_map is not None 717 is_delete_protocol = False 718 if is_delete_all: 719 is_delete_protocol = True 720 else: 721 is_delete_protocol = (new_metric_flag == ext_metric_flag) and (new_route_flag == ext_route_flag) 722 723 if is_delete_protocol: 724 url += '%s,%s,%s' % (src_protocol, dst_protocol, addr_family) 725 requests.append({'path': url, 'method': DELETE}) 726 continue 727 728 if new_metric_flag and ext_metric_flag: 729 url += '%s,%s,%s/config/openconfig-network-instance-ext:metric' % (src_protocol, dst_protocol, addr_family) 730 requests.append({'path': url, 'method': DELETE}) 731 732 if new_route_flag and ext_route_flag: 733 url += '%s,%s,%s/config/import-policy=%s' % (src_protocol, dst_protocol, addr_family, conf_route_map) 734 requests.append({'path': url, 'method': DELETE}) 735 736 return requests 737 738 def get_delete_bgp_af_requests(self, commands, have, is_delete_all): 739 requests = [] 740 for cmd in commands: 741 vrf_name = cmd['vrf_name'] 742 as_val = cmd['bgp_as'] 743 match_cfg = None 744 if not is_delete_all: 745 match_cfg = next((have_cfg for have_cfg in have if have_cfg['vrf_name'] == vrf_name and have_cfg['bgp_as'] == as_val), None) 746 requests.extend(self.get_delete_single_bgp_af_request(cmd, is_delete_all, match_cfg)) 747 return requests 748