CORD-1578 CORD-1708 Handle dual single homed router and single dual homed router
In addition
- Carry all alternatives in ROUTE_UPDATED and ROUTE_REMOVED event
- More unit tests
- More debug messages
- Fix routing table out-of-sync issue in MockRoutingService
- Fix populateRoute/revokeRoute mastership issue
Change-Id: I22d537625b570b09ecd4e22e6e14bb1ee27f8bcb
diff --git a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
index 3ad2d52..3e576b95 100644
--- a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
+++ b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
@@ -60,6 +60,7 @@
Maps.newConcurrentMap();
private static final Map<MockRoutingTableKey, MockRoutingTableValue> ROUTING_TABLE =
Maps.newConcurrentMap();
+ private static final Map<ConnectPoint, Set<IpPrefix>> SUBNET_TABLE = Maps.newConcurrentMap();
// Mocked Next Id
private static final Map<Integer, TrafficTreatment> NEXT_TABLE = Maps.newConcurrentMap();
@@ -191,6 +192,7 @@
srManager.deviceConfiguration = new DeviceConfiguration(srManager);
srManager.flowObjectiveService = new MockFlowObjectiveService(BRIDGING_TABLE, NEXT_TABLE);
srManager.routingRulePopulator = new MockRoutingRulePopulator(srManager, ROUTING_TABLE);
+ srManager.defaultRoutingHandler = new MockDefaultRoutingHandler(srManager, SUBNET_TABLE);
srManager.interfaceService = new MockInterfaceService(INTERFACES);
srManager.mastershipService = new MockMastershipService(LOCAL_DEVICES);
srManager.hostService = new MockHostService(HOSTS);
diff --git a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/MockDefaultRoutingHandler.java b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/MockDefaultRoutingHandler.java
index a69d5e7..ec07238 100644
--- a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/MockDefaultRoutingHandler.java
+++ b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/MockDefaultRoutingHandler.java
@@ -36,6 +36,15 @@
@Override
protected void populateSubnet(Set<ConnectPoint> cpts, Set<IpPrefix> subnets) {
+ subnetTable.forEach((k, v) -> {
+ if (!cpts.contains(k)) {
+ subnetTable.get(k).removeAll(subnets);
+ if (subnetTable.get(k).isEmpty()) {
+ subnetTable.remove(k);
+ }
+ }
+ });
+
cpts.forEach(cpt -> subnetTable.put(cpt, subnets));
}
diff --git a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/MockRouteService.java b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/MockRouteService.java
index 14e2166..7927636 100644
--- a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/MockRouteService.java
+++ b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/MockRouteService.java
@@ -16,29 +16,44 @@
package org.onosproject.segmentrouting;
-import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.routeservice.ResolvedRoute;
+import org.onosproject.routeservice.Route;
import org.onosproject.routeservice.RouteInfo;
import org.onosproject.routeservice.RouteServiceAdapter;
import org.onosproject.routeservice.RouteTableId;
import java.util.Collection;
-import java.util.Set;
+import java.util.Map;
+import java.util.stream.Collectors;
/**
* Mock Route Service.
* We assume there is only one routing table named "default".
*/
public class MockRouteService extends RouteServiceAdapter {
- private Set<RouteInfo> routes;
+ private Map<MockRoutingTableKey, MockRoutingTableValue> routingTable;
- MockRouteService(Set<RouteInfo> routes) {
- this.routes = ImmutableSet.copyOf(routes);
+ MockRouteService(Map<MockRoutingTableKey, MockRoutingTableValue> routingTable) {
+ this.routingTable = routingTable;
}
@Override
public Collection<RouteInfo> getRoutes(RouteTableId id) {
- return routes;
+ return routingTable.entrySet().stream().map(e -> {
+ IpPrefix prefix = e.getKey().ipPrefix;
+ IpAddress nextHop = IpAddress.valueOf(0); // dummy
+ MacAddress mac = e.getValue().macAddress;
+ VlanId vlan = e.getValue().vlanId;
+ Route route = new Route(Route.Source.STATIC, prefix, nextHop);
+ ResolvedRoute rr = new ResolvedRoute(route, mac, vlan);
+
+ return new RouteInfo(prefix, rr, Sets.newHashSet(rr));
+ }).collect(Collectors.toSet());
}
@Override
diff --git a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java
index 383f800..775e5ee 100644
--- a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java
+++ b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java
@@ -16,6 +16,8 @@
package org.onosproject.segmentrouting;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.junit.Before;
@@ -24,21 +26,25 @@
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
+import org.onosproject.net.config.ConfigApplyDelegate;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DefaultHost;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.HostId;
import org.onosproject.net.HostLocation;
+import org.onosproject.net.PortNumber;
import org.onosproject.net.config.NetworkConfigRegistryAdapter;
import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostService;
import org.onosproject.net.intf.Interface;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.routeservice.ResolvedRoute;
import org.onosproject.routeservice.Route;
import org.onosproject.routeservice.RouteEvent;
-import org.onosproject.routeservice.RouteInfo;
import org.onosproject.segmentrouting.config.DeviceConfiguration;
+import org.onosproject.segmentrouting.config.SegmentRoutingDeviceConfig;
import java.util.Map;
import java.util.Set;
@@ -50,6 +56,7 @@
*/
public class RouteHandlerTest {
private RouteHandler routeHandler;
+ private HostService hostService;
// Mocked routing and bridging tables
private static final Map<MockBridgingTableKey, MockBridgingTableValue> BRIDGING_TABLE =
@@ -61,6 +68,8 @@
private static final Map<Integer, TrafficTreatment> NEXT_TABLE = Maps.newConcurrentMap();
private static final IpPrefix P1 = IpPrefix.valueOf("10.0.0.0/24");
+
+ // Single homed router 1
private static final IpAddress N1 = IpAddress.valueOf("10.0.1.254");
private static final MacAddress M1 = MacAddress.valueOf("00:00:00:00:00:01");
private static final VlanId V1 = VlanId.vlanId((short) 1);
@@ -68,48 +77,61 @@
private static final Route R1 = new Route(Route.Source.STATIC, P1, N1);
private static final ResolvedRoute RR1 = new ResolvedRoute(R1, M1, V1);
+ // Single homed router 2
private static final IpAddress N2 = IpAddress.valueOf("10.0.2.254");
private static final MacAddress M2 = MacAddress.valueOf("00:00:00:00:00:02");
private static final VlanId V2 = VlanId.vlanId((short) 2);
- private static final ConnectPoint CP2 = ConnectPoint.deviceConnectPoint("of:0000000000000001/2");
+ private static final ConnectPoint CP2 = ConnectPoint.deviceConnectPoint("of:0000000000000002/2");
private static final Route R2 = new Route(Route.Source.STATIC, P1, N2);
private static final ResolvedRoute RR2 = new ResolvedRoute(R2, M2, V2);
- private static final RouteInfo RI1 = new RouteInfo(P1, RR1, Sets.newHashSet(RR1));
+ // Dual homed router 1
+ private static final IpAddress N3 = IpAddress.valueOf("10.0.3.254");
+ private static final MacAddress M3 = MacAddress.valueOf("00:00:00:00:00:03");
+ private static final VlanId V3 = VlanId.vlanId((short) 3);
+ private static final Route R3 = new Route(Route.Source.STATIC, P1, N3);
+ private static final ResolvedRoute RR3 = new ResolvedRoute(R3, M3, V3);
+ // Hosts
private static final Host H1 = new DefaultHost(ProviderId.NONE, HostId.hostId(M1, V1), M1, V1,
Sets.newHashSet(new HostLocation(CP1, 0)), Sets.newHashSet(N1), false);
private static final Host H2 = new DefaultHost(ProviderId.NONE, HostId.hostId(M2, V2), M2, V2,
Sets.newHashSet(new HostLocation(CP2, 0)), Sets.newHashSet(N2), false);
+ private static final Host H3D = new DefaultHost(ProviderId.NONE, HostId.hostId(M3, V3), M3, V3,
+ Sets.newHashSet(new HostLocation(CP1, 0), new HostLocation(CP2, 0)), Sets.newHashSet(N3), false);
+ private static final Host H3S = new DefaultHost(ProviderId.NONE, HostId.hostId(M3, V3), M3, V3,
+ Sets.newHashSet(new HostLocation(CP1, 0)), Sets.newHashSet(N3), false);
+
+ // Pair Local Port
+ private static final PortNumber P9 = PortNumber.portNumber(9);
// A set of hosts
- private static final Set<Host> HOSTS = Sets.newHashSet(H1, H2);
+ private static final Set<Host> HOSTS = Sets.newHashSet(H1, H2, H3D);
+ private static final Set<Host> HOSTS_ONE_FAIL = Sets.newHashSet(H1, H2, H3S);
+ private static final Set<Host> HOSTS_BOTH_FAIL = Sets.newHashSet(H1, H2);
// A set of devices of which we have mastership
- private static final Set<DeviceId> LOCAL_DEVICES = Sets.newHashSet();
+ private static final Set<DeviceId> LOCAL_DEVICES = Sets.newHashSet(CP1.deviceId(), CP2.deviceId());
// A set of interfaces
private static final Set<Interface> INTERFACES = Sets.newHashSet();
- // A set of routes
- private static final Set<RouteInfo> ROUTE_INFOS = Sets.newHashSet(RI1);
@Before
public void setUp() throws Exception {
-// TODO Initialize pairDevice and pairLocalPort config
-// ObjectMapper mapper = new ObjectMapper();
-// ConfigApplyDelegate delegate = config -> {};
-//
-// SegmentRoutingDeviceConfig dev3Config = new SegmentRoutingDeviceConfig();
-// JsonNode dev3Tree = mapper.createObjectNode();
-// dev3Config.init(DEV3, "host-handler-test", dev3Tree, mapper, delegate);
-// dev3Config.setPairDeviceId(DEV4).setPairLocalPort(P9);
-//
-// SegmentRoutingDeviceConfig dev4Config = new SegmentRoutingDeviceConfig();
-// JsonNode dev4Tree = mapper.createObjectNode();
-// dev4Config.init(DEV4, "host-handler-test", dev4Tree, mapper, delegate);
-// dev4Config.setPairDeviceId(DEV3).setPairLocalPort(P9);
+ ObjectMapper mapper = new ObjectMapper();
+ ConfigApplyDelegate delegate = config -> { };
+
+ SegmentRoutingDeviceConfig dev1Config = new SegmentRoutingDeviceConfig();
+ JsonNode dev1Tree = mapper.createObjectNode();
+ dev1Config.init(CP1.deviceId(), "host-handler-test", dev1Tree, mapper, delegate);
+ dev1Config.setPairDeviceId(CP2.deviceId()).setPairLocalPort(P9);
+
+ SegmentRoutingDeviceConfig dev2Config = new SegmentRoutingDeviceConfig();
+ JsonNode dev2Tree = mapper.createObjectNode();
+ dev2Config.init(CP2.deviceId(), "host-handler-test", dev2Tree, mapper, delegate);
+ dev2Config.setPairDeviceId(CP1.deviceId()).setPairLocalPort(P9);
MockNetworkConfigRegistry mockNetworkConfigRegistry = new MockNetworkConfigRegistry();
-// mockNetworkConfigRegistry.applyConfig(dev3Config);
-// mockNetworkConfigRegistry.applyConfig(dev4Config);
+ mockNetworkConfigRegistry.applyConfig(dev1Config);
+ mockNetworkConfigRegistry.applyConfig(dev2Config);
// Initialize Segment Routing Manager
SegmentRoutingManager srManager = new MockSegmentRoutingManager(NEXT_TABLE);
@@ -120,11 +142,18 @@
srManager.defaultRoutingHandler = new MockDefaultRoutingHandler(srManager, SUBNET_TABLE);
srManager.interfaceService = new MockInterfaceService(INTERFACES);
srManager.mastershipService = new MockMastershipService(LOCAL_DEVICES);
- srManager.hostService = new MockHostService(HOSTS);
+ hostService = new MockHostService(HOSTS);
+ srManager.hostService = hostService;
srManager.cfgService = mockNetworkConfigRegistry;
- srManager.routeService = new MockRouteService(ROUTE_INFOS);
+ srManager.routeService = new MockRouteService(ROUTING_TABLE);
- routeHandler = new RouteHandler(srManager);
+ routeHandler = new RouteHandler(srManager) {
+ // routeEventCache is not necessary for unit tests
+ @Override
+ void enqueueRouteEvent(RouteEvent routeEvent) {
+ dequeueRouteEvent(routeEvent);
+ }
+ };
ROUTING_TABLE.clear();
BRIDGING_TABLE.clear();
@@ -133,6 +162,10 @@
@Test
public void init() throws Exception {
+ MockRoutingTableKey rtk = new MockRoutingTableKey(CP1.deviceId(), P1);
+ MockRoutingTableValue rtv = new MockRoutingTableValue(CP1.port(), M1, V1);
+ ROUTING_TABLE.put(rtk, rtv);
+
routeHandler.init(CP1.deviceId());
assertEquals(1, ROUTING_TABLE.size());
@@ -147,7 +180,7 @@
@Test
public void processRouteAdded() throws Exception {
- RouteEvent re = new RouteEvent(RouteEvent.Type.ROUTE_ADDED, RR1);
+ RouteEvent re = new RouteEvent(RouteEvent.Type.ROUTE_ADDED, RR1, Sets.newHashSet(RR1));
routeHandler.processRouteAdded(re);
assertEquals(1, ROUTING_TABLE.size());
@@ -164,11 +197,12 @@
public void processRouteUpdated() throws Exception {
processRouteAdded();
- RouteEvent re = new RouteEvent(RouteEvent.Type.ROUTE_UPDATED, RR2, RR1);
+ RouteEvent re = new RouteEvent(RouteEvent.Type.ROUTE_UPDATED, RR2, RR1, Sets.newHashSet(RR2),
+ Sets.newHashSet(RR1));
routeHandler.processRouteUpdated(re);
assertEquals(1, ROUTING_TABLE.size());
- MockRoutingTableValue rtv2 = ROUTING_TABLE.get(new MockRoutingTableKey(CP1.deviceId(), P1));
+ MockRoutingTableValue rtv2 = ROUTING_TABLE.get(new MockRoutingTableKey(CP2.deviceId(), P1));
assertEquals(M2, rtv2.macAddress);
assertEquals(V2, rtv2.vlanId);
assertEquals(CP2.port(), rtv2.portNumber);
@@ -181,7 +215,148 @@
public void processRouteRemoved() throws Exception {
processRouteAdded();
- RouteEvent re = new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, RR1);
+ RouteEvent re = new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, RR1, Sets.newHashSet(RR1));
+ routeHandler.processRouteRemoved(re);
+
+ assertEquals(0, ROUTING_TABLE.size());
+ assertEquals(0, SUBNET_TABLE.size());
+ }
+
+ @Test
+ public void testTwoSingleHomedAdded() throws Exception {
+ RouteEvent re = new RouteEvent(RouteEvent.Type.ROUTE_ADDED, RR1, Sets.newHashSet(RR1, RR2));
+ 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(M1, rtv1.macAddress);
+ assertEquals(M2, rtv2.macAddress);
+ assertEquals(V1, rtv1.vlanId);
+ assertEquals(V2, 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));
+ }
+
+ @Test
+ public void testOneDualHomedAdded() throws Exception {
+ 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));
+ }
+
+ @Test
+ public void testOneSingleHomedToTwoSingleHomed() throws Exception {
+ processRouteAdded();
+
+ RouteEvent re = new RouteEvent(RouteEvent.Type.ALTERNATIVE_ROUTES_CHANGED, RR1, null,
+ Sets.newHashSet(RR1, RR2), Sets.newHashSet(RR1));
+ routeHandler.processAlternativeRoutesChanged(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(M1, rtv1.macAddress);
+ assertEquals(M2, rtv2.macAddress);
+ assertEquals(V1, rtv1.vlanId);
+ assertEquals(V2, 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));
+ }
+
+ @Test
+ public void testTwoSingleHomedToOneSingleHomed() throws Exception {
+ testTwoSingleHomedAdded();
+
+ RouteEvent re = new RouteEvent(RouteEvent.Type.ALTERNATIVE_ROUTES_CHANGED, RR1, null,
+ Sets.newHashSet(RR1), Sets.newHashSet(RR1, RR2));
+ routeHandler.processAlternativeRoutesChanged(re);
+
+ assertEquals(1, 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);
+
+ assertEquals(1, SUBNET_TABLE.size());
+ assertTrue(SUBNET_TABLE.get(CP1).contains(P1));
+ }
+
+ @Test
+ public void testDualHomedSingleLocationFail() throws Exception {
+ testOneDualHomedAdded();
+
+ HostEvent he = new HostEvent(HostEvent.Type.HOST_MOVED, H3S, H3D);
+ 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(P9, rtv2.portNumber);
+
+ // ECMP route table hasn't changed
+ assertEquals(2, SUBNET_TABLE.size());
+ assertTrue(SUBNET_TABLE.get(CP1).contains(P1));
+ assertTrue(SUBNET_TABLE.get(CP2).contains(P1));
+ }
+
+ @Test
+ public void testDualHomedBothLocationFail() throws Exception {
+ testDualHomedSingleLocationFail();
+
+ hostService = new MockHostService(HOSTS_ONE_FAIL);
+
+ 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());
+ }
+
+ @Test
+ public void testTwoSingleHomedRemoved() throws Exception {
+ testTwoSingleHomedAdded();
+
+ hostService = new MockHostService(HOSTS_BOTH_FAIL);
+
+ RouteEvent re = new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, RR1, Sets.newHashSet(RR1, RR2));
+ routeHandler.processRouteRemoved(re);
+
+ assertEquals(0, ROUTING_TABLE.size());
+ assertEquals(0, SUBNET_TABLE.size());
+ }
+
+ @Test
+ public void testOneDualHomeRemoved() throws Exception {
+ testOneDualHomedAdded();
+
+ RouteEvent re = new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, RR3, Sets.newHashSet(RR3));
routeHandler.processRouteRemoved(re);
assertEquals(0, ROUTING_TABLE.size());