SDFAB-104 Support routing via next hop in single leaf pair topology
The original design principle we adopted while implementing dual-homing is only recovering local failure using pair link.
Routing via next hop is a global thing recovered by updating ECMP hashing.
However, there is no spine in the single leaf pair setup so we need additional logic to recover this using pair link.
Change-Id: I3d648b139038be69656dd86b4c40d12bf10f50b2
diff --git a/impl/src/main/java/org/onosproject/segmentrouting/RouteHandler.java b/impl/src/main/java/org/onosproject/segmentrouting/RouteHandler.java
index c0ccfb6..dc1a25a 100644
--- a/impl/src/main/java/org/onosproject/segmentrouting/RouteHandler.java
+++ b/impl/src/main/java/org/onosproject/segmentrouting/RouteHandler.java
@@ -23,13 +23,13 @@
import org.onlab.packet.VlanId;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Host;
-import org.onosproject.net.HostLocation;
import org.onosproject.net.PortNumber;
import org.onosproject.net.host.HostEvent;
import org.onosproject.routeservice.ResolvedRoute;
import org.onosproject.routeservice.RouteEvent;
import org.onosproject.net.DeviceId;
import org.onosproject.routeservice.RouteInfo;
+import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -88,9 +88,9 @@
return;
}
- ResolvedRoute rr = routes.stream().findFirst().orElse(null);
- if (rr == null) {
+ if (routes.isEmpty()) {
log.warn("No resolved route found. Abort processRouteAddedInternal");
+ return;
}
Set<ConnectPoint> allLocations = Sets.newHashSet();
@@ -102,7 +102,6 @@
log.debug("RouteAdded. populateSubnet {}, {}", allLocations, allPrefixes);
srManager.defaultRoutingHandler.populateSubnet(allLocations, allPrefixes);
-
routes.forEach(route -> {
IpPrefix prefix = route.prefix();
MacAddress nextHopMac = route.nextHopMac();
@@ -115,6 +114,8 @@
log.debug("RouteAdded populateRoute {}, {}, {}, {}", location, prefix, nextHopMac, nextHopVlan);
srManager.defaultRoutingHandler.populateRoute(location.deviceId(), prefix,
nextHopMac, nextHopVlan, location.port(), false);
+
+ processSingleLeafPairIfNeeded(locations, location, prefix, nextHopVlan);
});
});
}
@@ -189,6 +190,8 @@
log.debug("RouteUpdated. populateRoute {}, {}, {}, {}", location, prefix, nextHopMac, nextHopVlan);
srManager.defaultRoutingHandler.populateRoute(location.deviceId(), prefix,
nextHopMac, nextHopVlan, location.port(), false);
+
+ processSingleLeafPairIfNeeded(locations, location, prefix, nextHopVlan);
});
});
}
@@ -223,19 +226,7 @@
srManager.deviceConfiguration.removeSubnet(location, prefix);
// We don't need to call revokeRoute again since revokeSubnet will remove the prefix
// from all devices, including the ones that next hop attaches to.
-
- // Also remove redirection flows on the pair device if exists.
- Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(location.deviceId());
- Optional<PortNumber> pairLocalPort = srManager.getPairLocalPort(location.deviceId());
- if (pairDeviceId.isPresent() && pairLocalPort.isPresent()) {
- // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
- // when the host is untagged
- VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(nextHopVlan);
-
- log.debug("RouteRemoved. revokeRoute {}, {}, {}, {}", location, prefix, nextHopMac, nextHopVlan);
- srManager.defaultRoutingHandler.revokeRoute(pairDeviceId.get(), prefix,
- nextHopMac, vlanId, pairLocalPort.get(), false);
- }
+ // revokeSubnet will also remove flow on the pair device (if exist) pointing to current location.
});
});
}
@@ -245,19 +236,21 @@
MacAddress hostMac = event.subject().mac();
VlanId hostVlanId = event.subject().vlan();
- Set<HostLocation> prevLocations = event.prevSubject().locations();
- Set<HostLocation> newLocations = event.subject().locations();
- Set<ConnectPoint> connectPoints = newLocations.stream()
- .map(l -> (ConnectPoint) l).collect(Collectors.toSet());
+ Set<ConnectPoint> prevLocations = event.prevSubject().locations()
+ .stream().map(h -> (ConnectPoint) h)
+ .collect(Collectors.toSet());
+ Set<ConnectPoint> newLocations = event.subject().locations()
+ .stream().map(h -> (ConnectPoint) h)
+ .collect(Collectors.toSet());
List<Set<IpPrefix>> batchedSubnets =
srManager.deviceConfiguration.getBatchedSubnets(event.subject().id());
- Set<DeviceId> newDeviceIds = newLocations.stream().map(HostLocation::deviceId)
+ Set<DeviceId> newDeviceIds = newLocations.stream().map(ConnectPoint::deviceId)
.collect(Collectors.toSet());
// Set of deviceIDs of the previous locations where the host was connected
// Used to determine if host moved to different connect points
// on same device or moved to a different device altogether
- Set<DeviceId> oldDeviceIds = prevLocations.stream().map(HostLocation::deviceId)
+ Set<DeviceId> oldDeviceIds = prevLocations.stream().map(ConnectPoint::deviceId)
.collect(Collectors.toSet());
// L3 Ucast bucket needs to be updated only once per host
@@ -288,7 +281,7 @@
batchedSubnets.forEach(subnets -> {
log.debug("HostMoved. populateSubnet {}, {}", newLocations, subnets);
- srManager.defaultRoutingHandler.populateSubnet(connectPoints, subnets);
+ srManager.defaultRoutingHandler.populateSubnet(newLocations, subnets);
subnets.forEach(prefix -> {
// For each old location
@@ -304,7 +297,10 @@
srManager.deviceConfiguration.removeSubnet(prevLocation, prefix);
// Do not remove flow from a device if the route is still reachable via its pair device.
- // populateSubnet will update the flow to point to its pair device via spine.
+ // If spine exists,
+ // populateSubnet above will update the flow to point to its pair device via spine.
+ // If spine does not exist,
+ // processSingleLeafPair below will update the flow to point to its pair device via pair port.
DeviceId pairDeviceId = srManager.getPairDeviceId(prevLocation.deviceId()).orElse(null);
if (newLocations.stream().anyMatch(n -> n.deviceId().equals(pairDeviceId))) {
return;
@@ -327,6 +323,10 @@
hostMac, hostVlanId, newLocation.port(), false);
}
});
+
+ newLocations.forEach(location -> {
+ processSingleLeafPairIfNeeded(newLocations, location, prefix, hostVlanId);
+ });
});
});
@@ -370,4 +370,45 @@
return Objects.nonNull(srManager.deviceConfiguration) &&
Objects.nonNull(srManager.defaultRoutingHandler);
}
+
+ protected boolean processSingleLeafPairIfNeeded(Set<ConnectPoint> locations, ConnectPoint location, IpPrefix prefix,
+ VlanId nextHopVlan) {
+ // Special handling for single leaf pair
+ if (!srManager.getInfraDeviceIds().isEmpty()) {
+ log.debug("Spine found. Skip single leaf pair handling");
+ return false;
+ }
+ Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(location.deviceId());
+ if (pairDeviceId.isEmpty()) {
+ log.debug("Pair device of {} not found", location.deviceId());
+ return false;
+ }
+ if (locations.stream().anyMatch(l -> l.deviceId().equals(pairDeviceId.get()))) {
+ log.debug("Pair device has a next hop available. Leave it as is.");
+ return false;
+ }
+ Optional<PortNumber> pairRemotePort = srManager.getPairLocalPort(pairDeviceId.get());
+ if (pairRemotePort.isEmpty()) {
+ log.debug("Pair remote port of {} not found", pairDeviceId.get());
+ return false;
+ }
+ // Use routerMac of the pair device as the next hop
+ MacAddress effectiveMac;
+ try {
+ effectiveMac = srManager.getDeviceMacAddress(location.deviceId());
+ } catch (DeviceConfigNotFoundException e) {
+ log.warn("Abort populateRoute on pair device {}. routerMac not found", pairDeviceId);
+ return false;
+ }
+ // Since the pairLocalPort is trunk port, use assigned vlan of original port
+ // when the host is untagged
+ VlanId effectiveVlan = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(nextHopVlan);
+
+ log.debug("Single leaf pair. populateRoute {}/{}, {}, {}, {}", pairDeviceId, pairRemotePort, prefix,
+ effectiveMac, effectiveVlan);
+ srManager.defaultRoutingHandler.populateRoute(pairDeviceId.get(), prefix,
+ effectiveMac, effectiveVlan, pairRemotePort.get(), false);
+
+ return true;
+ }
}
diff --git a/impl/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java b/impl/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
index 45d2b4d..e9cde67 100644
--- a/impl/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
+++ b/impl/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
@@ -231,7 +231,7 @@
mockNetworkConfigRegistry.applyConfig(dev4Config);
// Initialize Segment Routing Manager
- SegmentRoutingManager srManager = new MockSegmentRoutingManager(NEXT_TABLE);
+ SegmentRoutingManager srManager = new MockSegmentRoutingManager(NEXT_TABLE, Maps.newHashMap());
srManager.storageService = createMock(StorageService.class);
expect(srManager.storageService.consistentMapBuilder()).andReturn(new TestConsistentMap.Builder<>()).anyTimes();
expect(srManager.storageService.consistentMultimapBuilder()).andReturn(
diff --git a/impl/src/test/java/org/onosproject/segmentrouting/MockSegmentRoutingManager.java b/impl/src/test/java/org/onosproject/segmentrouting/MockSegmentRoutingManager.java
index ad098b4..fdd3951 100644
--- a/impl/src/test/java/org/onosproject/segmentrouting/MockSegmentRoutingManager.java
+++ b/impl/src/test/java/org/onosproject/segmentrouting/MockSegmentRoutingManager.java
@@ -16,12 +16,14 @@
package org.onosproject.segmentrouting;
+import org.onlab.packet.MacAddress;
import org.onosproject.core.DefaultApplicationId;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
@@ -30,11 +32,16 @@
*/
public class MockSegmentRoutingManager extends SegmentRoutingManager {
private Map<Integer, TrafficTreatment> nextTable;
+ private Map<DeviceId, MacAddress> routerMacs;
+ private List<DeviceId> infraDeviceIds;
private AtomicInteger atomicNextId = new AtomicInteger();
- MockSegmentRoutingManager(Map<Integer, TrafficTreatment> nextTable) {
+ MockSegmentRoutingManager(Map<Integer, TrafficTreatment> nextTable,
+ Map<DeviceId, MacAddress> routerMacs) {
appId = new DefaultApplicationId(1, SegmentRoutingManager.APP_NAME);
this.nextTable = nextTable;
+ this.routerMacs = routerMacs;
+ this.infraDeviceIds = List.of(DeviceId.deviceId("device:1"));
}
@Override
@@ -46,4 +53,18 @@
nextTable.put(nextId, treatment);
return nextId;
}
+
+ @Override
+ public List<DeviceId> getInfraDeviceIds() {
+ return List.copyOf(infraDeviceIds);
+ }
+
+ public void setInfraDeviceIds(List<DeviceId> infraDeviceIds) {
+ this.infraDeviceIds = infraDeviceIds;
+ }
+
+ @Override
+ public MacAddress getDeviceMacAddress(DeviceId deviceId) {
+ return routerMacs.get(deviceId);
+ }
}
diff --git a/impl/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java b/impl/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java
index 690f87e..e09aaa7 100644
--- a/impl/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java
+++ b/impl/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java
@@ -51,6 +51,7 @@
import org.onosproject.store.service.TestConsistentMap;
import org.onosproject.store.service.TestConsistentMultimap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -66,7 +67,7 @@
* Unit test for {@link RouteHandler}.
*/
public class RouteHandlerTest {
- private SegmentRoutingManager srManager;
+ private MockSegmentRoutingManager srManager;
private RouteHandler routeHandler;
private HostService hostService;
@@ -149,6 +150,14 @@
null, null, null, null, null);
private static final Set<Interface> INTERFACES = Sets.newHashSet(IF_CP1, IF_CP2);
+ // Route MAC
+ private static final MacAddress ROUTER_MAC_1 = MacAddress.valueOf("00:AA:00:00:00:01");
+ private static final MacAddress ROUTER_MAC_2 = MacAddress.valueOf("00:AA:00:00:00:01");
+ private static final Map<DeviceId, MacAddress> ROUTER_MACS = Map.of(
+ CP1.deviceId(), ROUTER_MAC_1,
+ CP2.deviceId(), ROUTER_MAC_2
+ );
+
@Before
public void setUp() {
ObjectMapper mapper = new ObjectMapper();
@@ -169,7 +178,7 @@
mockNetworkConfigRegistry.applyConfig(dev2Config);
// Initialize Segment Routing Manager
- srManager = new MockSegmentRoutingManager(NEXT_TABLE);
+ srManager = new MockSegmentRoutingManager(NEXT_TABLE, ROUTER_MACS);
srManager.storageService = createMock(StorageService.class);
expect(srManager.storageService.consistentMapBuilder()).andReturn(new TestConsistentMap.Builder<>()).anyTimes();
expect(srManager.storageService.consistentMultimapBuilder()).andReturn(
@@ -249,6 +258,31 @@
assertEquals(1, SUBNET_TABLE.size());
}
+ // Only one of two dual-homed next hops present.
+ // Expect one routing table to be programmed with direct flow.
+ // The other is pointing to pair port
+ @Test
+ public void initDhcpRouteSingleDualHomeNextHopSingleLeafPair() {
+ srManager.setInfraDeviceIds(List.of());
+
+ ROUTE_STORE.put(P1, Sets.newHashSet(DHCP_RR1));
+
+ routeHandler.init(CP1.deviceId());
+
+ assertEquals(2, ROUTING_TABLE.size());
+ MockRoutingTableValue rtv1 = ROUTING_TABLE.get(new MockRoutingTableKey(CP1.deviceId(), P1));
+ assertEquals(M1, rtv1.macAddress);
+ assertEquals(V1, rtv1.vlanId);
+ assertEquals(CP1.port(), rtv1.portNumber);
+
+ MockRoutingTableValue rtv2 = ROUTING_TABLE.get(new MockRoutingTableKey(CP2.deviceId(), P1));
+ assertEquals(ROUTER_MAC_1, rtv2.macAddress);
+ assertEquals(V1, rtv2.vlanId);
+ assertEquals(P9, rtv2.portNumber);
+
+ assertEquals(1, SUBNET_TABLE.size());
+ }
+
// Both dual-homed next hops present.
// Expect both routing table to be programmed with direct flow
@Test
@@ -272,6 +306,29 @@
}
@Test
+ public void initDhcpRouteBothDualHomeNextHopSingleLeafPair() {
+ srManager.setInfraDeviceIds(List.of());
+
+ ROUTE_STORE.put(P1, Sets.newHashSet(DHCP_RR3));
+
+ routeHandler.init(CP1.deviceId());
+
+ assertEquals(2, ROUTING_TABLE.size());
+ MockRoutingTableValue rtv1 = ROUTING_TABLE.get(new MockRoutingTableKey(CP1.deviceId(), P1));
+ assertEquals(M3, rtv1.macAddress);
+ assertEquals(V3, rtv1.vlanId);
+ assertEquals(CP1.port(), rtv1.portNumber);
+
+ MockRoutingTableValue rtv2 = ROUTING_TABLE.get(new MockRoutingTableKey(CP2.deviceId(), P1));
+ assertEquals(M3, rtv2.macAddress);
+ assertEquals(V3, rtv2.vlanId);
+ assertEquals(CP2.port(), rtv2.portNumber);
+
+ assertEquals(2, SUBNET_TABLE.size());
+ }
+
+
+ @Test
public void processRouteAdded() {
reset(srManager.deviceConfiguration);
srManager.deviceConfiguration.addSubnet(CP1, P1);
@@ -399,6 +456,37 @@
}
@Test
+ public void testOneDualHomedAddedSingleLeafPair() {
+ srManager.setInfraDeviceIds(List.of());
+
+ reset(srManager.deviceConfiguration);
+ srManager.deviceConfiguration.addSubnet(CP1, P1);
+ expectLastCall().once();
+ srManager.deviceConfiguration.addSubnet(CP2, P1);
+ expectLastCall().once();
+ replay(srManager.deviceConfiguration);
+
+ RouteEvent re = new RouteEvent(RouteEvent.Type.ROUTE_ADDED, RR3, Sets.newHashSet(RR3));
+ routeHandler.processRouteAdded(re);
+
+ assertEquals(2, ROUTING_TABLE.size());
+ MockRoutingTableValue rtv1 = ROUTING_TABLE.get(new MockRoutingTableKey(CP1.deviceId(), P1));
+ MockRoutingTableValue rtv2 = ROUTING_TABLE.get(new MockRoutingTableKey(CP2.deviceId(), P1));
+ assertEquals(M3, rtv1.macAddress);
+ assertEquals(M3, rtv2.macAddress);
+ assertEquals(V3, rtv1.vlanId);
+ assertEquals(V3, rtv2.vlanId);
+ assertEquals(CP1.port(), rtv1.portNumber);
+ assertEquals(CP2.port(), rtv2.portNumber);
+
+ assertEquals(2, SUBNET_TABLE.size());
+ assertTrue(SUBNET_TABLE.get(CP1).contains(P1));
+ assertTrue(SUBNET_TABLE.get(CP2).contains(P1));
+
+ verify(srManager.deviceConfiguration);
+ }
+
+ @Test
public void testOneSingleHomedToTwoSingleHomed() {
processRouteAdded();
@@ -480,6 +568,48 @@
assertEquals(V3, rtv1.vlanId);
assertEquals(CP1.port(), rtv1.portNumber);
+ MockRoutingTableValue rtv2 = ROUTING_TABLE.get(new MockRoutingTableKey(CP2.deviceId(), P1));
+ assertEquals(M3, rtv2.macAddress);
+ assertEquals(V3, rtv2.vlanId);
+ assertEquals(CP2.port(), rtv2.portNumber);
+
+ // ECMP route table hasn't changed
+ assertEquals(1, SUBNET_TABLE.size());
+ assertTrue(SUBNET_TABLE.get(CP1).contains(P1));
+
+ verify(srManager.deviceConfiguration);
+ }
+
+ @Test
+ public void testDualHomedSingleLocationFailSingleLeafPair() {
+ srManager.setInfraDeviceIds(List.of());
+
+ testOneDualHomedAddedSingleLeafPair();
+
+ ROUTE_STORE.put(P1, Sets.newHashSet(RR3));
+
+ reset(srManager.deviceConfiguration);
+ expect(srManager.deviceConfiguration.getBatchedSubnets(H3D.id()))
+ .andReturn(Lists.<Set<IpPrefix>>newArrayList(Sets.newHashSet(P1)));
+ srManager.deviceConfiguration.removeSubnet(CP2, P1);
+ expectLastCall().once();
+ replay(srManager.deviceConfiguration);
+
+ HostEvent he = new HostEvent(HostEvent.Type.HOST_MOVED, H3S, H3D);
+ routeHandler.processHostMovedEvent(he);
+
+ // We do not remove the route on CP2. Instead, we let the subnet population overrides it
+ assertEquals(2, ROUTING_TABLE.size());
+ MockRoutingTableValue rtv1 = ROUTING_TABLE.get(new MockRoutingTableKey(CP1.deviceId(), P1));
+ assertEquals(M3, rtv1.macAddress);
+ assertEquals(V3, rtv1.vlanId);
+ assertEquals(CP1.port(), rtv1.portNumber);
+
+ MockRoutingTableValue rtv2 = ROUTING_TABLE.get(new MockRoutingTableKey(CP2.deviceId(), P1));
+ assertEquals(ROUTER_MAC_1, rtv2.macAddress);
+ assertEquals(V3, rtv2.vlanId);
+ assertEquals(P9, rtv2.portNumber);
+
// ECMP route table hasn't changed
assertEquals(1, SUBNET_TABLE.size());
assertTrue(SUBNET_TABLE.get(CP1).contains(P1));
@@ -510,6 +640,30 @@
}
@Test
+ public void testDualHomedBothLocationFaiSingleLeafPair() {
+ srManager.setInfraDeviceIds(List.of());
+
+ testDualHomedSingleLocationFailSingleLeafPair();
+
+ hostService = new MockHostService(HOSTS_ONE_FAIL);
+
+ reset(srManager.deviceConfiguration);
+ srManager.deviceConfiguration.removeSubnet(CP1, P1);
+ expectLastCall().once();
+ srManager.deviceConfiguration.removeSubnet(CP2, P1);
+ expectLastCall().once();
+ replay(srManager.deviceConfiguration);
+
+ RouteEvent re = new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, RR3, Sets.newHashSet(RR3));
+ routeHandler.processRouteRemoved(re);
+
+ assertEquals(0, ROUTING_TABLE.size());
+ assertEquals(0, SUBNET_TABLE.size());
+
+ verify(srManager.deviceConfiguration);
+ }
+
+ @Test
public void testSingleHomedToDualHomed() {
testDualHomedSingleLocationFail();
@@ -541,6 +695,39 @@
}
@Test
+ public void testSingleHomedToDualHomedSingleLeafPair() {
+ srManager.setInfraDeviceIds(List.of());
+
+ testDualHomedSingleLocationFailSingleLeafPair();
+
+ reset(srManager.deviceConfiguration);
+ expect(srManager.deviceConfiguration.getBatchedSubnets(H3S.id()))
+ .andReturn(Lists.<Set<IpPrefix>>newArrayList(Sets.newHashSet(P1)));
+ srManager.deviceConfiguration.addSubnet(CP2, P1);
+ expectLastCall().once();
+ replay(srManager.deviceConfiguration);
+
+ HostEvent he = new HostEvent(HostEvent.Type.HOST_MOVED, H3D, H3S);
+ routeHandler.processHostMovedEvent(he);
+
+ assertEquals(2, ROUTING_TABLE.size());
+ MockRoutingTableValue rtv1 = ROUTING_TABLE.get(new MockRoutingTableKey(CP1.deviceId(), P1));
+ MockRoutingTableValue rtv2 = ROUTING_TABLE.get(new MockRoutingTableKey(CP2.deviceId(), P1));
+ assertEquals(M3, rtv1.macAddress);
+ assertEquals(M3, rtv2.macAddress);
+ assertEquals(V3, rtv1.vlanId);
+ assertEquals(V3, rtv2.vlanId);
+ assertEquals(CP1.port(), rtv1.portNumber);
+ assertEquals(CP2.port(), rtv2.portNumber);
+
+ assertEquals(2, SUBNET_TABLE.size());
+ assertTrue(SUBNET_TABLE.get(CP1).contains(P1));
+ assertTrue(SUBNET_TABLE.get(CP2).contains(P1));
+
+ verify(srManager.deviceConfiguration);
+ }
+
+ @Test
public void testTwoSingleHomedRemoved() {
testTwoSingleHomedAdded();
@@ -583,6 +770,28 @@
}
@Test
+ public void testOneDualHomeRemovedSingleLeafPair() {
+ srManager.setInfraDeviceIds(List.of());
+
+ testOneDualHomedAddedSingleLeafPair();
+
+ reset(srManager.deviceConfiguration);
+ srManager.deviceConfiguration.removeSubnet(CP1, P1);
+ expectLastCall().once();
+ srManager.deviceConfiguration.removeSubnet(CP2, P1);
+ expectLastCall().once();
+ replay(srManager.deviceConfiguration);
+
+ RouteEvent re = new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, RR3, Sets.newHashSet(RR3));
+ routeHandler.processRouteRemoved(re);
+
+ assertEquals(0, ROUTING_TABLE.size());
+ assertEquals(0, SUBNET_TABLE.size());
+
+ verify(srManager.deviceConfiguration);
+ }
+
+ @Test
public void testMoreThanTwoNextHop() {
// next hop = CP1, CP2
reset(srManager.deviceConfiguration);
diff --git a/impl/src/test/java/org/onosproject/segmentrouting/RoutingRulePopulatorTest.java b/impl/src/test/java/org/onosproject/segmentrouting/RoutingRulePopulatorTest.java
index 7250ef3..9ac553d 100644
--- a/impl/src/test/java/org/onosproject/segmentrouting/RoutingRulePopulatorTest.java
+++ b/impl/src/test/java/org/onosproject/segmentrouting/RoutingRulePopulatorTest.java
@@ -75,7 +75,7 @@
Set<Interface> interfaces = Sets.newHashSet(u10, t10, t10n20);
interfaceService = new MockInterfaceService(interfaces);
deviceService = EasyMock.createMock(DeviceService.class);
- srManager = new MockSegmentRoutingManager(Maps.newHashMap());
+ srManager = new MockSegmentRoutingManager(Maps.newHashMap(), Maps.newHashMap());
srManager.deviceConfiguration = EasyMock.createMock(DeviceConfiguration.class);
srManager.interfaceService = interfaceService;
srManager.deviceService = deviceService;