Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/PeerConnectivity.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/PeerConnectivityManager.java
similarity index 98%
rename from apps/sdnip/src/main/java/org/onlab/onos/sdnip/PeerConnectivity.java
rename to apps/sdnip/src/main/java/org/onlab/onos/sdnip/PeerConnectivityManager.java
index 31cd971..3917f5a 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/PeerConnectivity.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/PeerConnectivityManager.java
@@ -25,10 +25,10 @@
/**
* Manages the connectivity requirements between peers.
*/
-public class PeerConnectivity {
+public class PeerConnectivityManager {
private static final Logger log = LoggerFactory.getLogger(
- PeerConnectivity.class);
+ PeerConnectivityManager.class);
// TODO these shouldn't be defined here
private static final short BGP_PORT = 179;
@@ -41,7 +41,7 @@
// TODO this sucks.
private int intentId = 0;
- public PeerConnectivity(SdnIpConfigService configInfoService,
+ public PeerConnectivityManager(SdnIpConfigService configInfoService,
InterfaceService interfaceService, IntentService intentService) {
this.configInfoService = configInfoService;
this.interfaceService = interfaceService;
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/SdnIp.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/SdnIp.java
index e7ed601..3cc7708 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/SdnIp.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/SdnIp.java
@@ -36,7 +36,7 @@
protected HostService hostService;
private SdnIpConfigReader config;
- private PeerConnectivity peerConnectivity;
+ private PeerConnectivityManager peerConnectivity;
private Router router;
private BgpSessionManager bgpSessionManager;
@@ -49,7 +49,7 @@
InterfaceService interfaceService = new HostServiceBasedInterfaceService(hostService);
- peerConnectivity = new PeerConnectivity(config, interfaceService, intentService);
+ peerConnectivity = new PeerConnectivityManager(config, interfaceService, intentService);
peerConnectivity.start();
router = new Router(intentService, hostService, config, interfaceService);
@@ -79,4 +79,8 @@
public Collection<RouteEntry> getRoutes() {
return router.getRoutes();
}
+
+ static String dpidToUri(String dpid) {
+ return "of:" + dpid.replace(":", "");
+ }
}
diff --git a/apps/sdnip/src/test/java/org/onlab/onos/sdnip/PeerConnectivityManagerTest.java b/apps/sdnip/src/test/java/org/onlab/onos/sdnip/PeerConnectivityManagerTest.java
new file mode 100644
index 0000000..0ecea00
--- /dev/null
+++ b/apps/sdnip/src/test/java/org/onlab/onos/sdnip/PeerConnectivityManagerTest.java
@@ -0,0 +1,711 @@
+package org.onlab.onos.sdnip;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reportMatcher;
+import static org.easymock.EasyMock.reset;
+import static org.easymock.EasyMock.verify;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.easymock.IArgumentMatcher;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.flow.DefaultTrafficSelector;
+import org.onlab.onos.net.flow.DefaultTrafficTreatment;
+import org.onlab.onos.net.flow.TrafficSelector;
+import org.onlab.onos.net.flow.TrafficTreatment;
+import org.onlab.onos.net.intent.IntentId;
+import org.onlab.onos.net.intent.IntentService;
+import org.onlab.onos.net.intent.PointToPointIntent;
+import org.onlab.onos.sdnip.bgp.BgpConstants;
+import org.onlab.onos.sdnip.config.BgpPeer;
+import org.onlab.onos.sdnip.config.BgpSpeaker;
+import org.onlab.onos.sdnip.config.Interface;
+import org.onlab.onos.sdnip.config.InterfaceAddress;
+import org.onlab.onos.sdnip.config.SdnIpConfigService;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Unit tests for PeerConnectivityManager interface.
+ */
+public class PeerConnectivityManagerTest {
+
+ private PeerConnectivityManager peerConnectivityManager;
+ private IntentService intentService;
+ private SdnIpConfigService configInfoService;
+ private InterfaceService interfaceService;
+
+ private Map<String, BgpSpeaker> bgpSpeakers;
+ private Map<String, Interface> interfaces;
+ private Map<IpAddress, BgpPeer> peers;
+
+ private Map<String, BgpSpeaker> configuredBgpSpeakers;
+ private Map<String, Interface> configuredInterfaces;
+ private Map<IpAddress, BgpPeer> configuredPeers;
+ private List<PointToPointIntent> intentList;
+
+ private final String dpid1 = "00:00:00:00:00:00:00:01";
+ private final String dpid2 = "00:00:00:00:00:00:00:02";
+
+ private final DeviceId deviceId1 =
+ DeviceId.deviceId(SdnIp.dpidToUri(dpid1));
+ private final DeviceId deviceId2 =
+ DeviceId.deviceId(SdnIp.dpidToUri(dpid2));
+
+ // Interfaces connected to BGP speakers
+ private final ConnectPoint s1Eth100 =
+ new ConnectPoint(deviceId1, PortNumber.portNumber(100));
+ private final ConnectPoint s2Eth100 =
+ new ConnectPoint(deviceId2, PortNumber.portNumber(100));
+
+ // Interfaces connected to BGP peers
+ private final ConnectPoint s1Eth1 =
+ new ConnectPoint(deviceId1, PortNumber.portNumber(1));
+ private final ConnectPoint s2Eth1 =
+ new ConnectPoint(deviceId2, PortNumber.portNumber(1));
+
+ // We don't compare the intent ID so all expected intents can use the same ID
+ private final IntentId testIntentId = new IntentId(0);
+
+ private final TrafficTreatment noTreatment =
+ DefaultTrafficTreatment.builder().build();
+
+ @Before
+ public void setUp() throws Exception {
+ bgpSpeakers = Collections.unmodifiableMap(setUpBgpSpeakers());
+ interfaces = Collections.unmodifiableMap(setUpInterfaces());
+ peers = Collections.unmodifiableMap(setUpPeers());
+
+ initPeerConnectivity();
+ intentList = setUpIntentList();
+ }
+
+ /**
+ * Sets up BGP speakers.
+ *
+ * @return configured BGP speakers as a map from speaker name to speaker
+ */
+ private Map<String, BgpSpeaker> setUpBgpSpeakers() {
+
+ configuredBgpSpeakers = new HashMap<>();
+
+ BgpSpeaker bgpSpeaker1 = new BgpSpeaker(
+ "bgpSpeaker1",
+ "00:00:00:00:00:00:00:01", 100,
+ "00:00:00:00:00:01");
+ List<InterfaceAddress> interfaceAddresses1 =
+ new LinkedList<InterfaceAddress>();
+ interfaceAddresses1.add(new InterfaceAddress(dpid1, 1, "192.168.10.101"));
+ interfaceAddresses1.add(new InterfaceAddress(dpid2, 1, "192.168.20.101"));
+ bgpSpeaker1.setInterfaceAddresses(interfaceAddresses1);
+ configuredBgpSpeakers.put(bgpSpeaker1.name(), bgpSpeaker1);
+
+ // BGP speaker2 is attached to the same switch port with speaker1
+ BgpSpeaker bgpSpeaker2 = new BgpSpeaker(
+ "bgpSpeaker2",
+ "00:00:00:00:00:00:00:01", 100,
+ "00:00:00:00:00:02");
+ List<InterfaceAddress> interfaceAddresses2 =
+ new LinkedList<InterfaceAddress>();
+ interfaceAddresses2.add(new InterfaceAddress(dpid1, 1, "192.168.10.102"));
+ interfaceAddresses2.add(new InterfaceAddress(dpid2, 1, "192.168.20.102"));
+ bgpSpeaker2.setInterfaceAddresses(interfaceAddresses2);
+ configuredBgpSpeakers.put(bgpSpeaker2.name(), bgpSpeaker2);
+
+ BgpSpeaker bgpSpeaker3 = new BgpSpeaker(
+ "bgpSpeaker3",
+ "00:00:00:00:00:00:00:02", 100,
+ "00:00:00:00:00:03");
+ List<InterfaceAddress> interfaceAddresses3 =
+ new LinkedList<InterfaceAddress>();
+ interfaceAddresses3.add(new InterfaceAddress(dpid1, 1, "192.168.10.103"));
+ interfaceAddresses3.add(new InterfaceAddress(dpid2, 1, "192.168.20.103"));
+ bgpSpeaker3.setInterfaceAddresses(interfaceAddresses3);
+ configuredBgpSpeakers.put(bgpSpeaker3.name(), bgpSpeaker3);
+
+ return configuredBgpSpeakers;
+ }
+
+ /**
+ * Sets up logical interfaces, which emulate the configured interfaces
+ * in SDN-IP application.
+ *
+ * @return configured interfaces as a MAP from Interface name to Interface
+ */
+ private Map<String, Interface> setUpInterfaces() {
+
+ configuredInterfaces = new HashMap<>();
+
+ String interfaceSw1Eth1 = "s1-eth1";
+ Interface intfsw1eth1 = new Interface(s1Eth1,
+ Collections.singleton(IpPrefix.valueOf("192.168.10.0/24")),
+ MacAddress.valueOf("00:00:00:00:00:01"));
+
+ configuredInterfaces.put(interfaceSw1Eth1, intfsw1eth1);
+ String interfaceSw2Eth1 = "s2-eth1";
+ Interface intfsw2eth1 = new Interface(s2Eth1,
+ Collections.singleton(IpPrefix.valueOf("192.168.20.0/24")),
+ MacAddress.valueOf("00:00:00:00:00:02"));
+ configuredInterfaces.put(interfaceSw2Eth1, intfsw2eth1);
+
+ interfaceService = createMock(InterfaceService.class);
+
+ expect(interfaceService.getInterface(s1Eth1))
+ .andReturn(intfsw1eth1).anyTimes();
+ expect(interfaceService.getInterface(s2Eth1))
+ .andReturn(intfsw2eth1).anyTimes();
+
+ // Non-existent interface used during one of the tests
+ expect(interfaceService.getInterface(new ConnectPoint(
+ DeviceId.deviceId(SdnIp.dpidToUri("00:00:00:00:00:00:01:00")),
+ PortNumber.portNumber(1))))
+ .andReturn(null).anyTimes();
+
+ expect(interfaceService.getInterfaces()).andReturn(
+ Sets.newHashSet(configuredInterfaces.values())).anyTimes();
+ replay(interfaceService);
+
+ return configuredInterfaces;
+ }
+
+ /**
+ * Sets up BGP daemon peers.
+ *
+ * @return configured BGP peers as a MAP from peer IP address to BgpPeer
+ */
+ private Map<IpAddress, BgpPeer> setUpPeers() {
+
+ configuredPeers = new HashMap<>();
+
+ String peerSw1Eth1 = "192.168.10.1";
+ configuredPeers.put(IpAddress.valueOf(peerSw1Eth1),
+ new BgpPeer(dpid1, 1, peerSw1Eth1));
+
+ // Two BGP peers are connected to switch 2 port 1.
+ String peer1Sw2Eth1 = "192.168.20.1";
+ configuredPeers.put(IpAddress.valueOf(peer1Sw2Eth1),
+ new BgpPeer(dpid2, 1, peer1Sw2Eth1));
+
+ String peer2Sw2Eth1 = "192.168.20.2";
+ configuredPeers.put(IpAddress.valueOf(peer2Sw2Eth1),
+ new BgpPeer(dpid2, 1, peer2Sw2Eth1));
+
+ return configuredPeers;
+ }
+
+ /**
+ * Sets up expected point to point intent list.
+ *
+ * @return point to point intent list
+ */
+ private List<PointToPointIntent> setUpIntentList() {
+
+ intentList = new ArrayList<PointToPointIntent>();
+
+ setUpBgpIntents();
+ setUpIcmpIntents();
+
+ return intentList;
+
+ }
+
+ /**
+ * Constructs a BGP intent and put it into the intentList.
+ * <p/>
+ * The purpose of this method is too simplify the setUpBgpIntents() method,
+ * and to make the setUpBgpIntents() easy to read.
+ *
+ * @param srcPrefix source IP prefix to match
+ * @param dstPrefix destination IP prefix to match
+ * @param srcTcpPort source TCP port to match
+ * @param dstTcpPort destination TCP port to match
+ * @param srcConnectPoint source connect point for PointToPointIntent
+ * @param dstConnectPoint destination connect point for PointToPointIntent
+ */
+ private void bgpPathintentConstructor(String srcPrefix, String dstPrefix,
+ Short srcTcpPort, Short dstTcpPort,
+ ConnectPoint srcConnectPoint, ConnectPoint dstConnectPoint) {
+
+ TrafficSelector.Builder builder = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPProtocol(IPv4.PROTOCOL_TCP)
+ .matchIPSrc(IpPrefix.valueOf(srcPrefix))
+ .matchIPDst(IpPrefix.valueOf(dstPrefix));
+
+ if (srcTcpPort != null) {
+ builder.matchTcpSrc(srcTcpPort);
+ }
+ if (dstTcpPort != null) {
+ builder.matchTcpDst(dstTcpPort);
+ }
+
+ PointToPointIntent intent = new PointToPointIntent(
+ testIntentId, builder.build(), noTreatment,
+ srcConnectPoint, dstConnectPoint);
+
+ intentList.add(intent);
+ }
+
+ /**
+ * Sets up intents for BGP paths.
+ */
+ private void setUpBgpIntents() {
+
+ Short bgpPort = Short.valueOf((short) BgpConstants.BGP_PORT);
+
+ // Start to build intents between BGP speaker1 and BGP peer1
+ bgpPathintentConstructor(
+ "192.168.10.101/32", "192.168.10.1/32", null, bgpPort,
+ s1Eth100, s1Eth1);
+ bgpPathintentConstructor(
+ "192.168.10.101/32", "192.168.10.1/32", bgpPort, null,
+ s1Eth100, s1Eth1);
+
+ bgpPathintentConstructor(
+ "192.168.10.1/32", "192.168.10.101/32", null, bgpPort,
+ s1Eth1, s1Eth100);
+ bgpPathintentConstructor(
+ "192.168.10.1/32", "192.168.10.101/32", bgpPort, null,
+ s1Eth1, s1Eth100);
+
+ // Start to build intents between BGP speaker1 and BGP peer2
+ bgpPathintentConstructor(
+ "192.168.20.101/32", "192.168.20.1/32", null, bgpPort,
+ s1Eth100, s2Eth1);
+ bgpPathintentConstructor(
+ "192.168.20.101/32", "192.168.20.1/32", bgpPort, null,
+ s1Eth100, s2Eth1);
+
+ bgpPathintentConstructor(
+ "192.168.20.1/32", "192.168.20.101/32", null, bgpPort,
+ s2Eth1, s1Eth100);
+ bgpPathintentConstructor(
+ "192.168.20.1/32", "192.168.20.101/32", bgpPort, null,
+ s2Eth1, s1Eth100);
+
+ // Start to build intents between BGP speaker1 and BGP peer3
+ bgpPathintentConstructor(
+ "192.168.20.101/32", "192.168.20.2/32", null, bgpPort,
+ s1Eth100, s2Eth1);
+ bgpPathintentConstructor(
+ "192.168.20.101/32", "192.168.20.2/32", bgpPort, null,
+ s1Eth100, s2Eth1);
+
+ bgpPathintentConstructor(
+ "192.168.20.2/32", "192.168.20.101/32", null, bgpPort,
+ s2Eth1, s1Eth100);
+ bgpPathintentConstructor(
+ "192.168.20.2/32", "192.168.20.101/32", bgpPort, null,
+ s2Eth1, s1Eth100);
+
+ //
+ // Start to build intents between BGP speaker2 and BGP peer1
+ bgpPathintentConstructor(
+ "192.168.10.102/32", "192.168.10.1/32", null, bgpPort,
+ s1Eth100, s1Eth1);
+ bgpPathintentConstructor(
+ "192.168.10.102/32", "192.168.10.1/32", bgpPort, null,
+ s1Eth100, s1Eth1);
+
+ bgpPathintentConstructor(
+ "192.168.10.1/32", "192.168.10.102/32", null, bgpPort,
+ s1Eth1, s1Eth100);
+ bgpPathintentConstructor(
+ "192.168.10.1/32", "192.168.10.102/32", bgpPort, null,
+ s1Eth1, s1Eth100);
+ // Start to build intents between BGP speaker2 and BGP peer2
+ bgpPathintentConstructor(
+ "192.168.20.102/32", "192.168.20.1/32", null, bgpPort,
+ s1Eth100, s2Eth1);
+ bgpPathintentConstructor(
+ "192.168.20.102/32", "192.168.20.1/32", bgpPort, null,
+ s1Eth100, s2Eth1);
+
+ bgpPathintentConstructor(
+ "192.168.20.1/32", "192.168.20.102/32", null, bgpPort,
+ s2Eth1, s1Eth100);
+ bgpPathintentConstructor(
+ "192.168.20.1/32", "192.168.20.102/32", bgpPort, null,
+ s2Eth1, s1Eth100);
+
+ // Start to build intents between BGP speaker2 and BGP peer3
+ bgpPathintentConstructor(
+ "192.168.20.102/32", "192.168.20.2/32", null, bgpPort,
+ s1Eth100, s2Eth1);
+ bgpPathintentConstructor(
+ "192.168.20.102/32", "192.168.20.2/32", bgpPort, null,
+ s1Eth100, s2Eth1);
+
+ bgpPathintentConstructor(
+ "192.168.20.2/32", "192.168.20.102/32", null, bgpPort,
+ s2Eth1, s1Eth100);
+ bgpPathintentConstructor(
+ "192.168.20.2/32", "192.168.20.102/32", bgpPort, null,
+ s2Eth1, s1Eth100);
+
+ //
+ // Start to build intents between BGP speaker3 and BGP peer1
+ bgpPathintentConstructor(
+ "192.168.10.103/32", "192.168.10.1/32", null, bgpPort,
+ s2Eth100, s1Eth1);
+ bgpPathintentConstructor(
+ "192.168.10.103/32", "192.168.10.1/32", bgpPort, null,
+ s2Eth100, s1Eth1);
+
+ bgpPathintentConstructor(
+ "192.168.10.1/32", "192.168.10.103/32", null, bgpPort,
+ s1Eth1, s2Eth100);
+ bgpPathintentConstructor(
+ "192.168.10.1/32", "192.168.10.103/32", bgpPort, null,
+ s1Eth1, s2Eth100);
+
+ // Start to build intents between BGP speaker3 and BGP peer2
+ bgpPathintentConstructor(
+ "192.168.20.103/32", "192.168.20.1/32", null, bgpPort,
+ s2Eth100, s2Eth1);
+ bgpPathintentConstructor(
+ "192.168.20.103/32", "192.168.20.1/32", bgpPort, null,
+ s2Eth100, s2Eth1);
+
+ bgpPathintentConstructor(
+ "192.168.20.1/32", "192.168.20.103/32", null, bgpPort,
+ s2Eth1, s2Eth100);
+ bgpPathintentConstructor(
+ "192.168.20.1/32", "192.168.20.103/32", bgpPort, null,
+ s2Eth1, s2Eth100);
+
+ // Start to build intents between BGP speaker3 and BGP peer3
+ bgpPathintentConstructor(
+ "192.168.20.103/32", "192.168.20.2/32", null, bgpPort,
+ s2Eth100, s2Eth1);
+ bgpPathintentConstructor(
+ "192.168.20.103/32", "192.168.20.2/32", bgpPort, null,
+ s2Eth100, s2Eth1);
+
+ bgpPathintentConstructor(
+ "192.168.20.2/32", "192.168.20.103/32", null, bgpPort,
+ s2Eth1, s2Eth100);
+ bgpPathintentConstructor(
+ "192.168.20.2/32", "192.168.20.103/32", bgpPort, null,
+ s2Eth1, s2Eth100);
+ }
+
+ /**
+ * Constructs a BGP intent and put it into the intentList.
+ * <p/>
+ * The purpose of this method is too simplify the setUpBgpIntents() method,
+ * and to make the setUpBgpIntents() easy to read.
+ *
+ * @param srcPrefix source IP prefix to match
+ * @param dstPrefix destination IP prefix to match
+ * @param srcConnectPoint source connect point for PointToPointIntent
+ * @param dstConnectPoint destination connect point for PointToPointIntent
+ */
+ private void icmpPathintentConstructor(String srcPrefix, String dstPrefix,
+ ConnectPoint srcConnectPoint, ConnectPoint dstConnectPoint) {
+
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPProtocol(IPv4.PROTOCOL_ICMP)
+ .matchIPSrc(IpPrefix.valueOf(srcPrefix))
+ .matchIPDst(IpPrefix.valueOf(dstPrefix))
+ .build();
+
+ PointToPointIntent intent = new PointToPointIntent(
+ testIntentId, selector, noTreatment,
+ srcConnectPoint, dstConnectPoint);
+
+ intentList.add(intent);
+ }
+
+ /**
+ * Sets up intents for ICMP paths.
+ */
+ private void setUpIcmpIntents() {
+
+ // Start to build intents between BGP speaker1 and BGP peer1
+ icmpPathintentConstructor(
+ "192.168.10.101/32", "192.168.10.1/32", s1Eth100, s1Eth1);
+ icmpPathintentConstructor(
+ "192.168.10.1/32", "192.168.10.101/32", s1Eth1, s1Eth100);
+
+ // Start to build intents between BGP speaker1 and BGP peer2
+ icmpPathintentConstructor(
+ "192.168.20.101/32", "192.168.20.1/32", s1Eth100, s2Eth1);
+ icmpPathintentConstructor(
+ "192.168.20.1/32", "192.168.20.101/32", s2Eth1, s1Eth100);
+
+ // Start to build intents between BGP speaker1 and BGP peer3
+ icmpPathintentConstructor(
+ "192.168.20.101/32", "192.168.20.2/32", s1Eth100, s2Eth1);
+ icmpPathintentConstructor(
+ "192.168.20.2/32", "192.168.20.101/32", s2Eth1, s1Eth100);
+
+ //
+ // Start to build intents between BGP speaker2 and BGP peer1
+ icmpPathintentConstructor(
+ "192.168.10.102/32", "192.168.10.1/32", s1Eth100, s1Eth1);
+ icmpPathintentConstructor(
+ "192.168.10.1/32", "192.168.10.102/32", s1Eth1, s1Eth100);
+
+ // Start to build intents between BGP speaker2 and BGP peer2
+ icmpPathintentConstructor(
+ "192.168.20.102/32", "192.168.20.1/32", s1Eth100, s2Eth1);
+ icmpPathintentConstructor(
+ "192.168.20.1/32", "192.168.20.102/32", s2Eth1, s1Eth100);
+
+ // Start to build intents between BGP speaker2 and BGP peer3
+ icmpPathintentConstructor(
+ "192.168.20.102/32", "192.168.20.2/32", s1Eth100, s2Eth1);
+ icmpPathintentConstructor(
+ "192.168.20.2/32", "192.168.20.102/32", s2Eth1, s1Eth100);
+
+ //
+ // Start to build intents between BGP speaker3 and BGP peer1
+ icmpPathintentConstructor(
+ "192.168.10.103/32", "192.168.10.1/32", s2Eth100, s1Eth1);
+ icmpPathintentConstructor(
+ "192.168.10.1/32", "192.168.10.103/32", s1Eth1, s2Eth100);
+
+ // Start to build intents between BGP speaker3 and BGP peer2
+ icmpPathintentConstructor(
+ "192.168.20.103/32", "192.168.20.1/32", s2Eth100, s2Eth1);
+ icmpPathintentConstructor(
+ "192.168.20.1/32", "192.168.20.103/32", s2Eth1, s2Eth100);
+
+ // Start to build intents between BGP speaker3 and BGP peer3
+ icmpPathintentConstructor(
+ "192.168.20.103/32", "192.168.20.2/32", s2Eth100, s2Eth1);
+ icmpPathintentConstructor(
+ "192.168.20.2/32", "192.168.20.103/32", s2Eth1, s2Eth100);
+
+ }
+
+ /**
+ * Initializes peer connectivity testing environment.
+ */
+ private void initPeerConnectivity() {
+
+ configInfoService = createMock(SdnIpConfigService.class);
+ expect(configInfoService.getBgpPeers()).andReturn(peers).anyTimes();
+ expect(configInfoService.getBgpSpeakers()).andReturn(bgpSpeakers).anyTimes();
+ replay(configInfoService);
+
+ intentService = createMock(IntentService.class);
+ replay(intentService);
+
+ peerConnectivityManager = new PeerConnectivityManager(configInfoService,
+ interfaceService, intentService);
+ }
+
+ /*
+ * EasyMock matcher that matches {@link PointToPointIntent}s but
+ * ignores the {@link IntentId} when matching.
+ * <p/>
+ * The normal intent equals method tests that the intent IDs are equal,
+ * however in these tests we can't know what the intent IDs will be in
+ * advance, so we can't set up expected intents with the correct IDs. Thus,
+ * the solution is to use an EasyMock matcher that verifies that all the
+ * value properties of the provided intent match the expected values, but
+ * ignores the intent ID when testing equality.
+ */
+ private static final class IdAgnosticPointToPointIntentMatcher implements
+ IArgumentMatcher {
+
+ private final PointToPointIntent intent;
+ private String providedIntentString;
+
+ /**
+ * Constructor taking the expected intent to match against.
+ *
+ * @param intent the expected intent
+ */
+ public IdAgnosticPointToPointIntentMatcher(PointToPointIntent intent) {
+ this.intent = intent;
+ }
+
+ @Override
+ public void appendTo(StringBuffer strBuffer) {
+ strBuffer.append("PointToPointIntentMatcher unable to match: "
+ + providedIntentString);
+ }
+
+ @Override
+ public boolean matches(Object object) {
+ if (!(object instanceof PointToPointIntent)) {
+ return false;
+ }
+
+ PointToPointIntent providedIntent = (PointToPointIntent) object;
+ providedIntentString = providedIntent.toString();
+
+ PointToPointIntent matchIntent =
+ new PointToPointIntent(providedIntent.id(),
+ intent.selector(), intent.treatment(),
+ intent.ingressPoint(), intent.egressPoint());
+
+ return matchIntent.equals(providedIntent);
+ }
+ }
+
+ /**
+ * Matcher method to set an expected intent to match against (ignoring the
+ * the intent ID).
+ *
+ * @param intent the expected intent
+ * @return something of type PointToPointIntent
+ */
+ private static PointToPointIntent eqExceptId(
+ PointToPointIntent intent) {
+ reportMatcher(new IdAgnosticPointToPointIntentMatcher(intent));
+ return null;
+ }
+
+ /**
+ * Tests whether peer connectivity manager can set up correct BGP and
+ * ICMP intents according to specific configuration.
+ * <p/>
+ * Two tricky cases included in the configuration are: 2 peers on a same
+ * switch port, peer on the same switch with BGPd.
+ */
+ @Test
+ public void testConnectionSetup() {
+
+ reset(intentService);
+
+ // Sets up the expected PointToPoint intents.
+ for (int i = 0; i < intentList.size(); i++) {
+ intentService.submit(eqExceptId(intentList.get(i)));
+ }
+
+ replay(intentService);
+
+ // Running the interface to be tested.
+ peerConnectivityManager.start();
+
+ verify(intentService);
+
+ }
+
+ /**
+ * Tests a corner case, when there are no interfaces in the configuration.
+ */
+ @Test
+ public void testNullInterfaces() {
+ reset(interfaceService);
+ expect(interfaceService.getInterfaces()).andReturn(
+ Sets.<Interface>newHashSet()).anyTimes();
+ expect(interfaceService.getInterface(s2Eth1))
+ .andReturn(null).anyTimes();
+ expect(interfaceService.getInterface(s1Eth1))
+ .andReturn(null).anyTimes();
+ replay(interfaceService);
+
+ reset(configInfoService);
+ expect(configInfoService.getBgpPeers()).andReturn(peers).anyTimes();
+ expect(configInfoService.getBgpSpeakers()).andReturn(bgpSpeakers).anyTimes();
+ replay(configInfoService);
+
+ reset(intentService);
+ replay(intentService);
+ peerConnectivityManager.start();
+ verify(intentService);
+ }
+
+ /**
+ * Tests a corner case, when there are no BGP peers in the configuration.
+ */
+ @Test
+ public void testNullBgpPeers() {
+ reset(interfaceService);
+ expect(interfaceService.getInterfaces()).andReturn(
+ Sets.newHashSet(interfaces.values())).anyTimes();
+ replay(interfaceService);
+
+ reset(configInfoService);
+ expect(configInfoService.getBgpPeers()).andReturn(
+ new HashMap<IpAddress, BgpPeer>()).anyTimes();
+ expect(configInfoService.getBgpSpeakers()).andReturn(
+ bgpSpeakers).anyTimes();
+ replay(configInfoService);
+
+ reset(intentService);
+ replay(intentService);
+ peerConnectivityManager.start();
+ verify(intentService);
+ }
+
+ /**
+ * Tests a corner case, when there is no BGP speakers in the configuration.
+ */
+ @Test
+ public void testNullBgpSpeakers() {
+ reset(interfaceService);
+ expect(interfaceService.getInterfaces()).andReturn(
+ Sets.newHashSet(interfaces.values())).anyTimes();
+ replay(interfaceService);
+
+ reset(configInfoService);
+ expect(configInfoService.getBgpPeers()).andReturn(
+ peers).anyTimes();
+ expect(configInfoService.getBgpSpeakers()).andReturn(
+ null).anyTimes();
+ replay(configInfoService);
+
+ reset(intentService);
+ replay(intentService);
+ peerConnectivityManager.start();
+ verify(intentService);
+ }
+
+ /**
+ * Tests a corner case, when there is no Interface configured for one BGP
+ * peer.
+ */
+ @Test
+ public void testNoPeerInterface() {
+ String peerSw100Eth1 = "192.168.200.1";
+ configuredPeers.put(IpAddress.valueOf(peerSw100Eth1),
+ new BgpPeer("00:00:00:00:00:00:01:00", 1, peerSw100Eth1));
+ testConnectionSetup();
+ }
+
+ /**
+ * Tests a corner case, when there is no Interface configured for one BGP
+ * speaker.
+ * TODO: we should add a configuration correctness checking module/method
+ * before testing this corner case.
+ */
+ @Ignore
+ @Test
+ public void testNoSpeakerInterface() {
+ BgpSpeaker bgpSpeaker100 = new BgpSpeaker(
+ "bgpSpeaker100",
+ "00:00:00:00:00:00:01:00", 100,
+ "00:00:00:00:01:00");
+ List<InterfaceAddress> interfaceAddresses100 =
+ new LinkedList<InterfaceAddress>();
+ interfaceAddresses100.add(new InterfaceAddress(dpid1, 1, "192.168.10.201"));
+ interfaceAddresses100.add(new InterfaceAddress(dpid2, 1, "192.168.20.201"));
+ bgpSpeaker100.setInterfaceAddresses(interfaceAddresses100);
+ configuredBgpSpeakers.put(bgpSpeaker100.name(), bgpSpeaker100);
+ testConnectionSetup();
+ }
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/topology/Topology.java b/core/api/src/main/java/org/onlab/onos/net/topology/Topology.java
index f71a5ec..ea3c68d 100644
--- a/core/api/src/main/java/org/onlab/onos/net/topology/Topology.java
+++ b/core/api/src/main/java/org/onlab/onos/net/topology/Topology.java
@@ -7,6 +7,8 @@
*/
public interface Topology extends Provided {
+ // FIXME: Following is not true right now. It is actually System.nanoTime(),
+ // which has no relation to epoch time, wall clock, etc.
/**
* Returns the time, specified in milliseconds since start of epoch,
* when the topology became active and made available.
diff --git a/core/store/dist/pom.xml b/core/store/dist/pom.xml
index b1e7172..6482729 100644
--- a/core/store/dist/pom.xml
+++ b/core/store/dist/pom.xml
@@ -52,6 +52,12 @@
<artifactId>easymock</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.onlab.onos</groupId>
+ <artifactId>onos-api</artifactId>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/flow/ReplicaInfoService.java b/core/store/dist/src/main/java/org/onlab/onos/store/flow/ReplicaInfoService.java
index c3f1bbf..e613348 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/flow/ReplicaInfoService.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/flow/ReplicaInfoService.java
@@ -3,7 +3,7 @@
import org.onlab.onos.net.DeviceId;
/**
- * Service to return where the Replica should be placed.
+ * Service to return where the replica should be placed.
*/
public interface ReplicaInfoService {
@@ -15,4 +15,19 @@
* @return placement information
*/
ReplicaInfo getReplicaInfoFor(DeviceId deviceId);
+
+ /**
+ * Adds the specified replica placement info change listener.
+ *
+ * @param listener the replica placement info change listener
+ */
+ void addListener(ReplicaInfoEventListener listener);
+
+ /**
+ * Removes the specified replica placement info change listener.
+ *
+ * @param listener the replica placement info change listener
+ */
+ void removeListener(ReplicaInfoEventListener listener);
+
}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/ReplicaInfoManager.java b/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/ReplicaInfoManager.java
index 0d4cd08..dcbdb4a 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/ReplicaInfoManager.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/ReplicaInfoManager.java
@@ -1,5 +1,6 @@
package org.onlab.onos.store.flow.impl;
+import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
import static org.onlab.onos.store.flow.ReplicaInfoEvent.Type.MASTER_CHANGED;
@@ -66,6 +67,16 @@
Collections.<NodeId>emptyList());
}
+ @Override
+ public void addListener(ReplicaInfoEventListener listener) {
+ listenerRegistry.addListener(checkNotNull(listener));
+ }
+
+ @Override
+ public void removeListener(ReplicaInfoEventListener listener) {
+ listenerRegistry.removeListener(checkNotNull(listener));
+ }
+
final class InternalMastershipListener implements MastershipListener {
@Override
diff --git a/core/store/dist/src/test/java/org/onlab/onos/store/flow/impl/ReplicaInfoManagerTest.java b/core/store/dist/src/test/java/org/onlab/onos/store/flow/impl/ReplicaInfoManagerTest.java
new file mode 100644
index 0000000..105e37b
--- /dev/null
+++ b/core/store/dist/src/test/java/org/onlab/onos/store/flow/impl/ReplicaInfoManagerTest.java
@@ -0,0 +1,165 @@
+package org.onlab.onos.store.flow.impl;
+
+import static com.google.common.base.Preconditions.checkState;
+import static org.junit.Assert.*;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.onos.cluster.NodeId;
+import org.onlab.onos.event.AbstractListenerRegistry;
+import org.onlab.onos.event.DefaultEventSinkRegistry;
+import org.onlab.onos.event.Event;
+import org.onlab.onos.event.EventDeliveryService;
+import org.onlab.onos.event.EventSink;
+import org.onlab.onos.mastership.MastershipEvent;
+import org.onlab.onos.mastership.MastershipEvent.Type;
+import org.onlab.onos.mastership.MastershipListener;
+import org.onlab.onos.mastership.MastershipService;
+import org.onlab.onos.mastership.MastershipServiceAdapter;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.store.flow.ReplicaInfo;
+import org.onlab.onos.store.flow.ReplicaInfoEvent;
+import org.onlab.onos.store.flow.ReplicaInfoEventListener;
+import org.onlab.onos.store.flow.ReplicaInfoService;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Maps;
+
+public class ReplicaInfoManagerTest {
+
+
+ private static final DeviceId DID1 = DeviceId.deviceId("of:1");
+ private static final DeviceId DID2 = DeviceId.deviceId("of:2");
+ private static final NodeId NID1 = new NodeId("foo");
+
+ private ReplicaInfoManager mgr;
+ private ReplicaInfoService service;
+
+ private AbstractListenerRegistry<MastershipEvent, MastershipListener>
+ mastershipListenerRegistry;
+ private TestEventDispatcher eventDispatcher;
+
+
+ @Before
+ public void setUp() throws Exception {
+ mastershipListenerRegistry = new AbstractListenerRegistry<>();
+
+ mgr = new ReplicaInfoManager();
+ service = mgr;
+
+ eventDispatcher = new TestEventDispatcher();
+ mgr.eventDispatcher = eventDispatcher;
+ mgr.mastershipService = new TestMastershipService();
+
+ // register dummy mastership event source
+ mgr.eventDispatcher.addSink(MastershipEvent.class, mastershipListenerRegistry);
+
+ mgr.activate();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mgr.deactivate();
+ }
+
+ @Test
+ public void testGetReplicaInfoFor() {
+ ReplicaInfo info1 = service.getReplicaInfoFor(DID1);
+ assertEquals(Optional.of(NID1), info1.master());
+ // backups are always empty for now
+ assertEquals(Collections.emptyList(), info1.backups());
+
+ ReplicaInfo info2 = service.getReplicaInfoFor(DID2);
+ assertEquals("There's no master", Optional.absent(), info2.master());
+ // backups are always empty for now
+ assertEquals(Collections.emptyList(), info2.backups());
+ }
+
+ @Test
+ public void testReplicaInfoEvent() throws InterruptedException {
+ final CountDownLatch latch = new CountDownLatch(1);
+ service.addListener(new MasterNodeCheck(latch, DID1, NID1));
+
+ // fake MastershipEvent
+ eventDispatcher.post(new MastershipEvent(Type.MASTER_CHANGED, DID1, NID1));
+
+ assertTrue(latch.await(1, TimeUnit.SECONDS));
+ }
+
+
+ private final class MasterNodeCheck implements ReplicaInfoEventListener {
+ private final CountDownLatch latch;
+ private Optional<NodeId> expectedMaster;
+ private DeviceId expectedDevice;
+
+
+ MasterNodeCheck(CountDownLatch latch, DeviceId did,
+ NodeId nid) {
+ this.latch = latch;
+ this.expectedMaster = Optional.fromNullable(nid);
+ this.expectedDevice = did;
+ }
+
+ @Override
+ public void event(ReplicaInfoEvent event) {
+ assertEquals(expectedDevice, event.subject());
+ assertEquals(expectedMaster, event.replicaInfo().master());
+ // backups are always empty for now
+ assertEquals(Collections.emptyList(), event.replicaInfo().backups());
+ latch.countDown();
+ }
+ }
+
+
+ private final class TestMastershipService
+ extends MastershipServiceAdapter
+ implements MastershipService {
+
+ private Map<DeviceId, NodeId> masters;
+
+ TestMastershipService() {
+ masters = Maps.newHashMap();
+ masters.put(DID1, NID1);
+ // DID2 has no master
+ }
+
+ @Override
+ public NodeId getMasterFor(DeviceId deviceId) {
+ return masters.get(deviceId);
+ }
+
+ @Override
+ public void addListener(MastershipListener listener) {
+ mastershipListenerRegistry.addListener(listener);
+ }
+
+ @Override
+ public void removeListener(MastershipListener listener) {
+ mastershipListenerRegistry.removeListener(listener);
+ }
+ }
+
+
+ // code clone
+ /**
+ * Implements event delivery system that delivers events synchronously, or
+ * in-line with the post method invocation.
+ */
+ private static class TestEventDispatcher extends DefaultEventSinkRegistry
+ implements EventDeliveryService {
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Override
+ public void post(Event event) {
+ EventSink sink = getSink(event.getClass());
+ checkState(sink != null, "No sink for event %s", event);
+ sink.process(event);
+ }
+ }
+}
diff --git a/providers/openflow/device/src/main/java/org/onlab/onos/provider/of/device/impl/OpenFlowDeviceProvider.java b/providers/openflow/device/src/main/java/org/onlab/onos/provider/of/device/impl/OpenFlowDeviceProvider.java
index 88cb1ac..f322270 100644
--- a/providers/openflow/device/src/main/java/org/onlab/onos/provider/of/device/impl/OpenFlowDeviceProvider.java
+++ b/providers/openflow/device/src/main/java/org/onlab/onos/provider/of/device/impl/OpenFlowDeviceProvider.java
@@ -172,7 +172,7 @@
*/
private List<PortDescription> buildPortDescriptions(
List<OFPortDesc> ports) {
- final List<PortDescription> portDescs = new ArrayList<>();
+ final List<PortDescription> portDescs = new ArrayList<>(ports.size());
for (OFPortDesc port : ports) {
portDescs.add(buildPortDescription(port));
}