Routing/bridging rules on the same leaf pair should always be programmed by the same ONOS instance
Main change:
- Implement new logic for shouldHandleRouting, along with corresponding unit tests. Also rename it to shouldProgram
Side changes:
- Refactor revokeSubnet such that it is only invoked on the instance that should handle routing change
- Move the following methods to RoutingRulePopulator and follow the same design pattern as populate/revoke subnet/route
- populateBridging, revokeBridging, updateBridging
- updateFwdObj
- Make sure the following methods in RoutingRulePopulator are always invoked by DefaultRoutingHandler with shouldProgram check
- populateRoute, revokeRoute
- populateSubnet, revokeSubnet
- populateBridging, revokeBridging, updateBridging
- updateFwdObj
Change-Id: I903129271ede91c45ebf0d973e06faeae46c157a
diff --git a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/DefaultRoutingHandlerTest.java b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/DefaultRoutingHandlerTest.java
new file mode 100644
index 0000000..05abcfc
--- /dev/null
+++ b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/DefaultRoutingHandlerTest.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.segmentrouting;
+
+import com.google.common.collect.Sets;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.DefaultControllerNode;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.segmentrouting.config.DeviceConfiguration;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.TestConsistentMap;
+
+import java.util.Optional;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reset;
+import static org.junit.Assert.*;
+
+public class DefaultRoutingHandlerTest {
+ private SegmentRoutingManager srManager;
+ private DefaultRoutingHandler dfh;
+
+ private static final DeviceId DEV1A = DeviceId.deviceId("of:1a");
+ private static final DeviceId DEV1B = DeviceId.deviceId("of:1b");
+ private static final DeviceId DEV2 = DeviceId.deviceId("of:2");
+
+ private static final NodeId NODE1 = NodeId.nodeId("192.168.1.1");
+ private static final NodeId NODE2 = NodeId.nodeId("192.168.1.2");
+ private static final NodeId NODE3 = NodeId.nodeId("192.168.1.3");
+ private static final IpAddress IP1 = IpAddress.valueOf("192.168.1.1");
+ private static final IpAddress IP2 = IpAddress.valueOf("192.168.1.2");
+ private static final IpAddress IP3 = IpAddress.valueOf("192.168.1.3");
+
+ @Before
+ public void setUp() {
+ srManager = createMock(SegmentRoutingManager.class);
+ srManager.storageService = createMock(StorageService.class);
+ expect(srManager.storageService.consistentMapBuilder()).andReturn(new TestConsistentMap.Builder<>()).anyTimes();
+ replay(srManager.storageService);
+ srManager.routingRulePopulator = createMock(RoutingRulePopulator.class);
+ srManager.deviceService = createMock(DeviceService.class);
+ srManager.deviceConfiguration = createMock(DeviceConfiguration.class);
+ srManager.mastershipService = createMock(MastershipService.class);
+ srManager.clusterService = createMock(ClusterService.class);
+ dfh = new DefaultRoutingHandler(srManager);
+ }
+
+ // Node 1 is the master of switch 1A, 1B, and 2
+ @Test
+ public void testShouldHandleRoutingCase1() {
+ expect(srManager.mastershipService.getMasterFor(DEV1A)).andReturn(NODE1).anyTimes();
+ expect(srManager.mastershipService.getMasterFor(DEV1B)).andReturn(NODE1).anyTimes();
+ expect(srManager.mastershipService.getMasterFor(DEV2)).andReturn(NODE1).anyTimes();
+ replay(srManager.mastershipService);
+
+ expect(srManager.getPairDeviceId(DEV1A)).andReturn(Optional.of(DEV1B)).anyTimes();
+ expect(srManager.getPairDeviceId(DEV1B)).andReturn(Optional.of(DEV1A)).anyTimes();
+ expect(srManager.getPairDeviceId(DEV2)).andReturn(Optional.empty()).anyTimes();
+ replay(srManager);
+
+ // Node 1 should program every device
+ expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE1, IP1)).anyTimes();
+ replay(srManager.clusterService);
+ assertTrue(dfh.shouldProgram(DEV1A));
+ assertTrue(dfh.shouldProgram(DEV1B));
+ assertTrue(dfh.shouldProgram(DEV2));
+
+ reset(srManager.clusterService);
+
+ // Node 2 should program no device
+ expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE2, IP2)).anyTimes();
+ replay(srManager.clusterService);
+ assertFalse(dfh.shouldProgram(DEV1A));
+ assertFalse(dfh.shouldProgram(DEV1B));
+ assertFalse(dfh.shouldProgram(DEV2));
+
+ reset(srManager.clusterService);
+
+ // Node 3 should program no device
+ expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE3, IP3)).anyTimes();
+ replay(srManager.clusterService);
+ assertFalse(dfh.shouldProgram(DEV1A));
+ assertFalse(dfh.shouldProgram(DEV1B));
+ assertFalse(dfh.shouldProgram(DEV2));
+ }
+
+ // Node 1 is the master of switch 1A, 1B
+ // Node 2 is the master of switch 2
+ @Test
+ public void testShouldHandleRoutingCase2() {
+ expect(srManager.mastershipService.getMasterFor(DEV1A)).andReturn(NODE1).anyTimes();
+ expect(srManager.mastershipService.getMasterFor(DEV1B)).andReturn(NODE1).anyTimes();
+ expect(srManager.mastershipService.getMasterFor(DEV2)).andReturn(NODE2).anyTimes();
+ replay(srManager.mastershipService);
+
+ expect(srManager.getPairDeviceId(DEV1A)).andReturn(Optional.of(DEV1B)).anyTimes();
+ expect(srManager.getPairDeviceId(DEV1B)).andReturn(Optional.of(DEV1A)).anyTimes();
+ expect(srManager.getPairDeviceId(DEV2)).andReturn(Optional.empty()).anyTimes();
+ replay(srManager);
+
+ // Node 1 should program 1A, 1B
+ expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE1, IP1)).anyTimes();
+ replay(srManager.clusterService);
+ assertTrue(dfh.shouldProgram(DEV1A));
+ assertTrue(dfh.shouldProgram(DEV1B));
+ assertFalse(dfh.shouldProgram(DEV2));
+
+ reset(srManager.clusterService);
+
+ // Node 2 should program 2
+ expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE2, IP2)).anyTimes();
+ replay(srManager.clusterService);
+ assertFalse(dfh.shouldProgram(DEV1A));
+ assertFalse(dfh.shouldProgram(DEV1B));
+ assertTrue(dfh.shouldProgram(DEV2));
+
+ reset(srManager.clusterService);
+
+ // Node 3 should program no device
+ expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE3, IP3)).anyTimes();
+ replay(srManager.clusterService);
+ assertFalse(dfh.shouldProgram(DEV1A));
+ assertFalse(dfh.shouldProgram(DEV1B));
+ assertFalse(dfh.shouldProgram(DEV2));
+ }
+
+ // Node 1 is the master of switch 1A
+ // Node 2 is the master of switch 1B
+ // Node 3 is the master of switch 2
+ @Test
+ public void testShouldHandleRoutingCase3() {
+ expect(srManager.mastershipService.getMasterFor(DEV1A)).andReturn(NODE1).anyTimes();
+ expect(srManager.mastershipService.getMasterFor(DEV1B)).andReturn(NODE2).anyTimes();
+ expect(srManager.mastershipService.getMasterFor(DEV2)).andReturn(NODE3).anyTimes();
+ replay(srManager.mastershipService);
+
+ expect(srManager.getPairDeviceId(DEV1A)).andReturn(Optional.of(DEV1B)).anyTimes();
+ expect(srManager.getPairDeviceId(DEV1B)).andReturn(Optional.of(DEV1A)).anyTimes();
+ expect(srManager.getPairDeviceId(DEV2)).andReturn(Optional.empty()).anyTimes();
+ replay(srManager);
+
+ // Node 1 should program 1A, 1B
+ expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE1, IP1)).anyTimes();
+ replay(srManager.clusterService);
+ assertTrue(dfh.shouldProgram(DEV1A));
+ assertTrue(dfh.shouldProgram(DEV1B));
+ assertFalse(dfh.shouldProgram(DEV2));
+
+ reset(srManager.clusterService);
+
+ // Node 2 should program no device
+ expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE2, IP2)).anyTimes();
+ replay(srManager.clusterService);
+ assertFalse(dfh.shouldProgram(DEV1A));
+ assertFalse(dfh.shouldProgram(DEV1B));
+ assertFalse(dfh.shouldProgram(DEV2));
+
+ reset(srManager.clusterService);
+
+ // Node 3 should program 2
+ expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE3, IP3)).anyTimes();
+ replay(srManager.clusterService);
+ assertFalse(dfh.shouldProgram(DEV1A));
+ assertFalse(dfh.shouldProgram(DEV1B));
+ assertTrue(dfh.shouldProgram(DEV2));
+ }
+
+ // Node 3 is the master of switch 1A, 1B, 2
+ // Later on, node 1 becomes the master of 1A; Node 2 becomes the master of 1B.
+ @Test
+ public void testShouldHandleRoutingCase4() {
+ expect(srManager.mastershipService.getMasterFor(DEV1A)).andReturn(NODE3).anyTimes();
+ expect(srManager.mastershipService.getMasterFor(DEV1B)).andReturn(NODE3).anyTimes();
+ expect(srManager.mastershipService.getMasterFor(DEV2)).andReturn(NODE3).anyTimes();
+ replay(srManager.mastershipService);
+
+ expect(srManager.getPairDeviceId(DEV1A)).andReturn(Optional.of(DEV1B)).anyTimes();
+ expect(srManager.getPairDeviceId(DEV1B)).andReturn(Optional.of(DEV1A)).anyTimes();
+ expect(srManager.getPairDeviceId(DEV2)).andReturn(Optional.empty()).anyTimes();
+ replay(srManager);
+
+ // Node 1 should program no device
+ expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE1, IP1)).anyTimes();
+ replay(srManager.clusterService);
+ assertFalse(dfh.shouldProgram(DEV1A));
+ assertFalse(dfh.shouldProgram(DEV1B));
+ assertFalse(dfh.shouldProgram(DEV2));
+
+ reset(srManager.clusterService);
+
+ // Node 2 should program no device
+ expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE2, IP2)).anyTimes();
+ replay(srManager.clusterService);
+ assertFalse(dfh.shouldProgram(DEV1A));
+ assertFalse(dfh.shouldProgram(DEV1B));
+ assertFalse(dfh.shouldProgram(DEV2));
+
+ reset(srManager.clusterService);
+
+ // Node 3 should program 1A, 1B and 2
+ expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE3, IP3)).anyTimes();
+ replay(srManager.clusterService);
+ assertTrue(dfh.shouldProgram(DEV1A));
+ assertTrue(dfh.shouldProgram(DEV1B));
+ assertTrue(dfh.shouldProgram(DEV2));
+
+ // Mastership of switch 1A moves to Node 1
+ reset(srManager.mastershipService);
+ expect(srManager.mastershipService.getMasterFor(DEV1A)).andReturn(NODE1).anyTimes();
+ expect(srManager.mastershipService.getMasterFor(DEV1B)).andReturn(NODE2).anyTimes();
+ expect(srManager.mastershipService.getMasterFor(DEV2)).andReturn(NODE3).anyTimes();
+ replay(srManager.mastershipService);
+
+ reset(srManager.clusterService);
+
+ // Node 1 should program 1A, 1B
+ expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE1, IP1)).anyTimes();
+ replay(srManager.clusterService);
+ assertTrue(dfh.shouldProgram(DEV1A));
+ assertTrue(dfh.shouldProgram(DEV1B));
+ assertFalse(dfh.shouldProgram(DEV2));
+
+ reset(srManager.clusterService);
+
+ // Node 2 should program no device
+ expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE2, IP2)).anyTimes();
+ replay(srManager.clusterService);
+ assertFalse(dfh.shouldProgram(DEV1A));
+ assertFalse(dfh.shouldProgram(DEV1B));
+ assertFalse(dfh.shouldProgram(DEV2));
+
+ reset(srManager.clusterService);
+
+ // Node 3 should program 2
+ expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE3, IP3)).anyTimes();
+ replay(srManager.clusterService);
+ assertFalse(dfh.shouldProgram(DEV1A));
+ assertFalse(dfh.shouldProgram(DEV1B));
+ assertTrue(dfh.shouldProgram(DEV2));
+ }
+
+ // Node 1 is the master of 1A. 1B has no master
+ // Node 2 becomes the master of 1B later
+ @Test
+ public void testShouldHandleRoutingCase5() {
+ expect(srManager.mastershipService.getMasterFor(DEV1A)).andReturn(NODE1).anyTimes();
+ expect(srManager.mastershipService.getMasterFor(DEV1B)).andReturn(null).anyTimes();
+ replay(srManager.mastershipService);
+
+ expect(srManager.getPairDeviceId(DEV1A)).andReturn(Optional.of(DEV1B)).anyTimes();
+ expect(srManager.getPairDeviceId(DEV1B)).andReturn(Optional.of(DEV1A)).anyTimes();
+ replay(srManager);
+
+ // Node 1 should program both 1A and 1B
+ expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE1, IP1)).anyTimes();
+ replay(srManager.clusterService);
+ assertTrue(dfh.shouldProgram(DEV1A));
+ assertTrue(dfh.shouldProgram(DEV1B));
+
+ reset(srManager.clusterService);
+
+ // Node 2 should program no device
+ expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE2, IP2)).anyTimes();
+ replay(srManager.clusterService);
+ assertFalse(dfh.shouldProgram(DEV1A));
+ assertFalse(dfh.shouldProgram(DEV1B));
+
+ // Mastership of switch 1B moves to Node 2
+ reset(srManager.mastershipService);
+ expect(srManager.mastershipService.getMasterFor(DEV1A)).andReturn(NODE1).anyTimes();
+ expect(srManager.mastershipService.getMasterFor(DEV1B)).andReturn(NODE2).anyTimes();
+ replay(srManager.mastershipService);
+
+ reset(srManager.clusterService);
+
+ // Node 1 should program 1A, 1B
+ expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE1, IP1)).anyTimes();
+ replay(srManager.clusterService);
+ assertTrue(dfh.shouldProgram(DEV1A));
+ assertTrue(dfh.shouldProgram(DEV1B));
+
+ reset(srManager.clusterService);
+
+ // Node 2 should program no device
+ expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE2, IP2)).anyTimes();
+ replay(srManager.clusterService);
+ assertFalse(dfh.shouldProgram(DEV1A));
+ assertFalse(dfh.shouldProgram(DEV1B));
+ }
+
+ // Neither 1A or 1B has master
+ @Test
+ public void testShouldHandleRoutingCase6() {
+ expect(srManager.mastershipService.getMasterFor(DEV1A)).andReturn(null).anyTimes();
+ expect(srManager.mastershipService.getMasterFor(DEV1B)).andReturn(null).anyTimes();
+ replay(srManager.mastershipService);
+
+ expect(srManager.getPairDeviceId(DEV1A)).andReturn(Optional.of(DEV1B)).anyTimes();
+ expect(srManager.getPairDeviceId(DEV1B)).andReturn(Optional.of(DEV1A)).anyTimes();
+ replay(srManager);
+
+ // Node 1 should program no device
+ expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE1, IP1)).anyTimes();
+ replay(srManager.clusterService);
+ assertFalse(dfh.shouldProgram(DEV1A));
+ assertFalse(dfh.shouldProgram(DEV1B));
+
+ reset(srManager.clusterService);
+
+ // Node 2 should program no device
+ expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE2, IP2)).anyTimes();
+ replay(srManager.clusterService);
+ assertFalse(dfh.shouldProgram(DEV1A));
+ assertFalse(dfh.shouldProgram(DEV1B));
+
+ assertFalse(dfh.shouldProgram.containsKey(Sets.newHashSet(DEV1A, DEV1B)));
+ }
+}
\ No newline at end of file
diff --git a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
index b6ec070..3605727 100644
--- a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
+++ b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
@@ -44,10 +44,15 @@
import org.onosproject.net.provider.ProviderId;
import org.onosproject.segmentrouting.config.DeviceConfiguration;
import org.onosproject.segmentrouting.config.SegmentRoutingDeviceConfig;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.TestConsistentMap;
import java.util.Map;
import java.util.Set;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
import static org.junit.Assert.*;
/**r
@@ -201,6 +206,9 @@
// Initialize Segment Routing Manager
SegmentRoutingManager srManager = new MockSegmentRoutingManager(NEXT_TABLE);
+ srManager.storageService = createMock(StorageService.class);
+ expect(srManager.storageService.consistentMapBuilder()).andReturn(new TestConsistentMap.Builder<>()).anyTimes();
+ replay(srManager.storageService);
srManager.cfgService = new NetworkConfigRegistryAdapter();
srManager.deviceConfiguration = new DeviceConfiguration(srManager);
srManager.flowObjectiveService = new MockFlowObjectiveService(BRIDGING_TABLE, NEXT_TABLE);
@@ -846,10 +854,4 @@
assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)));
}
-
- @Test
- public void testBridgingFwdObjBuilder() throws Exception {
- assertNotNull(hostHandler.bridgingFwdObjBuilder(DEV2, HOST_MAC, HOST_VLAN_UNTAGGED, P1, false));
- assertNull(hostHandler.bridgingFwdObjBuilder(DEV2, HOST_MAC, HOST_VLAN_UNTAGGED, P3, false));
- }
}
diff --git a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/MockDefaultRoutingHandler.java b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/MockDefaultRoutingHandler.java
index d31be2f..0002948 100644
--- a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/MockDefaultRoutingHandler.java
+++ b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/MockDefaultRoutingHandler.java
@@ -18,6 +18,7 @@
import org.onlab.packet.IpPrefix;
import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
import java.util.Map;
import java.util.Set;
@@ -62,4 +63,9 @@
routingTable.entrySet().removeIf(e -> subnets.contains(e.getKey().ipPrefix));
return true;
}
+
+ @Override
+ protected boolean shouldProgram(DeviceId deviceId) {
+ return true;
+ }
}
\ No newline at end of file
diff --git a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java
index 36a9e79..a7388d1 100644
--- a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java
+++ b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java
@@ -45,10 +45,13 @@
import org.onosproject.routeservice.RouteEvent;
import org.onosproject.segmentrouting.config.DeviceConfiguration;
import org.onosproject.segmentrouting.config.SegmentRoutingDeviceConfig;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.TestConsistentMap;
import java.util.Map;
import java.util.Set;
+import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.reset;
import static org.junit.Assert.*;
import static org.easymock.EasyMock.createMock;
@@ -141,6 +144,9 @@
// Initialize Segment Routing Manager
srManager = new MockSegmentRoutingManager(NEXT_TABLE);
+ srManager.storageService = createMock(StorageService.class);
+ expect(srManager.storageService.consistentMapBuilder()).andReturn(new TestConsistentMap.Builder<>()).anyTimes();
+ replay(srManager.storageService);
srManager.cfgService = new NetworkConfigRegistryAdapter();
srManager.deviceConfiguration = createMock(DeviceConfiguration.class);
srManager.flowObjectiveService = new MockFlowObjectiveService(BRIDGING_TABLE, NEXT_TABLE);