예제 #1
0
class Path(object):
    """Represents a way of reaching an IP destination.

    Also contains other meta-data given to us by a specific source (such as a
    peer).
    """
    __metaclass__ = ABCMeta
    __slots__ = ('_source', '_path_attr_map', '_nlri', '_source_version_num',
                 '_exported_from', '_nexthop', 'next_path', 'prev_path',
                 '_is_withdraw', 'med_set_by_target_neighbor')
    ROUTE_FAMILY = RF_IPv4_UC

    def __init__(self, source, nlri, src_ver_num, pattrs=None, nexthop=None,
                 is_withdraw=False, med_set_by_target_neighbor=False):
        """Initializes Ipv4 path.

        If this path is not a withdraw, then path attribute and nexthop both
        should be provided.
        Parameters:
            - `source`: (Peer/str) source of this path.
            - `nlri`: (Vpnv4) Nlri instance for Vpnv4 route family.
            - `src_ver_num`: (int) version number of *source* when this path
            was learned.
            - `pattrs`: (OrderedDict) various path attributes for this path.
            - `nexthop`: (str) nexthop advertised for this path.
            - `is_withdraw`: (bool) True if this represents a withdrawal.
        """
        self.med_set_by_target_neighbor = med_set_by_target_neighbor
        if nlri.ROUTE_FAMILY != self.__class__.ROUTE_FAMILY:
            raise ValueError('NLRI and Path route families do not'
                             ' match (%s, %s).' %
                             (nlri.ROUTE_FAMILY, self.__class__.ROUTE_FAMILY))

        # Currently paths injected directly into VRF has only one source
        # src_peer can be None to denote NC else has to be instance of Peer.
        # Paths can be exported from one VRF and then imported into another
        # VRF, in such cases it source is denoted as string VPN_TABLE.
        if not (source is None or
                hasattr(source, 'version_num') or
                source in (VRF_TABLE, VPN_TABLE)):
            raise ValueError('Invalid or Unsupported source for path: %s' %
                             source)

        # If this path is not a withdraw path, than it should have path-
        # attributes and nexthop.
        if not is_withdraw and not (pattrs and nexthop):
            raise ValueError('Need to provide nexthop and patattrs '
                             'for path that is not a withdraw.')

        # The entity (peer) that gave us this path.
        self._source = source

        # Path attribute of this path.
        if pattrs:
            self._path_attr_map = copy(pattrs)
        else:
            self._path_attr_map = OrderedDict()

        # NLRI that this path represents.
        self._nlri = nlri

        # If given nlri is withdrawn.
        self._is_withdraw = is_withdraw

        # @see Source.version_num
        self._source_version_num = src_ver_num

        self._nexthop = nexthop

        # Automatically generated.
        #
        # self.next_path
        # self.prev_path

        # The Destination from which this path was exported, if any.
        self._exported_from = None

    @property
    def source_version_num(self):
        return self._source_version_num

    @property
    def source(self):
        return self._source

    @property
    def route_family(self):
        return self.__class__.ROUTE_FAMILY

    @property
    def nlri(self):
        return self._nlri

    @property
    def is_withdraw(self):
        return self._is_withdraw

    @property
    def pathattr_map(self):
        return copy(self._path_attr_map)

    @property
    def nexthop(self):
        return self._nexthop

    def get_pattr(self, pattr_type, default=None):
        """Returns path attribute of given type.

        Returns None if we do not attribute of type *pattr_type*.
        """
        return self._path_attr_map.get(pattr_type, default)

    def clone(self, for_withdrawal=False):
        pathattrs = None
        if not for_withdrawal:
            pathattrs = self.pathattr_map
        clone = self.__class__(
            self.source,
            self.nlri,
            self.source_version_num,
            pattrs=pathattrs,
            nexthop=self.nexthop,
            is_withdraw=for_withdrawal
        )
        return clone

    def get_rts(self):
        extcomm_attr = self._path_attr_map.get(
            BGP_ATTR_TYPE_EXTENDED_COMMUNITIES)
        if extcomm_attr is None:
            rts = []
        else:
            rts = extcomm_attr.rt_list
        return rts

    def has_rts_in(self, interested_rts):
        """Returns True if this `Path` has any `ExtCommunity` attribute
        route target common with `interested_rts`.
        """
        assert isinstance(interested_rts, set)
        curr_rts = self.get_rts()
        # Add default RT to path RTs so that we match interest for peers who
        # advertised default RT
        curr_rts.append(RouteTargetMembershipNLRI.DEFAULT_RT)

        return not interested_rts.isdisjoint(curr_rts)

    def __str__(self):
        return (
            'Path(source: %s, nlri: %s, source ver#: %s, '
            'path attrs.: %s, nexthop: %s, is_withdraw: %s)' %
            (
                self._source, self._nlri, self._source_version_num,
                self._path_attr_map, self._nexthop, self._is_withdraw
            )
        )

    def __repr__(self):
        return ('Path(%s, %s, %s, %s, %s, %s)' % (
            self._source, self._nlri, self._source_version_num,
            self._path_attr_map, self._nexthop, self._is_withdraw))