Exemple #1
0
    def _worker_cleanup(self, worker):
        '''Cleanup the subscriptions and routes advertised by a worker

        Consider all routes announced by this worker as withdrawn.
        Consider this worker unsubscribed from all of its current
        subscriptions.
        '''
        assert isinstance(worker, worker_m.Worker)
        # synthesize withdraw events for all routes from this worker
        LOG.info("  Preparing to withdraw %d routes that were advertised "
                 "by worker", len(worker._rtm_route_entries))
        for entry in worker._rtm_route_entries:
            LOG.info("  Enqueue event to Withdraw route %s", entry)
            self.enqueue(engine.RouteEvent(engine.RouteEvent.WITHDRAW, entry))

        # remove worker from all of its subscriptions
        for match in worker._rtm_matches:
            wa = self._match_2_workers_entries[match]
            if wa.del_worker(worker):
                self.callback_last_local_subscriber(
                    engine.Subscription(match.afi, match.safi,
                                        match.route_target, worker))
            self._check_match_2_workers_and_entries_cleanup(match)

        worker._rtm_matches.clear()
Exemple #2
0
    def _new_route_event(self, event_type, nlri, rts, source, nh, lp=0,
                         replaced_route_entry=None,
                         afi=exa.AFI(exa.AFI.ipv4),
                         safi=exa.SAFI(exa.SAFI.mpls_vpn),
                         **kwargs):
        attributes = exa.Attributes()
        attributes.add(exa.NextHop(nh))
        attributes.add(exa.LocalPreference(lp))

        if 'rtrecords' in kwargs:
            ecoms = exa.ExtendedCommunities()
            ecoms.communities += kwargs['rtrecords']
            attributes.add(ecoms)

        route_event = engine.RouteEvent(event_type,
                                        engine.RouteEntry(nlri, rts,
                                                          attributes, source),
                                        source)
        route_event.set_replaced_route(replaced_route_entry)

        LOG.info("*** Emitting event to %s: %s",
                 self.event_target_worker, route_event)

        self.event_target_worker._on_event(route_event)

        return route_event
Exemple #3
0
 def rtc_advertisement_for_sub(self, sub):
     if sub.safi in RTC_SAFIS:
         event = engine.RouteEvent(
             engine.RouteEvent.ADVERTISE,
             self._subscription_2_rtc_route_entry(sub), self)
         LOG.debug("Based on subscription => synthesized RTC %s", event)
         self.rtm.enqueue(event)
Exemple #4
0
    def _new_flow_event(self, event_type, nlri, to_rts, attract_rts, source,
                        afi=exa.AFI(exa.AFI.ipv4),
                        safi=exa.SAFI(exa.SAFI.flow_vpn),
                        **kwargs):
        attributes = exa.Attributes()

        ecommunities = exa.ExtendedCommunities()
        ecommunities.communities.append(
            exa.TrafficRedirect(exa.ASN(int(to_rts[0].asn)),
                                int(to_rts[0].number))
        )

        attributes.add(ecommunities)

        flow_event = engine.RouteEvent(event_type,
                                       engine.RouteEntry(nlri, attract_rts,
                                                         attributes, source),
                                       source)

        self.event_target_worker.enqueue(flow_event)

        LOG.info("*** Emitting FlowSpec event to %s: %s",
                 self.event_target_worker, flow_event)

        self._wait()

        return flow_event
 def rtc_withdrawal_for_sub(self, sub):
     if sub.safi in RTC_SAFIS:
         event = engine.RouteEvent(
             engine.RouteEvent.WITHDRAW,
             self._subscription_2_rtc_route_entry(sub),
             self)
         LOG.debug("Based on unsubscription => synthesized withdraw"
                   " for RTC %s", event)
         self.rtm.enqueue(event)
 def synthesize_withdraw_all(self, afi, safi):
     for tracked_entry, routes in list(self.tracked_entry_2_routes.items()):
         self.log.trace("Synthesizing withdraws for all routes of %s with "
                        "AFI(%s)/SAFI(%s)", tracked_entry, afi, safi)
         for route in routes:
             if (route.nlri.afi, route.nlri.safi) == (afi, safi):
                 self._on_event(
                     engine.RouteEvent(engine.RouteEvent.WITHDRAW, route)
                 )
Exemple #7
0
    def _worker_unsubscribes(self, sub):
        assert isinstance(sub.worker, worker_m.Worker)

        worker = sub.worker

        # self._dump_state()

        match = Match(sub.afi, sub.safi, sub.route_target)

        # update worker matches
        try:
            worker._rtm_matches.remove(match)
        except KeyError:
            LOG.warning("worker %s unsubs' from %s but this match was"
                        " not tracked for this worker (should not happen,"
                        " this is a bug)", worker, match)

        # synthesize withdraw events
        wa = self._match_2_workers_entries.get(match)
        if wa:
            for entry in wa.entries:
                intersect = set(matches_for(entry.afi,
                                            entry.safi,
                                            entry.route_targets)
                                ).intersection(worker._rtm_matches)
                if len(intersect) > 0:
                    LOG.debug("Will not synthesize withdraw event for %s, "
                              "because worker subscribed to %s", entry,
                              intersect)
                else:
                    LOG.debug("Found an entry for this match: %s", entry)
                    event = engine.RouteEvent(engine.RouteEvent.WITHDRAW,
                                              entry)
                    (dispatch, reason) = test_should_dispatch(event, worker)
                    if dispatch:
                        LOG.info("Dispatching re-synthesized event for %s",
                                 entry)
                        worker.enqueue(event)
                    else:
                        LOG.info("%s => not dispatching re-synthesized event"
                                 " for %s", reason, entry)

            # update _match_2_workers_entries
            try:
                if wa.del_worker(worker):
                    LOG.debug("see if need to callback on last local worker")
                    self.callback_last_local_subscriber(sub)
                    self._check_match_2_workers_and_entries_cleanup(match)
            except KeyError:
                LOG.warning("worker %s unsubscribed from %s but was not"
                            " subscribed yet", worker, match)
        else:  # wa is None
            LOG.warning("worker %s unsubscribed from %s but we had no such"
                        " subscription yet", worker, match)

        if len(worker._rtm_matches) == 0:
            self._workers.pop(worker.name, None)
Exemple #8
0
    def _revert_event(self, event):
        if event.type == engine.RouteEvent.ADVERTISE:
            type = engine.RouteEvent.WITHDRAW
        else:  # WITHDRAW
            type = engine.RouteEvent.ADVERTISE

        route_event = engine.RouteEvent(type, event.route_entry, event.source)

        self.event_target_worker._on_event(route_event)
Exemple #9
0
    def _revert_event(self, event):
        if event.type == engine.RouteEvent.ADVERTISE:
            type = engine.RouteEvent.WITHDRAW
        else:  # WITHDRAW
            type = engine.RouteEvent.ADVERTISE

        route_event = engine.RouteEvent(type, event.route_entry, event.source)

        self.event_target_worker.enqueue(route_event)

        LOG.info("*** Emitting event to %s: %s",
                 self.event_target_worker, route_event)

        self._wait()
Exemple #10
0
    def _worker_subscribes(self, sub):
        assert isinstance(sub.worker, worker_m.Worker)

        worker = sub.worker

        self._workers[worker.name] = worker

        match = Match(sub.afi, sub.safi, sub.route_target)

        wa = self._match_2_workers_entries[match]

        # update match2worker
        if wa.add_worker(worker):
            self.callback_first_local_subscriber(sub)

        LOG.debug("match2workers: %s", wa.workers)

        # re-synthesize events
        for entry in wa.entries:
            LOG.debug("Found an entry for this match: %s", entry)
            event = engine.RouteEvent(engine.RouteEvent.ADVERTISE, entry)
            (dispatch, reason) = test_should_dispatch(event, worker)

            if dispatch:
                # check if the entry carries a route_target to which the worker
                # was already subscribed
                for rt in entry.route_targets:
                    if Match(entry.afi,
                             entry.safi,
                             rt) in worker._rtm_matches:
                        (dispatch, reason) = (
                            False,
                            "worker already had a subscription for this route")
                        break

            if dispatch:
                LOG.info("Dispatching re-synthesized event for %s", entry)
                worker.enqueue(event)
            else:
                LOG.info("%s => not dispatching re-synthesized event for %s",
                         reason, entry)

        # update worker matches
        worker._rtm_matches.add(match)
Exemple #11
0
    def test_f1_test_empty_rt(self):
        # worker advertises a route with no RT

        w1 = self._new_worker("Worker1", worker.Worker)

        subscribe = engine.Subscription(exa.AFI(exa.AFI.ipv4),
                                        exa.SAFI(exa.SAFI.mpls_vpn), None, w1)
        self.rtm.enqueue(subscribe)

        w2 = self._new_worker("Worker2", worker.Worker)

        route_event = engine.RouteEvent(
            engine.RouteEvent.ADVERTISE,
            engine.RouteEntry(t.NLRI1, None, exa.Attributes()), w2)

        self.rtm.enqueue(route_event)

        self._wait()

        self.assertEqual(
            1, w1.enqueue.call_count,
            "1 route advertised should be synthesized to Worker1")
Exemple #12
0
    def _new_flow_event(self, event_type, nlri, to_rts, attract_rts, source,
                        afi=exa.AFI(exa.AFI.ipv4),
                        safi=exa.SAFI(exa.SAFI.flow_vpn),
                        **kwargs):
        attributes = exa.Attributes()

        ecommunities = exa.ExtendedCommunities()
        ecommunities.communities.append(
            exa.TrafficRedirect(exa.ASN(int(to_rts[0].asn)),
                                int(to_rts[0].number))
        )

        attributes.add(ecommunities)

        flow_event = engine.RouteEvent(event_type,
                                       engine.RouteEntry(nlri, attract_rts,
                                                         attributes, source),
                                       source)

        self.event_target_worker._on_event(flow_event)

        return flow_event
Exemple #13
0
    def _receive_route_event(self, route_event):
        entry = route_event.route_entry

        LOG.debug("Try to find an entry from same worker with same nlri")
        replaced_entry = self._source_nlri_2_entry.get((entry.source,
                                                        entry.nlri))

        LOG.debug("   Result: %s", replaced_entry)

        # replaced_entry should be non-empty for a withdraw
        if (replaced_entry is None and
                route_event.type == engine.RouteEvent.WITHDRAW):
            LOG.warning("WITHDRAW but found no route that we could remove: %s",
                        route_event.route_entry)
            return

        # Propagate events to interested workers...
        if route_event.type == engine.RouteEvent.ADVERTISE:

            if replaced_entry == route_event.route_entry:
                LOG.warning("Ignoring, the route advertized is the same as the"
                            " one previously advertized by the source %s: %s",
                            route_event.source, route_event.route_entry)
                return

            # propagate event to interested worker
            # and include the info on the route are replaced by this
            # route, if any
            route_event.set_replaced_route(replaced_entry)

            workers_already_notified = self._propagate_route_event(route_event)
        else:  # WITHDRAW
            workers_already_notified = None

        # Synthesize and dispatch a withdraw event for the route entry that
        # was withdrawn or replaced, except, in the case of a replaced route,
        # to workers that had the ADVERTISE event
        if replaced_entry is not None:
            LOG.debug("Synthesizing a withdraw event for replaced route %s",
                      replaced_entry)
            removal_event = engine.RouteEvent(engine.RouteEvent.WITHDRAW,
                                              replaced_entry,
                                              route_event.source)

            self._propagate_route_event(removal_event,
                                        workers_already_notified)

            # Update match2entries for the replaced_route
            for match in matches_for(replaced_entry.afi,
                                     replaced_entry.safi,
                                     replaced_entry.route_targets):
                wa = self._match_2_workers_entries.get(match, None)
                if wa is None:
                    LOG.error("Trying to remove a route from a match, but"
                              " match %s not found - not supposed to happen"
                              " (route: %s)", match, replaced_entry)
                else:
                    wa.entries.discard(replaced_entry)
                    self._check_match_2_workers_and_entries_cleanup(match)

            # update the route entries for this source
            replaced_entry.source._rtm_route_entries.discard(replaced_entry)

        if route_event.type == engine.RouteEvent.ADVERTISE:
            # Update match2entries and source2entries for the newly
            # advertized route
            for match in matches_for(entry.afi,
                                     entry.safi,
                                     entry.route_targets):
                self._match_2_workers_entries[match].entries.add(entry)

            entry.source._rtm_route_entries.add(entry)

            # Update _source_nlri_2_entry
            self._source_nlri_2_entry[(entry.source, entry.nlri)] = entry
        else:  # WITHDRAW
            # Update source2entries
            entry.source._rtm_route_entries.discard(entry)

            # Update _source_nlri_2_entry
            try:
                del self._source_nlri_2_entry[(entry.source, entry.nlri)]
            except KeyError:
                LOG.error("BUG: withdraw, but nothing could be removed in "
                          "_source_nlri_2_entry")