def __init__(self, _config, peerClass=ExaBGPPeerWorker): log.debug("Instantiating Manager") self.config = _config self.peerClass = peerClass # RTC is defaults to being enabled self.config['enable_rtc'] = getBoolean( self.config.get('enable_rtc', True)) self.routeTableManager = RouteTableManager() self.routeTableManager.start() if 'local_address' not in self.config: raise Exception("config needs a local_address") if 'my_as' not in self.config: raise Exception("config needs a my_as") self.config['my_as'] = int(self.config['my_as']) if 'peer_as' in self.config: raise Exception("config must omit peer_as, because only iBGP " "is supported yet") self.config['peer_as'] = self.config['my_as'] self.peers = {} if self.config['peers']: peersAddresses = [ x.strip() for x in self.config['peers'].strip().split(",") ] for peerAddress in peersAddresses: log.debug("Creating a peer worker for %s", peerAddress) peerWorker = self.peerClass(self, None, peerAddress, self.config) self.peers[peerAddress] = peerWorker peerWorker.start() self.trackedSubs = dict() # we need a .name since we'll masquerade as a routeEntry source self.name = "BGPManager"
def __init__(self, _config, peerClass=ExaBGPPeerWorker): log.debug("Instantiating Manager") self.config = _config self.peerClass = peerClass # RTC is defaults to being enabled self.config['enable_rtc'] = getBoolean(self.config.get('enable_rtc', True)) self.routeTableManager = RouteTableManager() self.routeTableManager.start() if 'local_address' not in self.config: raise Exception("config needs a local_address") if 'my_as' not in self.config: raise Exception("config needs a my_as") self.config['my_as'] = int(self.config['my_as']) if 'peer_as' in self.config: raise Exception("config must omit peer_as, because only iBGP " "is supported yet") self.config['peer_as'] = self.config['my_as'] self.peers = {} if self.config['peers']: peersAddresses = [x.strip() for x in self.config['peers'].strip().split(",")] for peerAddress in peersAddresses: log.debug("Creating a peer worker for %s", peerAddress) peerWorker = self.peerClass( self, None, peerAddress, self.config) self.peers[peerAddress] = peerWorker peerWorker.start() self.trackedSubs = dict() # we need a .name since we'll masquerade as a routeEntry source self.name = "BGPManager"
class Manager(LookingGlass): def __init__(self, _config, peerClass=ExaBGPPeerWorker): log.debug("Instantiating Manager") self.config = _config self.peerClass = peerClass # RTC is defaults to being enabled self.config['enable_rtc'] = getBoolean(self.config.get('enable_rtc', True)) self.routeTableManager = RouteTableManager() self.routeTableManager.start() if 'local_address' not in self.config: raise Exception("config needs a local_address") if 'my_as' not in self.config: raise Exception("config needs a my_as") self.config['my_as'] = int(self.config['my_as']) if 'peer_as' in self.config: raise Exception("config must omit peer_as, because only iBGP " "is supported yet") self.config['peer_as'] = self.config['my_as'] self.peers = {} if self.config['peers']: peersAddresses = [x.strip() for x in self.config['peers'].strip().split(",")] for peerAddress in peersAddresses: log.debug("Creating a peer worker for %s", peerAddress) peerWorker = self.peerClass( self, None, peerAddress, self.config) self.peers[peerAddress] = peerWorker peerWorker.start() self.trackedSubs = dict() # we need a .name since we'll masquerade as a routeEntry source self.name = "BGPManager" @logDecorator.log def stop(self): for peer in self.peers.itervalues(): peer.stop() self.routeTableManager.stop() for peer in self.peers.itervalues(): peer.join() self.routeTableManager.join() def _pushEvent(self, routeEvent): log.debug("push event to RouteTableManager") self.routeTableManager.enqueue(routeEvent) def cleanup(self, worker): log.debug("push cleanup event for worker %s to RouteTableManager", worker.name) self.routeTableManager.enqueue(WorkerCleanupEvent(worker)) # TODO(tmmorin): withdraw RTC routes corresponding to worker # subscriptions -- currently ok since VPNInstance._stop() calls # unsubscribe def getLocalAddress(self): try: return self.config['local_address'] except KeyError: log.error("BGPManager config has no localAddress defined") return "0.0.0.0" def routeEventSubUnsub(self, subobj): if isinstance(subobj, Subscription): self._routeEventSubscribe(subobj) elif isinstance(subobj, Unsubscription): self._routeEventUnsubscribe(subobj) else: assert(False) @logDecorator.log def _routeEventSubscribe(self, subscription): self.routeTableManager.enqueue(subscription) # synthesize a RouteEvent for a RouteTarget constraint route if (self.config['enable_rtc'] and not isinstance(subscription.worker, BGPPeerWorker)): firstWorkerForSubscription = self._trackedSubscriptionsAddWorker( subscription) # FIXME: not excellent to hardcode this here if ((subscription.safi in (SAFI.mpls_vpn, SAFI.evpn)) and firstWorkerForSubscription): routeEvent = RouteEvent( RouteEvent.ADVERTISE, self._subscription2RTCRouteEntry(subscription), self) log.debug( "Based on subscription => synthesized RTC %s", routeEvent) self.routeTableManager.enqueue(routeEvent) else: log.debug("No need to synthesize an RTC route " "(firstWorkerForSubscription:%s) ", firstWorkerForSubscription) @logDecorator.log def _routeEventUnsubscribe(self, unsubscription): self.routeTableManager.enqueue(unsubscription) if (self.config['enable_rtc'] and not isinstance(unsubscription.worker, BGPPeerWorker)): wasLastWorkerForSubscription = \ self._trackedSubscriptionsRemoveWorker(unsubscription) # FIXME: not excellent to hardcode this here if ((unsubscription.safi in (SAFI.mpls_vpn, SAFI.evpn)) and wasLastWorkerForSubscription): # synthesize a withdraw RouteEvent for a RouteTarget constraint # route routeEvent = RouteEvent( RouteEvent.WITHDRAW, self._subscription2RTCRouteEntry(unsubscription), self) log.debug("Based on unsubscription => synthesized withdraw" " for RTC %s", routeEvent) self.routeTableManager.enqueue(routeEvent) else: log.debug("No need to synthesize an RTC route " "(wasLastWorkerForSubscription:%s) ", wasLastWorkerForSubscription) # FIXME: this can be subject to races def _trackedSubscriptionsAddWorker(self, subs): '''returns 1 if this is the first worker subscribed''' result = 0 if (subs.afi, subs.safi, subs.routeTarget) not in self.trackedSubs: self.trackedSubs[(subs.afi, subs.safi, subs.routeTarget)] = set() result = 1 self.trackedSubs[ (subs.afi, subs.safi, subs.routeTarget)].add(subs.worker) return result # FIXME: this can be subject to races def _trackedSubscriptionsRemoveWorker(self, subs): '''returns 1 if this was the last worker subscribed''' self.trackedSubs[(subs.afi, subs.safi, subs.routeTarget) ].remove(subs.worker) if len(self.trackedSubs[(subs.afi, subs.safi, subs.routeTarget)]) == 0: del self.trackedSubs[(subs.afi, subs.safi, subs.routeTarget)] return 1 else: return 0 def _subscription2RTCRouteEntry(self, subscription): route = Route(RouteTargetConstraint(AFI(AFI.ipv4), SAFI( SAFI.rtc), self.config['my_as'], subscription.routeTarget)) nh = Inet( 1, socket.inet_pton(socket.AF_INET, self.config['local_address'])) route.attributes.add(NextHop(nh)) routeEntry = RouteEntry(AFI(AFI.ipv4), SAFI( SAFI.rtc), [], route.nlri, route.attributes, self) return routeEntry # Looking Glass Functions ################### def getLGMap(self): return {"peers": (LGMap.COLLECTION, (self.getLGPeerList, self.getLGPeerPathItem)), "routes": (LGMap.FORWARD, self.routeTableManager), "workers": (LGMap.FORWARD, self.routeTableManager), } def getEstablishedPeersCount(self): return reduce(lambda count, peer: count + (isinstance(peer, BGPPeerWorker) and peer.isEstablished()), self.peers.itervalues(), 0) def getLGPeerList(self): return [{"id": peer.peerAddress, "state": peer.fsm.state} for peer in self.peers.itervalues()] def getLGPeerPathItem(self, pathItem): return self.peers[pathItem]
def setUp(self): super(TestRouteTableManager, self).setUp() self.routeTableManager = RouteTableManager() self.routeTableManager.start() self.setEventTargetWorker(self.routeTableManager)
class TestRouteTableManager(TestCase, BaseTestBagPipeBGP): def setUp(self): super(TestRouteTableManager, self).setUp() self.routeTableManager = RouteTableManager() self.routeTableManager.start() self.setEventTargetWorker(self.routeTableManager) def tearDown(self): super(TestRouteTableManager, self).tearDown() self.routeTableManager.stop() self.routeTableManager.join() def _newworker(self, workerName, workerType): worker = mock.Mock(spec=workerType) worker.name = workerName worker.enqueue = mock.Mock() return worker def _workerSubscriptions(self, worker, rts, afi=AFI(AFI.ipv4), safi=SAFI(SAFI.mpls_vpn)): for rt in rts: subscribe = Subscription(afi, safi, rt, worker) self.routeTableManager.enqueue(subscribe) def _workerUnsubscriptions(self, worker, rts, afi=AFI(AFI.ipv4), safi=SAFI(SAFI.mpls_vpn)): for rt in rts: unsubscribe = Unsubscription(afi, safi, rt, worker) self.routeTableManager.enqueue(unsubscribe) def _checkSubscriptions(self, worker, matches): for match in matches: subs = self.routeTableManager.getWorkerSubscriptions(worker) self.assertIn(match, subs, "Subscription not found") def _checkUnsubscriptions(self, worker, matches): for match in matches: subs = self.routeTableManager.getWorkerSubscriptions(worker) self.assertNotIn(match, subs, "Subscription not found") def _checkCalls(self, worker, entry): self.assertTrue( entry in self.routeTableManager.getWorkerRouteEntries(worker), "Route entry not found") def _checkNoRouteEntry(self, worker, entry): self.assertTrue( entry not in self.routeTableManager.getWorkerRouteEntries(worker), "Route entry found") def _checkEventsCalls(self, events, advertisedRoutes, withdrawnNLRIs): ''' checks that each advertise event in 'events' is in advertisedRoutes, that each withdraw event in 'events' is in withdrawnNLRIs and that all events in withdrawnNLRIs and advertisedRoutes are in 'events' ''' for (callArgs, _) in events: if (callArgs[0].type == RouteEvent.ADVERTISE): self.assertIn(callArgs[0].routeEntry, advertisedRoutes, "Bad advertised route") advertisedRoutes.remove(callArgs[0].routeEntry) else: # WITHDRAW self.assertIn(callArgs[0].routeEntry.nlri, withdrawnNLRIs, "Bad withdrawn route") withdrawnNLRIs.remove(callArgs[0].routeEntry.nlri) self.assertEqual(0, len(advertisedRoutes), "some routes not advert'd") self.assertEqual(0, len(withdrawnNLRIs), "some routes not withdrawn") def testA1_SubscriptionsWithNoRouteTosynthesize(self): # Worker1 subscribes to RT1 and RT2 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1, RT2]) # Waiting for RouteTableManager thread finishes to process the # subscriptions self._wait() # check subscriptions self._checkSubscriptions(worker1, [MATCH1, MATCH2]) def testA2_SubscriptionsWithRouteTosynthesize(self): # BGPPeerWorker1 advertises a route for RT1 and RT2 bgpPeerWorker1 = self._newworker("BGPWorker1", BGPPeerWorker) evt1 = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], bgpPeerWorker1, NH1) # BGPPeerWorker1 advertises an other route for RT2 evt2 = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI2, [RT2], bgpPeerWorker1, NH1) # BGPPeerWorker1 subscribes to RT1 self._workerSubscriptions(bgpPeerWorker1, [RT1]) # Worker1 subscribes to RT1 and RT2 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1, RT2]) # Worker2 subscribes to RT1 worker2 = self._newworker("Worker-2", Worker) self._workerSubscriptions(worker2, [RT1]) # Worker3 subscribes to RT3 worker3 = self._newworker("Worker-3", Worker) self._workerSubscriptions(worker3, [RT3]) # BGPPeerWorker2 subscribes to RT1 bgpPeerWorker2 = self._newworker("BGPWorker2", BGPPeerWorker) self._workerSubscriptions(bgpPeerWorker2, [RT1]) # Waiting for RouteTableManager thread finishes to process the # subscription self._wait() # check route entry synthesized self.assertEqual(0, bgpPeerWorker1.enqueue.call_count, "Route should not be synthesized to its source") self.assertEqual(0, worker3.enqueue.call_count, "no route should be synthesized to Worker3") self.assertEqual( 0, bgpPeerWorker2.enqueue.call_count, "Route should not be synthesized between BGP workers") self.assertEqual( 2, worker1.enqueue.call_count, "2 advertise events should be synthesized to Worker1") self._checkEventsCalls(worker1.enqueue.call_args_list, [evt1.routeEntry, evt2.routeEntry], []) self.assertEqual(1, worker2.enqueue.call_count, "1 advertise event should be synthesized to Worker2") self._checkEventsCalls(worker2.enqueue.call_args_list, [evt1.routeEntry], []) def testA3_ReSubscription(self): # BGPPeerWorker1 advertises a route for RT1 and RT2 bgpPeerWorker1 = self._newworker("BGPWorker1", BGPPeerWorker) routeEvent = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], bgpPeerWorker1, NH1) # Worker1 subscribes to RT1 and RT2 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1, RT2]) # Worker1 subscribes again to RT1 self._workerSubscriptions(worker1, [RT1]) # Worker2 subscribes to RT1 worker2 = self._newworker("Worker-2", Worker) self._workerSubscriptions(worker2, [RT1]) # Worker1 subscribes again to RT2 self._workerSubscriptions(worker2, [RT2]) # Waiting for RouteTableManager thread finishes to process the # subscription self._wait() # check route entry synthesized self.assertEqual( 1, worker1.enqueue.call_count, "1 route advertised should be synthesized to Worker1") self._checkEventsCalls(worker1.enqueue.call_args_list, [routeEvent.routeEntry], []) self.assertEqual( 1, worker2.enqueue.call_count, "1 route advertised should be synthesized to Worker2") self._checkEventsCalls(worker2.enqueue.call_args_list, [routeEvent.routeEntry], []) def testB1_UnsubscriptionWithNoRouteTosynthesize(self): # Worker1 subscribes to RT1 and RT2 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1, RT2]) # BGPPeerWorker1 subscribes to RT1 and RT2 bgpPeerWorker1 = self._newworker("BGPWorker1", BGPPeerWorker) self._workerSubscriptions(bgpPeerWorker1, [RT1, RT2]) # Worker1 unsubscribes to RT1 self._workerUnsubscriptions(worker1, [RT1]) # BGPPeerWorker1 unsubscribes to RT1 and RT2 self._workerUnsubscriptions(bgpPeerWorker1, [RT1, RT2]) # Waiting for RouteTableManager thread finishes to process the # subscription self._wait() # check subscription/unsubscriptions self._checkUnsubscriptions(worker1, [MATCH1]) self._checkSubscriptions(worker1, [MATCH2]) self._checkUnsubscriptions(bgpPeerWorker1, [MATCH1, MATCH2]) def testB2_UnsubscriptionWithRouteTosynthesize(self): # BGPPeerWorker1 advertises a route for RT1 bgpPeerWorker1 = self._newworker("BGPWorker1", BGPPeerWorker) evt1 = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], bgpPeerWorker1, NH1) # BGPPeerWorker1 advertises an other route for RT2 evt2 = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI2, [RT2], bgpPeerWorker1, NH1) # BGPPeerWorker1 subscribes to RT1 self._workerSubscriptions(bgpPeerWorker1, [RT1]) # Worker1 subscribes to RT1 and RT2 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1, RT2]) # Worker2 subscribes to RT2 worker2 = self._newworker("Worker-2", Worker) self._workerSubscriptions(worker2, [RT2]) # Worker3 subscribes to RT3 worker3 = self._newworker("Worker-3", Worker) self._workerSubscriptions(worker3, [RT3]) # BGPPeerWorker2 subscribes to RT1 bgpPeerWorker2 = self._newworker("BGPWorker2", BGPPeerWorker) self._workerSubscriptions(bgpPeerWorker2, [RT1]) # Workers and BGPPeerWorker unsubscriptions self._workerUnsubscriptions(bgpPeerWorker1, [RT1]) self._workerUnsubscriptions(worker1, [RT1]) self._workerUnsubscriptions(worker2, [RT2]) self._workerUnsubscriptions(worker3, [RT3]) self._workerUnsubscriptions(bgpPeerWorker2, [RT1]) # Waiting for RouteTableManager thread finishes to process the # subscription self._wait() # check route entry synthesized self.assertEqual(0, bgpPeerWorker1.enqueue.call_count, "Route should not be synthesized to its source") self.assertEqual(0, worker3.enqueue.call_count, "no route should be synthesized to Worker3") self.assertEqual( 0, bgpPeerWorker2.enqueue.call_count, "Route should not be synthesized between " "BGPPeerWorkers") self.assertEqual(2, worker1.enqueue.call_count, "2 advertise event should be synthesized to Worker1") self._checkEventsCalls(worker1.enqueue.call_args_list, [evt1.routeEntry, evt2.routeEntry], []) self.assertEqual( 4, worker2.enqueue.call_count, "4 events should be synthesized to Worker2: " "2 advertise and 2 withdraw") self._checkEventsCalls(worker2.enqueue.call_args_list, [evt1.routeEntry, evt2.routeEntry], [evt1.routeEntry.nlri, evt2.routeEntry.nlri]) def testB3_UnsubscriptionNotRegistered(self): # Worker1 subscribes to RT1 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1]) # Worker1 unsubscribes to RT2 self._workerUnsubscriptions(worker1, [RT2]) # BGPPeerWorker1 unsubscribes to RT1 bgpPeerWorker1 = self._newworker("BGPWorker1", BGPPeerWorker) self._workerUnsubscriptions(bgpPeerWorker1, [RT1, RT2]) # Waiting for RouteTableManager thread finishes to process the # subscription self._wait() # check subscription/unsubscriptions self._checkSubscriptions(worker1, [MATCH1]) self._checkUnsubscriptions(bgpPeerWorker1, [MATCH1, MATCH2]) def testC1_routeAdvertiseByWorkerWithoutPropagation(self): # Worker1 advertises a route for RT1 and RT2 worker1 = self._newworker("Worker-1", Worker) routeEvent = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], worker1, NH1) # check route entry has been inserted self._checkCalls(worker1, routeEvent.routeEntry) def testC2_routeWithdrawByWorkerWithoutPropagation(self): # Worker1 advertises then withdraws a route worker1 = self._newworker("Worker-1", Worker) self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], worker1, NH1) routeEvent = self._newRouteEvent(RouteEvent.WITHDRAW, NLRI1, [RT1], worker1, NH1) # check route entry has been removed self._checkNoRouteEntry(worker1, routeEvent.routeEntry) def testC3_routeAdvertiseByBGPPeerWithPropagation(self): # Worker1 subscribes to RT1 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1]) # Worker2 subscribes to RT2 worker2 = self._newworker("Worker-2", Worker) self._workerSubscriptions(worker2, [RT2]) # BGPPeerWorker1 subscribes to RT1 and RT2 bgpPeerWorker1 = self._newworker("BGPWorker1", BGPPeerWorker) self._workerSubscriptions(bgpPeerWorker1, [RT1, RT2]) # BGPPeerWorker2 subscribes to RT1 and RT2 bgpPeerWorker2 = self._newworker("BGPWorker2", BGPPeerWorker) self._workerSubscriptions(bgpPeerWorker2, [RT1, RT2]) # BGPPeerWorker1 advertises a route for RT1 routeEvent = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1], bgpPeerWorker1, NH1) # check routeEvent propagation self.assertEqual(1, worker1.enqueue.call_count, "1 route should be propagated to Worker1") self._checkEventsCalls(worker1.enqueue.call_args_list, [routeEvent.routeEntry], []) self.assertEqual(0, worker2.enqueue.call_count, "no route should be propagated to Worker2") self.assertEqual(0, bgpPeerWorker1.enqueue.call_count, "Route should not be propagated to its source") self.assertEqual(0, bgpPeerWorker2.enqueue.call_count, "Route should not be propagated between BGP workers") def testC4_routeWithdrawByPeerWorkerWithPropagation(self): # Worker1 subscribes to RT1 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1]) # Worker2 subscribes to RT2 worker2 = self._newworker("Worker-2", Worker) self._workerSubscriptions(worker2, [RT2]) # Worker3 subscribes to RT3 worker3 = self._newworker("Worker-3", Worker) self._workerSubscriptions(worker3, [RT3]) # BGPPeerWorker1 subscribes to RT1 bgpPeerWorker1 = self._newworker("BGPWorker1", BGPPeerWorker) self._workerSubscriptions(bgpPeerWorker1, [RT1]) # BGPPeerWorker2 subscribes to RT2 bgpPeerWorker2 = self._newworker("BGPWorker2", BGPPeerWorker) self._workerSubscriptions(bgpPeerWorker2, [RT2]) # BGPPeerWorker1 advertises a route for RT1 and RT2 routeEventA = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], bgpPeerWorker1, NH1) # BGPPeerWorker1 withdraw previous route (without RT routeEventW = self._newRouteEvent(RouteEvent.WITHDRAW, NLRI1, [], bgpPeerWorker1, NH1) # check routeEvent propagation self.assertEqual(2, worker1.enqueue.call_count, "2 routes should be propagated to Worker1") self._checkEventsCalls(worker1.enqueue.call_args_list, [routeEventA.routeEntry], [routeEventW.routeEntry.nlri]) self.assertEqual(2, worker2.enqueue.call_count, "2 routes should be propagated to Worker2") self._checkEventsCalls(worker2.enqueue.call_args_list, [routeEventA.routeEntry], [routeEventW.routeEntry.nlri]) self.assertEqual(0, worker3.enqueue.call_count, "No route should be propagated to Worker3") self.assertEqual(0, bgpPeerWorker1.enqueue.call_count, "Route should not be propagated to its source") self.assertEqual(0, bgpPeerWorker2.enqueue.call_count, "Route should not be propagated between BGP workers") def testC5_routeUpdateByBGPPeerWithWithdrawPropagation(self): # Worker1 subscribes to RT1 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1]) # Worker2 subscribes to RT2 worker2 = self._newworker("Worker-2", Worker) self._workerSubscriptions(worker2, [RT2]) # Worker3 subscribes to RT3 worker3 = self._newworker("Worker-3", Worker) self._workerSubscriptions(worker3, [RT3]) # BGPPeerWorker1 advertises a route for RT1, RT2 and RT3 bgpPeerWorker1 = self._newworker("BGPWorker1", BGPPeerWorker) evt1 = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2, RT3], bgpPeerWorker1, NH1) # BGPPeerWorker1 advertises the same nlri with attributes NH and RTs # modification evt2 = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], bgpPeerWorker1, NH2) # check route event propagation # TO DO : check routeEvent.replacedRoute self.assertEqual(0, bgpPeerWorker1.enqueue.call_count, "Route should not be propagated to its source") self.assertEqual(2, worker1.enqueue.call_count, "2 routes should be advertised to Worker1") self._checkEventsCalls(worker1.enqueue.call_args_list, [evt1.routeEntry, evt2.routeEntry], []) self.assertEqual(2, worker2.enqueue.call_count, "2 routes should be advertised to Worker2") self._checkEventsCalls(worker2.enqueue.call_args_list, [evt1.routeEntry, evt2.routeEntry], []) self.assertEqual(2, worker3.enqueue.call_count, "2 routes should be advert/withdraw to Worker3") self._checkEventsCalls(worker3.enqueue.call_args_list, [evt1.routeEntry], [evt1.routeEntry.nlri]) def testC6_routeReadvertised(self): # Worker1 subscribes to RT1 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1, RT2, RT3]) # BGPPeerWorker1 advertises a route for RT1, RT2 and RT3 bgpPeerWorker1 = self._newworker("BGPWorker1", BGPPeerWorker) evt1 = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], bgpPeerWorker1, NH1) # BGPPeerWorker1 advertises the same nlri with same attributes and RTs self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], bgpPeerWorker1, NH1) # check route event propagation self.assertEqual(0, bgpPeerWorker1.enqueue.call_count, "Route should not be propagated to its source") self.assertEqual(1, worker1.enqueue.call_count, "only 1 route should be advertised to Worker1") self._checkEventsCalls(worker1.enqueue.call_args_list, [evt1.routeEntry], []) def testC7_routeWithdrawNotRegistered(self): # Worker1 subscribes to RT1 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1, RT2]) # BGPPeerWorker1 advertises a route for RT1 and RT2 bgpPeerWorker1 = self._newworker("BGPWorker1", BGPPeerWorker) evt1 = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], bgpPeerWorker1, NH1) # BGPPeerWorker1 withdraw a not registered route (without RT self._newRouteEvent(RouteEvent.WITHDRAW, NLRI2, [], bgpPeerWorker1, NH1) # Waiting for RouteTableManager thread finishes to process routeEvent self._wait() # check routeEvent propagation self.assertEqual(1, worker1.enqueue.call_count, "1 route1 should be propagated to Worker1") self._checkEventsCalls(worker1.enqueue.call_args_list, [evt1.routeEntry], []) self.assertEqual(0, bgpPeerWorker1.enqueue.call_count, "Route should not be propagated back to its source") def testD1_WorkerCleanup(self): # Worker1 subscribes to RT1 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1]) # Worker2 subscribes to RT2 worker2 = self._newworker("Worker-2", Worker) self._workerSubscriptions(worker2, [RT2]) # BGPPeerWorker1 subscribes to RT1 and RT2 bgpPeerWorker1 = self._newworker("BGPWorker1", BGPPeerWorker) self._workerSubscriptions(bgpPeerWorker1, [RT1, RT2]) # BGPPeerWorker1 advertises a route for RT1 and RT2 evt1 = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], bgpPeerWorker1, NH1) # BGPPeerWorker1 advertises an other route for RT2 evt2 = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI2, [RT2], bgpPeerWorker1, NH1) # Cleanup Worker1 self.routeTableManager.enqueue(WorkerCleanupEvent(bgpPeerWorker1)) # Waiting for RouteTableManager thread finishes to process the # subscriptions self._wait() # check unsubscriptions self._checkUnsubscriptions(bgpPeerWorker1, [MATCH1, MATCH2]) # Check route synthesize to Worker1 and Worker2 self.assertEqual(2, worker1.enqueue.call_count, "2 routes should be advert/withdraw to Worker1") self._checkEventsCalls(worker1.enqueue.call_args_list, [evt1.routeEntry], [evt1.routeEntry.nlri]) self.assertEqual(4, worker2.enqueue.call_count, "4 routes should be advert/withdraw to Worker2") self._checkEventsCalls(worker2.enqueue.call_args_list, [evt1.routeEntry, evt2.routeEntry], [evt1.routeEntry.nlri, evt2.routeEntry.nlri]) # Check route entries have been removed for BGPPeerWorker1 self._checkNoRouteEntry(bgpPeerWorker1, evt1.routeEntry) self._checkNoRouteEntry(bgpPeerWorker1, evt2.routeEntry) def testE1_DumpState(self): # BGPPeerWorker1 advertises a route for RT1 and RT2 bgpPeerWorker1 = self._newworker("BGPWorker1", BGPPeerWorker) self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], bgpPeerWorker1, NH1) # BGPPeerWorker1 advertises an other route for RT2 self._newRouteEvent(RouteEvent.ADVERTISE, NLRI2, [RT2], bgpPeerWorker1, NH1) # BGPPeerWorker1 subscribes to RT1 self._workerSubscriptions(bgpPeerWorker1, [RT1]) # Worker1 subscribes to RT1 and RT2 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1, RT2]) # Worker2 subscribes to RT1 worker2 = self._newworker("Worker-2", Worker) self._workerSubscriptions(worker2, [RT1]) # Worker3 subscribes to RT3 worker3 = self._newworker("Worker-3", Worker) self._workerSubscriptions(worker3, [RT3]) # Waiting for RouteTableManager thread finishes to process the # subscriptions and route event processing self._wait() self.routeTableManager._dumpState() def testF1_testEmptyRT(self): # worker advertises a route with no RT w1 = self._newworker("Worker1", Worker) subscribe = Subscription(AFI(AFI.ipv4), SAFI(SAFI.mpls_vpn), None, w1) self.routeTableManager.enqueue(subscribe) w2 = self._newworker("Worker2", Worker) routeEvent = RouteEvent( RouteEvent.ADVERTISE, RouteEntry(AFI(AFI.ipv4), SAFI(SAFI.mpls_vpn), None, NLRI1, Attributes(), w2), w2) self.routeTableManager.enqueue(routeEvent) self._wait() self.assertEqual( 1, w1.enqueue.call_count, "1 route advertised should be synthesized to Worker1")
class Manager(LookingGlass): def __init__(self, _config, peerClass=ExaBGPPeerWorker): log.debug("Instantiating Manager") self.config = _config self.peerClass = peerClass # RTC is defaults to being enabled self.config['enable_rtc'] = getBoolean( self.config.get('enable_rtc', True)) self.routeTableManager = RouteTableManager() self.routeTableManager.start() if 'local_address' not in self.config: raise Exception("config needs a local_address") if 'my_as' not in self.config: raise Exception("config needs a my_as") self.config['my_as'] = int(self.config['my_as']) if 'peer_as' in self.config: raise Exception("config must omit peer_as, because only iBGP " "is supported yet") self.config['peer_as'] = self.config['my_as'] self.peers = {} if self.config['peers']: peersAddresses = [ x.strip() for x in self.config['peers'].strip().split(",") ] for peerAddress in peersAddresses: log.debug("Creating a peer worker for %s", peerAddress) peerWorker = self.peerClass(self, None, peerAddress, self.config) self.peers[peerAddress] = peerWorker peerWorker.start() self.trackedSubs = dict() # we need a .name since we'll masquerade as a routeEntry source self.name = "BGPManager" @logDecorator.log def stop(self): for peer in self.peers.itervalues(): peer.stop() self.routeTableManager.stop() for peer in self.peers.itervalues(): peer.join() self.routeTableManager.join() def _pushEvent(self, routeEvent): log.debug("push event to RouteTableManager") self.routeTableManager.enqueue(routeEvent) def cleanup(self, worker): log.debug("push cleanup event for worker %s to RouteTableManager", worker.name) self.routeTableManager.enqueue(WorkerCleanupEvent(worker)) # TODO(tmmorin): withdraw RTC routes corresponding to worker # subscriptions -- currently ok since VPNInstance._stop() calls # unsubscribe def getLocalAddress(self): try: return self.config['local_address'] except KeyError: log.error("BGPManager config has no localAddress defined") return "0.0.0.0" def routeEventSubUnsub(self, subobj): if isinstance(subobj, Subscription): self._routeEventSubscribe(subobj) elif isinstance(subobj, Unsubscription): self._routeEventUnsubscribe(subobj) else: assert (False) @logDecorator.log def _routeEventSubscribe(self, subscription): self.routeTableManager.enqueue(subscription) # synthesize a RouteEvent for a RouteTarget constraint route if (self.config['enable_rtc'] and not isinstance(subscription.worker, BGPPeerWorker)): firstWorkerForSubscription = self._trackedSubscriptionsAddWorker( subscription) # FIXME: not excellent to hardcode this here if ((subscription.safi in (SAFI.mpls_vpn, SAFI.evpn)) and firstWorkerForSubscription): routeEvent = RouteEvent( RouteEvent.ADVERTISE, self._subscription2RTCRouteEntry(subscription), self) log.debug("Based on subscription => synthesized RTC %s", routeEvent) self.routeTableManager.enqueue(routeEvent) else: log.debug( "No need to synthesize an RTC route " "(firstWorkerForSubscription:%s) ", firstWorkerForSubscription) @logDecorator.log def _routeEventUnsubscribe(self, unsubscription): self.routeTableManager.enqueue(unsubscription) if (self.config['enable_rtc'] and not isinstance(unsubscription.worker, BGPPeerWorker)): wasLastWorkerForSubscription = \ self._trackedSubscriptionsRemoveWorker(unsubscription) # FIXME: not excellent to hardcode this here if ((unsubscription.safi in (SAFI.mpls_vpn, SAFI.evpn)) and wasLastWorkerForSubscription): # synthesize a withdraw RouteEvent for a RouteTarget constraint # route routeEvent = RouteEvent( RouteEvent.WITHDRAW, self._subscription2RTCRouteEntry(unsubscription), self) log.debug( "Based on unsubscription => synthesized withdraw" " for RTC %s", routeEvent) self.routeTableManager.enqueue(routeEvent) else: log.debug( "No need to synthesize an RTC route " "(wasLastWorkerForSubscription:%s) ", wasLastWorkerForSubscription) # FIXME: this can be subject to races def _trackedSubscriptionsAddWorker(self, subs): '''returns 1 if this is the first worker subscribed''' result = 0 if (subs.afi, subs.safi, subs.routeTarget) not in self.trackedSubs: self.trackedSubs[(subs.afi, subs.safi, subs.routeTarget)] = set() result = 1 self.trackedSubs[(subs.afi, subs.safi, subs.routeTarget)].add(subs.worker) return result # FIXME: this can be subject to races def _trackedSubscriptionsRemoveWorker(self, subs): '''returns 1 if this was the last worker subscribed''' self.trackedSubs[(subs.afi, subs.safi, subs.routeTarget)].remove(subs.worker) if len(self.trackedSubs[(subs.afi, subs.safi, subs.routeTarget)]) == 0: del self.trackedSubs[(subs.afi, subs.safi, subs.routeTarget)] return 1 else: return 0 def _subscription2RTCRouteEntry(self, subscription): route = Route( RouteTargetConstraint(AFI(AFI.ipv4), SAFI(SAFI.rtc), self.config['my_as'], subscription.routeTarget)) nh = Inet( 1, socket.inet_pton(socket.AF_INET, self.config['local_address'])) route.attributes.add(NextHop(nh)) routeEntry = RouteEntry(AFI(AFI.ipv4), SAFI(SAFI.rtc), [], route.nlri, route.attributes, self) return routeEntry # Looking Glass Functions ################### def getLGMap(self): return { "peers": (LGMap.COLLECTION, (self.getLGPeerList, self.getLGPeerPathItem)), "routes": (LGMap.FORWARD, self.routeTableManager), "workers": (LGMap.FORWARD, self.routeTableManager), } def getEstablishedPeersCount(self): return reduce( lambda count, peer: count + (isinstance(peer, BGPPeerWorker) and peer.isEstablished()), self.peers.itervalues(), 0) def getLGPeerList(self): return [{ "id": peer.peerAddress, "state": peer.fsm.state } for peer in self.peers.itervalues()] def getLGPeerPathItem(self, pathItem): return self.peers[pathItem]
class TestRouteTableManager(TestCase, BaseTestBagPipeBGP): def setUp(self): super(TestRouteTableManager, self).setUp() self.routeTableManager = RouteTableManager() self.routeTableManager.start() self.setEventTargetWorker(self.routeTableManager) def tearDown(self): super(TestRouteTableManager, self).tearDown() self.routeTableManager.stop() self.routeTableManager.join() def _newworker(self, workerName, workerType): worker = mock.Mock(spec=workerType) worker.name = workerName worker.enqueue = mock.Mock() return worker def _workerSubscriptions(self, worker, rts, afi=AFI(AFI.ipv4), safi=SAFI(SAFI.mpls_vpn)): for rt in rts: subscribe = Subscription(afi, safi, rt, worker) self.routeTableManager.enqueue(subscribe) def _workerUnsubscriptions(self, worker, rts, afi=AFI(AFI.ipv4), safi=SAFI(SAFI.mpls_vpn)): for rt in rts: unsubscribe = Unsubscription(afi, safi, rt, worker) self.routeTableManager.enqueue(unsubscribe) def _checkSubscriptions(self, worker, matches): for match in matches: subs = self.routeTableManager.getWorkerSubscriptions(worker) self.assertIn(match, subs, "Subscription not found") def _checkUnsubscriptions(self, worker, matches): for match in matches: subs = self.routeTableManager.getWorkerSubscriptions(worker) self.assertNotIn(match, subs, "Subscription not found") def _checkCalls(self, worker, entry): self.assertTrue( entry in self.routeTableManager.getWorkerRouteEntries(worker), "Route entry not found") def _checkNoRouteEntry(self, worker, entry): self.assertTrue( entry not in self.routeTableManager.getWorkerRouteEntries(worker), "Route entry found") def _checkEventsCalls(self, events, advertisedRoutes, withdrawnNLRIs): ''' checks that each advertise event in 'events' is in advertisedRoutes, that each withdraw event in 'events' is in withdrawnNLRIs and that all events in withdrawnNLRIs and advertisedRoutes are in 'events' ''' for (callArgs, _) in events: if (callArgs[0].type == RouteEvent.ADVERTISE): self.assertIn(callArgs[0].routeEntry, advertisedRoutes, "Bad advertised route") advertisedRoutes.remove(callArgs[0].routeEntry) else: # WITHDRAW self.assertIn(callArgs[0].routeEntry.nlri, withdrawnNLRIs, "Bad withdrawn route") withdrawnNLRIs.remove(callArgs[0].routeEntry.nlri) self.assertEqual(0, len(advertisedRoutes), "some routes not advert'd") self.assertEqual(0, len(withdrawnNLRIs), "some routes not withdrawn") def testA1_SubscriptionsWithNoRouteTosynthesize(self): # Worker1 subscribes to RT1 and RT2 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1, RT2]) # Waiting for RouteTableManager thread finishes to process the # subscriptions self._wait() # check subscriptions self._checkSubscriptions(worker1, [MATCH1, MATCH2]) def testA2_SubscriptionsWithRouteTosynthesize(self): # BGPPeerWorker1 advertises a route for RT1 and RT2 bgpPeerWorker1 = self._newworker("BGPWorker1", BGPPeerWorker) evt1 = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], bgpPeerWorker1, NH1) # BGPPeerWorker1 advertises an other route for RT2 evt2 = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI2, [RT2], bgpPeerWorker1, NH1) # BGPPeerWorker1 subscribes to RT1 self._workerSubscriptions(bgpPeerWorker1, [RT1]) # Worker1 subscribes to RT1 and RT2 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1, RT2]) # Worker2 subscribes to RT1 worker2 = self._newworker("Worker-2", Worker) self._workerSubscriptions(worker2, [RT1]) # Worker3 subscribes to RT3 worker3 = self._newworker("Worker-3", Worker) self._workerSubscriptions(worker3, [RT3]) # BGPPeerWorker2 subscribes to RT1 bgpPeerWorker2 = self._newworker("BGPWorker2", BGPPeerWorker) self._workerSubscriptions(bgpPeerWorker2, [RT1]) # Waiting for RouteTableManager thread finishes to process the # subscription self._wait() # check route entry synthesized self.assertEqual(0, bgpPeerWorker1.enqueue.call_count, "Route should not be synthesized to its source") self.assertEqual(0, worker3.enqueue.call_count, "no route should be synthesized to Worker3") self.assertEqual(0, bgpPeerWorker2.enqueue.call_count, "Route should not be synthesized between BGP workers") self.assertEqual(2, worker1.enqueue.call_count, "2 advertise events should be synthesized to Worker1") self._checkEventsCalls(worker1.enqueue.call_args_list, [evt1.routeEntry, evt2.routeEntry], []) self.assertEqual(1, worker2.enqueue.call_count, "1 advertise event should be synthesized to Worker2") self._checkEventsCalls( worker2.enqueue.call_args_list, [evt1.routeEntry], []) def testA3_ReSubscription(self): # BGPPeerWorker1 advertises a route for RT1 and RT2 bgpPeerWorker1 = self._newworker( "BGPWorker1", BGPPeerWorker) routeEvent = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], bgpPeerWorker1, NH1) # Worker1 subscribes to RT1 and RT2 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1, RT2]) # Worker1 subscribes again to RT1 self._workerSubscriptions(worker1, [RT1]) # Worker2 subscribes to RT1 worker2 = self._newworker("Worker-2", Worker) self._workerSubscriptions(worker2, [RT1]) # Worker1 subscribes again to RT2 self._workerSubscriptions(worker2, [RT2]) # Waiting for RouteTableManager thread finishes to process the # subscription self._wait() # check route entry synthesized self.assertEqual(1, worker1.enqueue.call_count, "1 route advertised should be synthesized to Worker1") self._checkEventsCalls(worker1.enqueue.call_args_list, [routeEvent.routeEntry], []) self.assertEqual(1, worker2.enqueue.call_count, "1 route advertised should be synthesized to Worker2") self._checkEventsCalls(worker2.enqueue.call_args_list, [routeEvent.routeEntry], []) def testB1_UnsubscriptionWithNoRouteTosynthesize(self): # Worker1 subscribes to RT1 and RT2 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1, RT2]) # BGPPeerWorker1 subscribes to RT1 and RT2 bgpPeerWorker1 = self._newworker("BGPWorker1", BGPPeerWorker) self._workerSubscriptions(bgpPeerWorker1, [RT1, RT2]) # Worker1 unsubscribes to RT1 self._workerUnsubscriptions(worker1, [RT1]) # BGPPeerWorker1 unsubscribes to RT1 and RT2 self._workerUnsubscriptions(bgpPeerWorker1, [RT1, RT2]) # Waiting for RouteTableManager thread finishes to process the # subscription self._wait() # check subscription/unsubscriptions self._checkUnsubscriptions(worker1, [MATCH1]) self._checkSubscriptions(worker1, [MATCH2]) self._checkUnsubscriptions(bgpPeerWorker1, [MATCH1, MATCH2]) def testB2_UnsubscriptionWithRouteTosynthesize(self): # BGPPeerWorker1 advertises a route for RT1 bgpPeerWorker1 = self._newworker( "BGPWorker1", BGPPeerWorker) evt1 = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], bgpPeerWorker1, NH1) # BGPPeerWorker1 advertises an other route for RT2 evt2 = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI2, [RT2], bgpPeerWorker1, NH1) # BGPPeerWorker1 subscribes to RT1 self._workerSubscriptions(bgpPeerWorker1, [RT1]) # Worker1 subscribes to RT1 and RT2 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1, RT2]) # Worker2 subscribes to RT2 worker2 = self._newworker("Worker-2", Worker) self._workerSubscriptions(worker2, [RT2]) # Worker3 subscribes to RT3 worker3 = self._newworker("Worker-3", Worker) self._workerSubscriptions(worker3, [RT3]) # BGPPeerWorker2 subscribes to RT1 bgpPeerWorker2 = self._newworker("BGPWorker2", BGPPeerWorker) self._workerSubscriptions(bgpPeerWorker2, [RT1]) # Workers and BGPPeerWorker unsubscriptions self._workerUnsubscriptions(bgpPeerWorker1, [RT1]) self._workerUnsubscriptions(worker1, [RT1]) self._workerUnsubscriptions(worker2, [RT2]) self._workerUnsubscriptions(worker3, [RT3]) self._workerUnsubscriptions(bgpPeerWorker2, [RT1]) # Waiting for RouteTableManager thread finishes to process the # subscription self._wait() # check route entry synthesized self.assertEqual(0, bgpPeerWorker1.enqueue.call_count, "Route should not be synthesized to its source") self.assertEqual(0, worker3.enqueue.call_count, "no route should be synthesized to Worker3") self.assertEqual(0, bgpPeerWorker2.enqueue.call_count, "Route should not be synthesized between " "BGPPeerWorkers") self.assertEqual(2, worker1.enqueue.call_count, "2 advertise event should be synthesized to Worker1") self._checkEventsCalls(worker1.enqueue.call_args_list, [evt1.routeEntry, evt2.routeEntry], []) self.assertEqual(4, worker2.enqueue.call_count, "4 events should be synthesized to Worker2: " "2 advertise and 2 withdraw") self._checkEventsCalls(worker2.enqueue.call_args_list, [evt1.routeEntry, evt2.routeEntry], [evt1.routeEntry.nlri, evt2.routeEntry.nlri]) def testB3_UnsubscriptionNotRegistered(self): # Worker1 subscribes to RT1 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1]) # Worker1 unsubscribes to RT2 self._workerUnsubscriptions(worker1, [RT2]) # BGPPeerWorker1 unsubscribes to RT1 bgpPeerWorker1 = self._newworker("BGPWorker1", BGPPeerWorker) self._workerUnsubscriptions(bgpPeerWorker1, [RT1, RT2]) # Waiting for RouteTableManager thread finishes to process the # subscription self._wait() # check subscription/unsubscriptions self._checkSubscriptions(worker1, [MATCH1]) self._checkUnsubscriptions(bgpPeerWorker1, [MATCH1, MATCH2]) def testC1_routeAdvertiseByWorkerWithoutPropagation(self): # Worker1 advertises a route for RT1 and RT2 worker1 = self._newworker("Worker-1", Worker) routeEvent = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], worker1, NH1) # check route entry has been inserted self._checkCalls(worker1, routeEvent.routeEntry) def testC2_routeWithdrawByWorkerWithoutPropagation(self): # Worker1 advertises then withdraws a route worker1 = self._newworker("Worker-1", Worker) self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], worker1, NH1) routeEvent = self._newRouteEvent(RouteEvent.WITHDRAW, NLRI1, [RT1], worker1, NH1) # check route entry has been removed self._checkNoRouteEntry(worker1, routeEvent.routeEntry) def testC3_routeAdvertiseByBGPPeerWithPropagation(self): # Worker1 subscribes to RT1 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1]) # Worker2 subscribes to RT2 worker2 = self._newworker("Worker-2", Worker) self._workerSubscriptions(worker2, [RT2]) # BGPPeerWorker1 subscribes to RT1 and RT2 bgpPeerWorker1 = self._newworker("BGPWorker1", BGPPeerWorker) self._workerSubscriptions(bgpPeerWorker1, [RT1, RT2]) # BGPPeerWorker2 subscribes to RT1 and RT2 bgpPeerWorker2 = self._newworker("BGPWorker2", BGPPeerWorker) self._workerSubscriptions(bgpPeerWorker2, [RT1, RT2]) # BGPPeerWorker1 advertises a route for RT1 routeEvent = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1], bgpPeerWorker1, NH1) # check routeEvent propagation self.assertEqual(1, worker1.enqueue.call_count, "1 route should be propagated to Worker1") self._checkEventsCalls(worker1.enqueue.call_args_list, [routeEvent.routeEntry], []) self.assertEqual(0, worker2.enqueue.call_count, "no route should be propagated to Worker2") self.assertEqual(0, bgpPeerWorker1.enqueue.call_count, "Route should not be propagated to its source") self.assertEqual(0, bgpPeerWorker2.enqueue.call_count, "Route should not be propagated between BGP workers") def testC4_routeWithdrawByPeerWorkerWithPropagation(self): # Worker1 subscribes to RT1 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1]) # Worker2 subscribes to RT2 worker2 = self._newworker("Worker-2", Worker) self._workerSubscriptions(worker2, [RT2]) # Worker3 subscribes to RT3 worker3 = self._newworker("Worker-3", Worker) self._workerSubscriptions(worker3, [RT3]) # BGPPeerWorker1 subscribes to RT1 bgpPeerWorker1 = self._newworker("BGPWorker1", BGPPeerWorker) self._workerSubscriptions(bgpPeerWorker1, [RT1]) # BGPPeerWorker2 subscribes to RT2 bgpPeerWorker2 = self._newworker("BGPWorker2", BGPPeerWorker) self._workerSubscriptions(bgpPeerWorker2, [RT2]) # BGPPeerWorker1 advertises a route for RT1 and RT2 routeEventA = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], bgpPeerWorker1, NH1) # BGPPeerWorker1 withdraw previous route (without RT routeEventW = self._newRouteEvent(RouteEvent.WITHDRAW, NLRI1, [], bgpPeerWorker1, NH1) # check routeEvent propagation self.assertEqual(2, worker1.enqueue.call_count, "2 routes should be propagated to Worker1") self._checkEventsCalls(worker1.enqueue.call_args_list, [routeEventA.routeEntry], [routeEventW.routeEntry.nlri]) self.assertEqual(2, worker2.enqueue.call_count, "2 routes should be propagated to Worker2") self._checkEventsCalls(worker2.enqueue.call_args_list, [routeEventA.routeEntry], [routeEventW.routeEntry.nlri]) self.assertEqual(0, worker3.enqueue.call_count, "No route should be propagated to Worker3") self.assertEqual(0, bgpPeerWorker1.enqueue.call_count, "Route should not be propagated to its source") self.assertEqual(0, bgpPeerWorker2.enqueue.call_count, "Route should not be propagated between BGP workers") def testC5_routeUpdateByBGPPeerWithWithdrawPropagation(self): # Worker1 subscribes to RT1 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1]) # Worker2 subscribes to RT2 worker2 = self._newworker("Worker-2", Worker) self._workerSubscriptions(worker2, [RT2]) # Worker3 subscribes to RT3 worker3 = self._newworker("Worker-3", Worker) self._workerSubscriptions(worker3, [RT3]) # BGPPeerWorker1 advertises a route for RT1, RT2 and RT3 bgpPeerWorker1 = self._newworker( "BGPWorker1", BGPPeerWorker) evt1 = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2, RT3], bgpPeerWorker1, NH1) # BGPPeerWorker1 advertises the same nlri with attributes NH and RTs # modification evt2 = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], bgpPeerWorker1, NH2) # check route event propagation # TO DO : check routeEvent.replacedRoute self.assertEqual(0, bgpPeerWorker1.enqueue.call_count, "Route should not be propagated to its source") self.assertEqual(2, worker1.enqueue.call_count, "2 routes should be advertised to Worker1") self._checkEventsCalls(worker1.enqueue.call_args_list, [evt1.routeEntry, evt2.routeEntry], []) self.assertEqual(2, worker2.enqueue.call_count, "2 routes should be advertised to Worker2") self._checkEventsCalls(worker2.enqueue.call_args_list, [evt1.routeEntry, evt2.routeEntry], []) self.assertEqual(2, worker3.enqueue.call_count, "2 routes should be advert/withdraw to Worker3") self._checkEventsCalls(worker3.enqueue.call_args_list, [evt1.routeEntry], [evt1.routeEntry.nlri]) def testC6_routeReadvertised(self): # Worker1 subscribes to RT1 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1, RT2, RT3]) # BGPPeerWorker1 advertises a route for RT1, RT2 and RT3 bgpPeerWorker1 = self._newworker("BGPWorker1", BGPPeerWorker) evt1 = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], bgpPeerWorker1, NH1) # BGPPeerWorker1 advertises the same nlri with same attributes and RTs self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], bgpPeerWorker1, NH1) # check route event propagation self.assertEqual(0, bgpPeerWorker1.enqueue.call_count, "Route should not be propagated to its source") self.assertEqual(1, worker1.enqueue.call_count, "only 1 route should be advertised to Worker1") self._checkEventsCalls(worker1.enqueue.call_args_list, [evt1.routeEntry], []) def testC7_routeWithdrawNotRegistered(self): # Worker1 subscribes to RT1 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1, RT2]) # BGPPeerWorker1 advertises a route for RT1 and RT2 bgpPeerWorker1 = self._newworker( "BGPWorker1", BGPPeerWorker) evt1 = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], bgpPeerWorker1, NH1) # BGPPeerWorker1 withdraw a not registered route (without RT self._newRouteEvent(RouteEvent.WITHDRAW, NLRI2, [], bgpPeerWorker1, NH1) # Waiting for RouteTableManager thread finishes to process routeEvent self._wait() # check routeEvent propagation self.assertEqual(1, worker1.enqueue.call_count, "1 route1 should be propagated to Worker1") self._checkEventsCalls(worker1.enqueue.call_args_list, [evt1.routeEntry], []) self.assertEqual(0, bgpPeerWorker1.enqueue.call_count, "Route should not be propagated back to its source") def testD1_WorkerCleanup(self): # Worker1 subscribes to RT1 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1]) # Worker2 subscribes to RT2 worker2 = self._newworker("Worker-2", Worker) self._workerSubscriptions(worker2, [RT2]) # BGPPeerWorker1 subscribes to RT1 and RT2 bgpPeerWorker1 = self._newworker( "BGPWorker1", BGPPeerWorker) self._workerSubscriptions(bgpPeerWorker1, [RT1, RT2]) # BGPPeerWorker1 advertises a route for RT1 and RT2 evt1 = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], bgpPeerWorker1, NH1) # BGPPeerWorker1 advertises an other route for RT2 evt2 = self._newRouteEvent(RouteEvent.ADVERTISE, NLRI2, [RT2], bgpPeerWorker1, NH1) # Cleanup Worker1 self.routeTableManager.enqueue(WorkerCleanupEvent(bgpPeerWorker1)) # Waiting for RouteTableManager thread finishes to process the # subscriptions self._wait() # check unsubscriptions self._checkUnsubscriptions(bgpPeerWorker1, [MATCH1, MATCH2]) # Check route synthesize to Worker1 and Worker2 self.assertEqual(2, worker1.enqueue.call_count, "2 routes should be advert/withdraw to Worker1") self._checkEventsCalls(worker1.enqueue.call_args_list, [evt1.routeEntry], [evt1.routeEntry.nlri]) self.assertEqual(4, worker2.enqueue.call_count, "4 routes should be advert/withdraw to Worker2") self._checkEventsCalls(worker2.enqueue.call_args_list, [evt1.routeEntry, evt2.routeEntry], [evt1.routeEntry.nlri, evt2.routeEntry.nlri]) # Check route entries have been removed for BGPPeerWorker1 self._checkNoRouteEntry(bgpPeerWorker1, evt1.routeEntry) self._checkNoRouteEntry(bgpPeerWorker1, evt2.routeEntry) def testE1_DumpState(self): # BGPPeerWorker1 advertises a route for RT1 and RT2 bgpPeerWorker1 = self._newworker("BGPWorker1", BGPPeerWorker) self._newRouteEvent(RouteEvent.ADVERTISE, NLRI1, [RT1, RT2], bgpPeerWorker1, NH1) # BGPPeerWorker1 advertises an other route for RT2 self._newRouteEvent(RouteEvent.ADVERTISE, NLRI2, [RT2], bgpPeerWorker1, NH1) # BGPPeerWorker1 subscribes to RT1 self._workerSubscriptions(bgpPeerWorker1, [RT1]) # Worker1 subscribes to RT1 and RT2 worker1 = self._newworker("Worker-1", Worker) self._workerSubscriptions(worker1, [RT1, RT2]) # Worker2 subscribes to RT1 worker2 = self._newworker("Worker-2", Worker) self._workerSubscriptions(worker2, [RT1]) # Worker3 subscribes to RT3 worker3 = self._newworker("Worker-3", Worker) self._workerSubscriptions(worker3, [RT3]) # Waiting for RouteTableManager thread finishes to process the # subscriptions and route event processing self._wait() self.routeTableManager._dumpState() def testF1_testEmptyRT(self): # worker advertises a route with no RT w1 = self._newworker("Worker1", Worker) subscribe = Subscription(AFI(AFI.ipv4), SAFI(SAFI.mpls_vpn), None, w1) self.routeTableManager.enqueue(subscribe) w2 = self._newworker("Worker2", Worker) routeEvent = RouteEvent(RouteEvent.ADVERTISE, RouteEntry( AFI(AFI.ipv4), SAFI(SAFI.mpls_vpn), None, NLRI1, Attributes(), w2), w2) self.routeTableManager.enqueue(routeEvent) self._wait() self.assertEqual(1, w1.enqueue.call_count, "1 route advertised should be synthesized to Worker1")