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