backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / services / protocols / bgp / info_base / vrf.py
1 # Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #    http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12 # implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 """
17  Defines base data types and models required specifically for VRF support.
18 """
19
20 import abc
21 import logging
22 import six
23
24 from ryu.lib.packet.bgp import BGP_ATTR_TYPE_ORIGIN
25 from ryu.lib.packet.bgp import BGP_ATTR_TYPE_AS_PATH
26 from ryu.lib.packet.bgp import BGP_ATTR_TYPE_EXTENDED_COMMUNITIES
27 from ryu.lib.packet.bgp import BGP_ATTR_TYEP_PMSI_TUNNEL_ATTRIBUTE
28 from ryu.lib.packet.bgp import BGP_ATTR_TYPE_MULTI_EXIT_DISC
29 from ryu.lib.packet.bgp import BGPPathAttributeOrigin
30 from ryu.lib.packet.bgp import BGPPathAttributeAsPath
31 from ryu.lib.packet.bgp import EvpnEthernetSegmentNLRI
32 from ryu.lib.packet.bgp import BGPPathAttributeExtendedCommunities
33 from ryu.lib.packet.bgp import BGPPathAttributeMultiExitDisc
34 from ryu.lib.packet.bgp import BGPEncapsulationExtendedCommunity
35 from ryu.lib.packet.bgp import BGPEvpnEsiLabelExtendedCommunity
36 from ryu.lib.packet.bgp import BGPEvpnEsImportRTExtendedCommunity
37 from ryu.lib.packet.bgp import BGPPathAttributePmsiTunnel
38 from ryu.lib.packet.bgp import PmsiTunnelIdIngressReplication
39 from ryu.lib.packet.bgp import RF_L2_EVPN
40 from ryu.lib.packet.bgp import EvpnMacIPAdvertisementNLRI
41 from ryu.lib.packet.bgp import EvpnIpPrefixNLRI
42 from ryu.lib.packet.safi import (
43     IP_FLOWSPEC,
44     VPN_FLOWSPEC,
45 )
46
47 from ryu.services.protocols.bgp.base import OrderedDict
48 from ryu.services.protocols.bgp.constants import VPN_TABLE
49 from ryu.services.protocols.bgp.constants import VRF_TABLE
50 from ryu.services.protocols.bgp.info_base.base import Destination
51 from ryu.services.protocols.bgp.info_base.base import Path
52 from ryu.services.protocols.bgp.info_base.base import Table
53 from ryu.services.protocols.bgp.utils.bgp import create_rt_extended_community
54 from ryu.services.protocols.bgp.utils.stats import LOCAL_ROUTES
55 from ryu.services.protocols.bgp.utils.stats import REMOTE_ROUTES
56 from ryu.services.protocols.bgp.utils.stats import RESOURCE_ID
57 from ryu.services.protocols.bgp.utils.stats import RESOURCE_NAME
58
59 LOG = logging.getLogger('bgpspeaker.info_base.vrf')
60
61
62 @six.add_metaclass(abc.ABCMeta)
63 class VrfTable(Table):
64     """Virtual Routing and Forwarding information base.
65      Keeps destination imported to given vrf in represents.
66      """
67
68     ROUTE_FAMILY = None
69     VPN_ROUTE_FAMILY = None
70     NLRI_CLASS = None
71     VRF_PATH_CLASS = None
72     VRF_DEST_CLASS = None
73
74     def __init__(self, vrf_conf, core_service, signal_bus):
75         Table.__init__(self, vrf_conf.route_dist, core_service, signal_bus)
76         self._vrf_conf = vrf_conf
77         self._import_maps = []
78         self.init_import_maps(vrf_conf.import_maps)
79
80     def init_import_maps(self, import_maps):
81         LOG.debug(
82             "Initializing import maps (%s) for %r", import_maps, self
83         )
84         del self._import_maps[:]
85         importmap_manager = self._core_service.importmap_manager
86         for name in import_maps:
87             import_map = importmap_manager.get_import_map_by_name(name)
88             if import_map is None:
89                 raise KeyError('No import map with name %s' % name)
90             self._import_maps.append(import_map)
91
92     @property
93     def import_rts(self):
94         return self._vrf_conf.import_rts
95
96     @property
97     def vrf_conf(self):
98         return self._vrf_conf
99
100     def _table_key(self, nlri):
101         """Return a key that will uniquely identify this NLRI inside
102         this table.
103         """
104         # Note: We use `prefix` representation of the NLRI, because
105         # BGP route can be identified without the route distinguisher
106         # value in the VRF space.
107         return nlri.prefix
108
109     def _create_dest(self, nlri):
110         return self.VRF_DEST_CLASS(self, nlri)
111
112     def append_import_map(self, import_map):
113         self._import_maps.append(import_map)
114
115     def remove_import_map(self, import_map):
116         self._import_maps.remove(import_map)
117
118     def get_stats_summary_dict(self):
119         """Returns count of local and remote paths."""
120
121         remote_route_count = 0
122         local_route_count = 0
123         for dest in self.values():
124             for path in dest.known_path_list:
125                 if (hasattr(path.source, 'version_num') or
126                         path.source == VPN_TABLE):
127                     remote_route_count += 1
128                 else:
129                     local_route_count += 1
130         return {RESOURCE_ID: self._vrf_conf.id,
131                 RESOURCE_NAME: self._vrf_conf.name,
132                 REMOTE_ROUTES: remote_route_count,
133                 LOCAL_ROUTES: local_route_count}
134
135     def import_vpn_paths_from_table(self, vpn_table, import_rts=None):
136         for vpn_dest in vpn_table.values():
137             vpn_path = vpn_dest.best_path
138             if not vpn_path:
139                 continue
140
141             if import_rts is None:
142                 import_rts = set(self.import_rts)
143             else:
144                 import_rts = set(import_rts)
145
146             path_rts = vpn_path.get_rts()
147             if import_rts.intersection(path_rts):
148                 # TODO(PH): When (re-)implementing extranet, check what should
149                 # be the label reported back to NC for local paths coming from
150                 # other VRFs.
151                 self.import_vpn_path(vpn_path)
152
153     def import_vpn_path(self, vpn_path):
154         """Imports `vpnv(4|6)_path` into `vrf(4|6)_table` or `evpn_path`
155         into vrfevpn_table`.
156
157         :Parameters:
158             - `vpn_path`: (Path) VPN path that will be cloned and imported
159             into VRF.
160         Note: Does not do any checking if this import is valid.
161         """
162         assert vpn_path.route_family == self.VPN_ROUTE_FAMILY
163         # If source of given vpnv4 path is NC we import it to given VRF
164         # table because of extranet setting. Hence we identify source of
165         # EXTRANET prefixes as VRF_TABLE, else VPN_TABLE.
166         source = vpn_path.source
167         if not source:
168             source = VRF_TABLE
169
170         if self.VPN_ROUTE_FAMILY == RF_L2_EVPN:
171             # Because NLRI class is the same if the route family is EVPN,
172             # we re-use the NLRI instance.
173             vrf_nlri = vpn_path.nlri
174         elif self.ROUTE_FAMILY.safi in [IP_FLOWSPEC, VPN_FLOWSPEC]:
175             vrf_nlri = self.NLRI_CLASS(rules=vpn_path.nlri.rules)
176         else:  # self.VPN_ROUTE_FAMILY in [RF_IPv4_VPN, RF_IPv6_VPN]
177             # Copy NLRI instance
178             ip, masklen = vpn_path.nlri.prefix.split('/')
179             vrf_nlri = self.NLRI_CLASS(length=int(masklen), addr=ip)
180
181         vrf_path = self.VRF_PATH_CLASS(
182             puid=self.VRF_PATH_CLASS.create_puid(
183                 vpn_path.nlri.route_dist,
184                 vpn_path.nlri.prefix),
185             source=source,
186             nlri=vrf_nlri,
187             src_ver_num=vpn_path.source_version_num,
188             pattrs=vpn_path.pathattr_map,
189             nexthop=vpn_path.nexthop,
190             is_withdraw=vpn_path.is_withdraw,
191             label_list=getattr(vpn_path.nlri, 'label_list', None),
192         )
193         if self._is_vrf_path_already_in_table(vrf_path):
194             return None
195
196         if self._is_vrf_path_filtered_out_by_import_maps(vrf_path):
197             return None
198         else:
199             vrf_dest = self.insert(vrf_path)
200             self._signal_bus.dest_changed(vrf_dest)
201
202     def _is_vrf_path_filtered_out_by_import_maps(self, vrf_path):
203         for import_map in self._import_maps:
204             if import_map.match(vrf_path):
205                 return True
206
207         return False
208
209     def _is_vrf_path_already_in_table(self, vrf_path):
210         dest = self._get_dest(vrf_path.nlri)
211         if dest is None:
212             return False
213         return vrf_path in dest.known_path_list
214
215     def apply_import_maps(self):
216         changed_dests = []
217         for dest in self.values():
218             assert isinstance(dest, VrfDest)
219             for import_map in self._import_maps:
220                 for path in dest.known_path_list:
221                     if import_map.match(path):
222                         dest.withdraw_path(path)
223                         changed_dests.append(dest)
224         return changed_dests
225
226     def insert_vrf_path(self, nlri, next_hop=None,
227                         gen_lbl=False, is_withdraw=False, **kwargs):
228         assert nlri
229         pattrs = None
230         label_list = []
231         vrf_conf = self.vrf_conf
232         if not is_withdraw:
233             table_manager = self._core_service.table_manager
234             if gen_lbl and next_hop:
235                 # Label per next_hop demands we use a different label
236                 # per next_hop. Here connected interfaces are advertised per
237                 # VRF.
238                 label_key = (vrf_conf.route_dist, next_hop)
239                 nh_label = table_manager.get_nexthop_label(label_key)
240                 if not nh_label:
241                     nh_label = table_manager.get_next_vpnv4_label()
242                     table_manager.set_nexthop_label(label_key, nh_label)
243                 label_list.append(nh_label)
244
245             elif gen_lbl:
246                 # If we do not have next_hop, get a new label.
247                 label_list.append(table_manager.get_next_vpnv4_label())
248
249             # Set MPLS labels with the generated labels
250             if gen_lbl and isinstance(nlri, EvpnMacIPAdvertisementNLRI):
251                 nlri.mpls_labels = label_list[:2]
252             elif gen_lbl and isinstance(nlri, EvpnIpPrefixNLRI):
253                 nlri.mpls_label = label_list[0]
254
255             # Create a dictionary for path-attrs.
256             pattrs = OrderedDict()
257
258             # MpReachNlri and/or MpUnReachNlri attribute info. is contained
259             # in the path. Hence we do not add these attributes here.
260             from ryu.services.protocols.bgp.core import EXPECTED_ORIGIN
261
262             pattrs[BGP_ATTR_TYPE_ORIGIN] = BGPPathAttributeOrigin(
263                 EXPECTED_ORIGIN)
264             pattrs[BGP_ATTR_TYPE_AS_PATH] = BGPPathAttributeAsPath([])
265             communities = []
266
267             # Set ES-Import Route Target
268             if isinstance(nlri, EvpnEthernetSegmentNLRI):
269                 subtype = 2
270                 es_import = nlri.esi.mac_addr
271                 communities.append(BGPEvpnEsImportRTExtendedCommunity(
272                                    subtype=subtype,
273                                    es_import=es_import))
274
275             for rt in vrf_conf.export_rts:
276                 communities.append(create_rt_extended_community(rt, 2))
277             for soo in vrf_conf.soo_list:
278                 communities.append(create_rt_extended_community(soo, 3))
279
280             # Set Tunnel Encapsulation Attribute
281             tunnel_type = kwargs.get('tunnel_type', None)
282             if tunnel_type:
283                 communities.append(
284                     BGPEncapsulationExtendedCommunity.from_str(tunnel_type))
285
286             # Set ESI Label Extended Community
287             redundancy_mode = kwargs.get('redundancy_mode', None)
288             if redundancy_mode is not None:
289                 subtype = 1
290                 flags = 0
291
292                 from ryu.services.protocols.bgp.api.prefix import (
293                     REDUNDANCY_MODE_SINGLE_ACTIVE)
294                 if redundancy_mode == REDUNDANCY_MODE_SINGLE_ACTIVE:
295                     flags |= BGPEvpnEsiLabelExtendedCommunity.SINGLE_ACTIVE_BIT
296
297                 vni = kwargs.get('vni', None)
298                 if vni is not None:
299                     communities.append(BGPEvpnEsiLabelExtendedCommunity(
300                         subtype=subtype,
301                         flags=flags,
302                         vni=vni))
303                 else:
304                     communities.append(BGPEvpnEsiLabelExtendedCommunity(
305                                        subtype=subtype,
306                                        flags=flags,
307                                        mpls_label=label_list[0]))
308
309             pattrs[BGP_ATTR_TYPE_EXTENDED_COMMUNITIES] = \
310                 BGPPathAttributeExtendedCommunities(communities=communities)
311             if vrf_conf.multi_exit_disc:
312                 pattrs[BGP_ATTR_TYPE_MULTI_EXIT_DISC] = \
313                     BGPPathAttributeMultiExitDisc(vrf_conf.multi_exit_disc)
314
315             # Set PMSI Tunnel Attribute
316             pmsi_tunnel_type = kwargs.get('pmsi_tunnel_type', None)
317             if pmsi_tunnel_type is not None:
318                 from ryu.services.protocols.bgp.api.prefix import (
319                     PMSI_TYPE_INGRESS_REP)
320                 if pmsi_tunnel_type == PMSI_TYPE_INGRESS_REP:
321                     tunnel_id = PmsiTunnelIdIngressReplication(
322                         tunnel_endpoint_ip=self._core_service.router_id)
323                 else:  # pmsi_tunnel_type == PMSI_TYPE_NO_TUNNEL_INFO
324                     tunnel_id = None
325                 pattrs[BGP_ATTR_TYEP_PMSI_TUNNEL_ATTRIBUTE] = \
326                     BGPPathAttributePmsiTunnel(pmsi_flags=0,
327                                                tunnel_type=pmsi_tunnel_type,
328                                                tunnel_id=tunnel_id,
329                                                vni=kwargs.get('vni', None))
330
331         puid = self.VRF_PATH_CLASS.create_puid(
332             vrf_conf.route_dist, nlri.prefix)
333
334         path = self.VRF_PATH_CLASS(
335             puid, None, nlri, 0, pattrs=pattrs,
336             nexthop=next_hop, label_list=label_list,
337             is_withdraw=is_withdraw
338         )
339
340         # Insert the path into VRF table, get affected destination so that we
341         # can process it further.
342         eff_dest = self.insert(path)
343         # Enqueue the eff_dest for further processing.
344         self._signal_bus.dest_changed(eff_dest)
345         return label_list
346
347     def clean_uninteresting_paths(self, interested_rts=None):
348         if interested_rts is None:
349             interested_rts = set(self.vrf_conf.import_rts)
350         return super(VrfTable, self).clean_uninteresting_paths(interested_rts)
351
352
353 @six.add_metaclass(abc.ABCMeta)
354 class VrfDest(Destination):
355     """Base class for VRF destination."""
356
357     def __init__(self, table, nlri):
358         super(VrfDest, self).__init__(table, nlri)
359         self._route_dist = self._table.vrf_conf.route_dist
360
361     @property
362     def nlri_str(self):
363         # Returns `prefix` without the route distinguisher value, because
364         # a destination in VRF space can be identified without the route
365         # distinguisher.
366         return self._nlri.prefix
367
368     def _best_path_lost(self):
369         # Have to send update messages for withdraw of best-path to Network
370         # controller or Global table.
371         old_best_path = self._best_path
372         self._best_path = None
373
374         if old_best_path is None:
375             return
376
377         if old_best_path.source is not None:
378             # Send update-withdraw msg. to Sink. Create withdraw path
379             # out of old best path and queue it into flexinet sinks.
380             old_best_path = old_best_path.clone(for_withdrawal=True)
381             self._core_service.update_flexinet_peers(old_best_path,
382                                                      self._route_dist)
383         else:
384             # Create withdraw-path out of old best path.
385             gpath = old_best_path.clone_to_vpn(self._route_dist,
386                                                for_withdrawal=True)
387             # Insert withdraw into global table and enqueue the destination
388             # for further processing.
389             tm = self._core_service.table_manager
390             tm.learn_path(gpath)
391
392     def _new_best_path(self, best_path):
393         LOG.debug('New best path selected for destination %s', self)
394
395         old_best_path = self._best_path
396         assert (best_path != old_best_path)
397         self._best_path = best_path
398         # Distribute new best-path to flexinet-peers.
399         if best_path.source is not None:
400             # Since route-refresh just causes the version number to
401             # go up and this changes best-path, we check if new-
402             # best-path is really different than old-best-path that
403             # warrants sending update to flexinet peers.
404
405             def really_diff():
406                 old_labels = old_best_path.label_list
407                 new_labels = best_path.label_list
408                 return old_best_path.nexthop != best_path.nexthop \
409                     or set(old_labels) != set(new_labels)
410
411             if not old_best_path or (old_best_path and really_diff()):
412                 # Create OutgoingRoute and queue it into NC sink.
413                 self._core_service.update_flexinet_peers(
414                     best_path, self._route_dist
415                 )
416         else:
417             # If NC is source, we create new path and insert into global
418             # table.
419             gpath = best_path.clone_to_vpn(self._route_dist)
420             tm = self._core_service.table_manager
421             tm.learn_path(gpath)
422             LOG.debug('VRF table %s has new best path: %s',
423                       self._route_dist, self.best_path)
424
425     def _remove_withdrawals(self):
426         """Removes withdrawn paths.
427
428         Note:
429         We may have disproportionate number of withdraws compared to know paths
430         since not all paths get installed into the table due to bgp policy and
431         we can receive withdraws for such paths and withdrawals may not be
432         stopped by the same policies.
433         """
434
435         LOG.debug('Removing %s withdrawals', len(self._withdraw_list))
436
437         # If we have not withdrawals, we have nothing to do.
438         if not self._withdraw_list:
439             return
440
441         # If we have some withdrawals and no know-paths, it means it is safe to
442         # delete these withdraws.
443         if not self._known_path_list:
444             LOG.debug('Found %s withdrawals for path(s) that did not get'
445                       ' installed.', len(self._withdraw_list))
446             del (self._withdraw_list[:])
447             return
448
449         # If we have some known paths and some withdrawals, we find matches and
450         # delete them first.
451         matches = []
452         w_matches = []
453         # Match all withdrawals from destination paths.
454         for withdraw in self._withdraw_list:
455             match = None
456             for path in self._known_path_list:
457                 # We have a match if the source are same.
458                 if path.puid == withdraw.puid:
459                     match = path
460                     matches.append(path)
461                     w_matches.append(withdraw)
462                     # One withdraw can remove only one path.
463                     break
464                 # We do no have any match for this withdraw.
465             if not match:
466                 LOG.debug('No matching path for withdraw found, may be path '
467                           'was not installed into table: %s',
468                           withdraw)
469             # If we have partial match.
470         if len(matches) != len(self._withdraw_list):
471             LOG.debug('Did not find match for some withdrawals. Number of '
472                       'matches(%s), number of withdrawals (%s)',
473                       len(matches), len(self._withdraw_list))
474
475         # Clear matching paths and withdrawals.
476         for match in matches:
477             self._known_path_list.remove(match)
478         for w_match in w_matches:
479             self._withdraw_list.remove(w_match)
480
481     def _remove_old_paths(self):
482         """Identifies which of known paths are old and removes them.
483
484         Known paths will no longer have paths whose new version is present in
485         new paths.
486         """
487         new_paths = self._new_path_list
488         known_paths = self._known_path_list
489         for new_path in new_paths:
490             old_paths = []
491             for path in known_paths:
492                 # Here we just check if source is same and not check if path
493                 # version num. as new_paths are implicit withdrawal of old
494                 # paths and when doing RouteRefresh (not EnhancedRouteRefresh)
495                 # we get same paths again.
496                 if new_path.puid == path.puid:
497                     old_paths.append(path)
498                     break
499
500             for old_path in old_paths:
501                 known_paths.remove(old_path)
502                 LOG.debug('Implicit withdrawal of old path, since we have'
503                           ' learned new path from same source: %s', old_path)
504
505     def _validate_path(self, path):
506         if not path or not hasattr(path, 'label_list'):
507             raise ValueError('Invalid value of path. Expected type '
508                              'with attribute label_list got %s' % path)
509
510
511 @six.add_metaclass(abc.ABCMeta)
512 class VrfPath(Path):
513     """Represents a way of reaching an IP destination with a VPN.
514     """
515     __slots__ = ('_label_list', '_puid')
516
517     ROUTE_FAMILY = None
518     VPN_PATH_CLASS = None
519     VPN_NLRI_CLASS = None
520
521     def __init__(self, puid, source, nlri, src_ver_num,
522                  pattrs=None, nexthop=None,
523                  is_withdraw=False, label_list=None):
524         """Initializes a Vrf path.
525
526             Parameters:
527                 - `puid`: (str) path ID, identifies VPN path from which this
528                 VRF path was imported.
529                 - `label_list`: (list) List of labels for this path.
530             Note: other parameters are as documented in super class.
531         """
532         if self.ROUTE_FAMILY.safi in [IP_FLOWSPEC, VPN_FLOWSPEC]:
533             nexthop = '0.0.0.0'
534
535         Path.__init__(self, source, nlri, src_ver_num, pattrs, nexthop,
536                       is_withdraw)
537         if label_list is None:
538             label_list = []
539         self._label_list = label_list
540         self._puid = puid
541
542     @property
543     def puid(self):
544         return self._puid
545
546     @property
547     def origin_rd(self):
548         tokens = self.puid.split(':')
549         return tokens[0] + ':' + tokens[1]
550
551     @property
552     def label_list(self):
553         return self._label_list[:]
554
555     @property
556     def nlri_str(self):
557         # Returns `prefix` without the route distinguisher value, because
558         # a destination in VRF space can be identified without the route
559         # distinguisher.
560         return self._nlri.prefix
561
562     @staticmethod
563     def create_puid(route_dist, ip_prefix):
564         assert route_dist and ip_prefix
565         return str(route_dist) + ':' + ip_prefix
566
567     def clone(self, for_withdrawal=False):
568         pathattrs = None
569         if not for_withdrawal:
570             pathattrs = self.pathattr_map
571
572         clone = self.__class__(
573             self.puid,
574             self._source,
575             self.nlri,
576             self.source_version_num,
577             pattrs=pathattrs,
578             nexthop=self.nexthop,
579             is_withdraw=for_withdrawal,
580             label_list=self.label_list
581         )
582         return clone
583
584     def clone_to_vpn(self, route_dist, for_withdrawal=False):
585         if self.ROUTE_FAMILY == RF_L2_EVPN:
586             # Because NLRI class is the same if the route family is EVPN,
587             # we re-use the NLRI instance.
588             vpn_nlri = self._nlri
589
590         elif self.ROUTE_FAMILY.safi in [IP_FLOWSPEC, VPN_FLOWSPEC]:
591             vpn_nlri = self.VPN_NLRI_CLASS(route_dist=route_dist,
592                                            rules=self.nlri.rules)
593
594         else:  # self.ROUTE_FAMILY in [RF_IPv4_UC, RF_IPv6_UC]
595             ip, masklen = self._nlri.prefix.split('/')
596             vpn_nlri = self.VPN_NLRI_CLASS(length=int(masklen),
597                                            addr=ip,
598                                            labels=self.label_list,
599                                            route_dist=route_dist)
600
601         pathattrs = None
602         if not for_withdrawal:
603             pathattrs = self.pathattr_map
604
605         vpnv_path = self.VPN_PATH_CLASS(
606             source=self.source,
607             nlri=vpn_nlri,
608             src_ver_num=self.source_version_num,
609             pattrs=pathattrs,
610             nexthop=self.nexthop,
611             is_withdraw=for_withdrawal)
612
613         return vpnv_path
614
615     def __eq__(self, b_path):
616         if not isinstance(b_path, self.__class__):
617             return False
618         if not self.route_family == b_path.route_family:
619             return False
620         if not self.puid == b_path.puid:
621             return False
622         if not self.label_list == b_path.label_list:
623             return False
624         if not self.nexthop == b_path.nexthop:
625             return False
626         if not self.pathattr_map == b_path.pathattr_map:
627             return False
628
629         return True
630
631
632 class ImportMap(object):
633     def match(self, vrf_path):
634         raise NotImplementedError()
635
636
637 class VrfNlriImportMap(ImportMap):
638     VRF_PATH_CLASS = None
639     NLRI_CLASS = None
640
641     def __init__(self, prefix):
642         assert self.VRF_PATH_CLASS is not None
643         assert self.NLRI_CLASS is not None
644         self._nlri = self.NLRI_CLASS(prefix)
645
646     def match(self, vrf_path):
647         if vrf_path.route_family != self.VRF_PATH_CLASS.ROUTE_FAMILY:
648             LOG.error(
649                 "vrf_paths route_family does not match importmaps"
650                 "route_family. Applied to wrong table?")
651             return False
652
653         return vrf_path.nlri == self._nlri
654
655
656 class VrfRtImportMap(ImportMap):
657     def __init__(self, rt):
658         self._rt = rt
659
660     def match(self, vrf_path):
661         extcomm = vrf_path.pathattr_map.get(BGP_ATTR_TYPE_EXTENDED_COMMUNITIES)
662         return extcomm is not None and self._rt in extcomm.rt_list