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/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java b/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java
index 383f800..775e5ee 100644
--- a/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java
+++ b/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());