Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
diff --git a/apps/sdnip/pom.xml b/apps/sdnip/pom.xml
index 99960a4..c8db20d 100644
--- a/apps/sdnip/pom.xml
+++ b/apps/sdnip/pom.xml
@@ -2,35 +2,40 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
+ <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.onlab.onos</groupId>
- <artifactId>onos-apps</artifactId>
- <version>1.0.0-SNAPSHOT</version>
- <relativePath>../pom.xml</relativePath>
- </parent>
+ <parent>
+ <groupId>org.onlab.onos</groupId>
+ <artifactId>onos-apps</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
- <artifactId>onos-app-sdnip</artifactId>
- <packaging>bundle</packaging>
+ <artifactId>onos-app-sdnip</artifactId>
+ <packaging>bundle</packaging>
- <description>SDN-IP peering application</description>
+ <description>SDN-IP peering application</description>
- <dependencies>
- <dependency>
- <groupId>org.codehaus.jackson</groupId>
- <artifactId>jackson-core-asl</artifactId>
- </dependency>
- <dependency>
- <groupId>org.codehaus.jackson</groupId>
- <artifactId>jackson-mapper-asl</artifactId>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-annotations</artifactId>
- <version>2.4.2</version>
- <scope>provided</scope>
- </dependency>
- </dependencies>
+ <dependencies>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-core-asl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-annotations</artifactId>
+ <version>2.4.2</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ </dependencies>
</project>
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/HostServiceBasedInterfaceService.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/HostServiceBasedInterfaceService.java
new file mode 100644
index 0000000..d6ad3c4
--- /dev/null
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/HostServiceBasedInterfaceService.java
@@ -0,0 +1,59 @@
+package org.onlab.onos.sdnip;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Set;
+
+import org.apache.commons.lang.NotImplementedException;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.host.HostService;
+import org.onlab.onos.net.host.PortAddresses;
+import org.onlab.onos.sdnip.config.Interface;
+import org.onlab.packet.IpAddress;
+
+import com.google.common.collect.Sets;
+
+
+
+/**
+ * Provides IntefaceService using PortAddresses data from the HostService.
+ */
+public class HostServiceBasedInterfaceService implements InterfaceService {
+
+ private final HostService hostService;
+
+ public HostServiceBasedInterfaceService(HostService hostService) {
+ this.hostService = checkNotNull(hostService);
+ }
+
+ @Override
+ public Set<Interface> getInterfaces() {
+ Set<PortAddresses> addresses = hostService.getAddressBindings();
+ Set<Interface> interfaces = Sets.newHashSetWithExpectedSize(addresses.size());
+ for (PortAddresses a : addresses) {
+ interfaces.add(new Interface(a));
+ }
+ return interfaces;
+ }
+
+ @Override
+ public Interface getInterface(ConnectPoint connectPoint) {
+ checkNotNull(connectPoint);
+
+ PortAddresses portAddresses =
+ hostService.getAddressBindingsForPort(connectPoint);
+
+ if (!portAddresses.ips().isEmpty()) {
+ return new Interface(portAddresses);
+ }
+
+ return null;
+ }
+
+ @Override
+ public Interface getMatchingInterface(IpAddress ipAddress) {
+ // TODO implement
+ throw new NotImplementedException("getMatchingInteface is not yet implemented");
+ }
+
+}
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/InterfaceService.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/InterfaceService.java
new file mode 100644
index 0000000..f9e881f
--- /dev/null
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/InterfaceService.java
@@ -0,0 +1,37 @@
+package org.onlab.onos.sdnip;
+
+import java.util.Set;
+
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.sdnip.config.Interface;
+import org.onlab.packet.IpAddress;
+
+/**
+ * Provides information about the interfaces in the network.
+ */
+public interface InterfaceService {
+ /**
+ * Retrieves the entire set of interfaces in the network.
+ *
+ * @return the set of interfaces
+ */
+ Set<Interface> getInterfaces();
+
+ /**
+ * Retrieves the interface associated with the given connect point.
+ *
+ * @param connectPoint the connect point to retrieve interface information
+ * for
+ * @return the interface
+ */
+ Interface getInterface(ConnectPoint connectPoint);
+
+ /**
+ * Retrieves the interface that matches the given IP address. Matching
+ * means that the IP address is in one of the interface's assigned subnets.
+ *
+ * @param ipAddress IP address to match
+ * @return the matching interface
+ */
+ Interface getMatchingInterface(IpAddress ipAddress);
+}
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/PeerConnectivity.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/PeerConnectivity.java
new file mode 100644
index 0000000..e17206d
--- /dev/null
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/PeerConnectivity.java
@@ -0,0 +1,290 @@
+package org.onlab.onos.sdnip;
+
+import java.util.List;
+
+import org.onlab.onos.net.ConnectPoint;
+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.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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Manages the connectivity requirements between peers.
+ */
+public class PeerConnectivity {
+
+ private static final Logger log = LoggerFactory.getLogger(
+ PeerConnectivity.class);
+
+ // TODO these shouldn't be defined here
+ private static final short BGP_PORT = 179;
+ private static final int IPV4_BIT_LENGTH = 32;
+
+ private final SdnIpConfigService configInfoService;
+ private final InterfaceService interfaceService;
+ private final IntentService intentService;
+
+ // TODO this sucks.
+ private int intentId = 0;
+
+ public PeerConnectivity(SdnIpConfigService configInfoService,
+ InterfaceService interfaceService, IntentService intentService) {
+ this.configInfoService = configInfoService;
+ this.interfaceService = interfaceService;
+ this.intentService = intentService;
+ }
+
+ public void start() {
+ // TODO are any of these errors?
+ if (interfaceService.getInterfaces().isEmpty()) {
+
+ log.warn("The interface in configuration file is empty. "
+ + "Thus, the SDN-IP application can not be started.");
+ } else if (configInfoService.getBgpPeers().isEmpty()) {
+
+ log.warn("The BGP peer in configuration file is empty."
+ + "Thus, the SDN-IP application can not be started.");
+ } else if (configInfoService.getBgpSpeakers() == null) {
+
+ log.error("The BGP speaker in configuration file is empty. "
+ + "Thus, the SDN-IP application can not be started.");
+ return;
+ }
+
+ setupBgpPaths();
+ setupIcmpPaths();
+ }
+
+ /**
+ * Sets up paths for all {@link BgpSpeaker}s and all external peers.
+ * <p/>
+ * Run a loop for all BGP speakers and a loop for all BGP peers outside.
+ * Push intents for paths from each BGP speaker to all peers. Push intents
+ * for paths from all peers to each BGP speaker.
+ */
+ private void setupBgpPaths() {
+ for (BgpSpeaker bgpSpeaker : configInfoService.getBgpSpeakers()
+ .values()) {
+ log.debug("Start to set up BGP paths for BGP speaker: {}",
+ bgpSpeaker);
+ ConnectPoint bgpdConnectPoint = bgpSpeaker.connectPoint();
+
+ List<InterfaceAddress> interfaceAddresses =
+ bgpSpeaker.interfaceAddresses();
+
+ for (BgpPeer bgpPeer : configInfoService.getBgpPeers().values()) {
+
+ log.debug("Start to set up BGP paths between BGP speaker: {} "
+ + "to BGP peer: {}", bgpSpeaker, bgpPeer);
+
+ Interface peerInterface = interfaceService.getInterface(
+ bgpPeer.connectPoint());
+ if (peerInterface == null) {
+ log.error("Can not find the corresponding Interface from "
+ + "configuration for BGP peer {}",
+ bgpPeer.ipAddress());
+ continue;
+ }
+
+ IpAddress bgpdAddress = null;
+ for (InterfaceAddress interfaceAddress : interfaceAddresses) {
+ if (interfaceAddress.connectPoint().equals(
+ peerInterface.connectPoint())) {
+ bgpdAddress = interfaceAddress.ipAddress();
+ break;
+ }
+ }
+ if (bgpdAddress == null) {
+ log.debug("There is no interface IP address for bgpPeer: {}"
+ + " on interface {}", bgpPeer, bgpPeer.connectPoint());
+ return;
+ }
+
+ IpAddress bgpdPeerAddress = bgpPeer.ipAddress();
+ ConnectPoint bgpdPeerConnectPoint = peerInterface.connectPoint();
+
+ // install intent for BGP path from BGPd to BGP peer matching
+ // destination TCP port 179
+
+ // TODO: The usage of PacketMatchBuilder will be improved, then we
+ // only need to new the PacketMatchBuilder once.
+ // By then, the code here will be improved accordingly.
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPProtocol(IPv4.PROTOCOL_TCP)
+ .matchIPSrc(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH))
+ .matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH))
+ .matchTcpDst(BGP_PORT)
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .build();
+
+ PointToPointIntent intentMatchDstTcpPort = new PointToPointIntent(
+ nextIntentId(), selector, treatment,
+ bgpdConnectPoint, bgpdPeerConnectPoint);
+ intentService.submit(intentMatchDstTcpPort);
+ log.debug("Submitted BGP path intent matching dst TCP port 179 "
+ + "from BGPd {} to peer {}: {}",
+ bgpdAddress, bgpdPeerAddress, intentMatchDstTcpPort);
+
+ // install intent for BGP path from BGPd to BGP peer matching
+ // source TCP port 179
+ selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPProtocol(IPv4.PROTOCOL_TCP)
+ .matchIPSrc(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH))
+ .matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH))
+ .matchTcpSrc(BGP_PORT)
+ .build();
+
+ PointToPointIntent intentMatchSrcTcpPort = new PointToPointIntent(
+ nextIntentId(), selector, treatment,
+ bgpdConnectPoint, bgpdPeerConnectPoint);
+ intentService.submit(intentMatchSrcTcpPort);
+ log.debug("Submitted BGP path intent matching src TCP port 179"
+ + "from BGPd {} to peer {}: {}",
+ bgpdAddress, bgpdPeerAddress, intentMatchSrcTcpPort);
+
+ // install intent for reversed BGP path from BGP peer to BGPd
+ // matching destination TCP port 179
+ selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPProtocol(IPv4.PROTOCOL_TCP)
+ .matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH))
+ .matchIPDst(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH))
+ .matchTcpDst(BGP_PORT)
+ .build();
+
+ PointToPointIntent reversedIntentMatchDstTcpPort = new PointToPointIntent(
+ nextIntentId(), selector, treatment,
+ bgpdPeerConnectPoint, bgpdConnectPoint);
+ intentService.submit(reversedIntentMatchDstTcpPort);
+ log.debug("Submitted BGP path intent matching dst TCP port 179"
+ + "from BGP peer {} to BGPd {} : {}",
+ bgpdPeerAddress, bgpdAddress, reversedIntentMatchDstTcpPort);
+
+ // install intent for reversed BGP path from BGP peer to BGPd
+ // matching source TCP port 179
+ selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPProtocol(IPv4.PROTOCOL_TCP)
+ .matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH))
+ .matchIPDst(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH))
+ .matchTcpSrc(BGP_PORT)
+ .build();
+
+ PointToPointIntent reversedIntentMatchSrcTcpPort = new PointToPointIntent(
+ nextIntentId(), selector, treatment,
+ bgpdPeerConnectPoint, bgpdConnectPoint);
+ intentService.submit(reversedIntentMatchSrcTcpPort);
+ log.debug("Submitted BGP path intent matching src TCP port 179"
+ + "from BGP peer {} to BGPd {} : {}",
+ bgpdPeerAddress, bgpdAddress, reversedIntentMatchSrcTcpPort);
+
+ }
+ }
+ }
+
+ /**
+ * Sets up ICMP paths between each {@link BgpSpeaker} and all BGP peers
+ * located in other external networks.
+ * <p/>
+ * Run a loop for all BGP speakers and a loop for all BGP Peers. Push
+ * intents for paths from each BGP speaker to all peers. Push intents
+ * for paths from all peers to each BGP speaker.
+ */
+ private void setupIcmpPaths() {
+ for (BgpSpeaker bgpSpeaker : configInfoService.getBgpSpeakers()
+ .values()) {
+ log.debug("Start to set up ICMP paths for BGP speaker: {}",
+ bgpSpeaker);
+ ConnectPoint bgpdConnectPoint = bgpSpeaker.connectPoint();
+ List<InterfaceAddress> interfaceAddresses = bgpSpeaker
+ .interfaceAddresses();
+
+ for (BgpPeer bgpPeer : configInfoService.getBgpPeers().values()) {
+
+ Interface peerInterface = interfaceService.getInterface(
+ bgpPeer.connectPoint());
+
+ if (peerInterface == null) {
+ log.error("Can not find the corresponding Interface from "
+ + "configuration for BGP peer {}",
+ bgpPeer.ipAddress());
+ continue;
+ }
+ IpAddress bgpdAddress = null;
+ for (InterfaceAddress interfaceAddress : interfaceAddresses) {
+ if (interfaceAddress.connectPoint().equals(
+ peerInterface.connectPoint())) {
+ bgpdAddress = interfaceAddress.ipAddress();
+ break;
+ }
+
+ }
+ if (bgpdAddress == null) {
+ log.debug("There is no IP address for bgpPeer: {} on "
+ + "interface port: {}", bgpPeer,
+ bgpPeer.connectPoint());
+ return;
+ }
+
+ IpAddress bgpdPeerAddress = bgpPeer.ipAddress();
+ ConnectPoint bgpdPeerConnectPoint = peerInterface.connectPoint();
+
+ // install intent for ICMP path from BGPd to BGP peer
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPProtocol(IPv4.PROTOCOL_ICMP)
+ .matchIPSrc(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH))
+ .matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH))
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .build();
+
+ PointToPointIntent intent = new PointToPointIntent(
+ nextIntentId(), selector, treatment,
+ bgpdConnectPoint, bgpdPeerConnectPoint);
+ intentService.submit(intent);
+ log.debug("Submitted ICMP path intent from BGPd {} to peer {} :"
+ + " {}", bgpdAddress, bgpdPeerAddress, intent);
+
+ // install intent for reversed ICMP path from BGP peer to BGPd
+ selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPProtocol(IPv4.PROTOCOL_ICMP)
+ .matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH))
+ .matchIPDst(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH))
+ .build();
+
+ PointToPointIntent reversedIntent = new PointToPointIntent(
+ nextIntentId(), selector, treatment,
+ bgpdPeerConnectPoint, bgpdConnectPoint);
+ intentService.submit(reversedIntent);
+ log.debug("Submitted ICMP path intent from BGP peer {} to BGPd"
+ + " {} : {}",
+ bgpdPeerAddress, bgpdAddress, reversedIntent);
+ }
+ }
+ }
+
+ private IntentId nextIntentId() {
+ return new IntentId(intentId++);
+ }
+}
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 a4f9b0f..25b13f1 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
@@ -5,6 +5,11 @@
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.onos.net.host.HostService;
+import org.onlab.onos.net.intent.IntentService;
+import org.onlab.onos.sdnip.config.SdnIpConfigReader;
import org.slf4j.Logger;
/**
@@ -15,9 +20,27 @@
private final Logger log = getLogger(getClass());
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentService intentService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ private SdnIpConfigReader config;
+ private PeerConnectivity peerConnectivity;
+
@Activate
protected void activate() {
log.debug("SDN-IP started");
+
+ config = new SdnIpConfigReader();
+ config.init();
+
+ InterfaceService interfaceService = new HostServiceBasedInterfaceService(hostService);
+
+ peerConnectivity = new PeerConnectivity(config, interfaceService, intentService);
+ peerConnectivity.start();
+
}
@Deactivate
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/BgpPeer.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/BgpPeer.java
new file mode 100644
index 0000000..3e89c58
--- /dev/null
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/BgpPeer.java
@@ -0,0 +1,81 @@
+package org.onlab.onos.sdnip.config;
+
+import java.util.Objects;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.PortNumber;
+import org.onlab.packet.IpAddress;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Configuration details for a BGP peer.
+ */
+public class BgpPeer {
+ private final ConnectPoint connectPoint;
+ private final IpAddress ipAddress;
+
+ /**
+ * Creates a new BgpPeer.
+ *
+ * @param dpid the DPID of the switch the peer is attached at, as a String
+ * @param port the port the peer is attached at
+ * @param ipAddress the IP address of the peer as a String
+ */
+ public BgpPeer(@JsonProperty("attachmentDpid") String dpid,
+ @JsonProperty("attachmentPort") int port,
+ @JsonProperty("ipAddress") String ipAddress) {
+ this.connectPoint = new ConnectPoint(
+ DeviceId.deviceId(SdnIpConfigReader.dpidToUri(dpid)),
+ PortNumber.portNumber(port));
+ this.ipAddress = IpAddress.valueOf(ipAddress);
+ }
+
+ /**
+ * Gets the connection point of the peer.
+ *
+ * @return the connection point
+ */
+ public ConnectPoint connectPoint() {
+ return connectPoint;
+ }
+
+ /**
+ * Gets the IP address of the peer.
+ *
+ * @return the IP address
+ */
+ public IpAddress ipAddress() {
+ return ipAddress;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(connectPoint, ipAddress);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+
+ if (!(obj instanceof BgpPeer)) {
+ return false;
+ }
+
+ BgpPeer that = (BgpPeer) obj;
+ return Objects.equals(this.connectPoint, that.connectPoint)
+ && Objects.equals(this.ipAddress, that.ipAddress);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("connectPoint", connectPoint)
+ .add("ipAddress", ipAddress)
+ .toString();
+ }
+}
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/BgpSpeaker.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/BgpSpeaker.java
new file mode 100644
index 0000000..248fc1d
--- /dev/null
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/BgpSpeaker.java
@@ -0,0 +1,136 @@
+package org.onlab.onos.sdnip.config;
+
+import java.util.List;
+import java.util.Objects;
+
+import org.codehaus.jackson.annotate.JsonCreator;
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.PortNumber;
+import org.onlab.packet.MacAddress;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Represents a BGP daemon in SDN network.
+ * <p/>
+ * Each BGP speaker has a attachment point, which includes a switch DPID and a
+ * switch port. Each BGP speaker has one MAC address and several IP addresses,
+ * which are used to peer with BGP peers outside the SDN network. For each
+ * peer outside the SDN network, we configure a different IP address to BGP
+ * speaker inside the SDN network.
+ * <p/>
+ * Each BGP speaker has a name, which is a unique identifying String that is
+ * used to reference this speaker in the configuration.
+ */
+public class BgpSpeaker {
+ private final String name;
+ private final ConnectPoint connectPoint;
+ private final MacAddress macAddress;
+ private List<InterfaceAddress> interfaceAddresses;
+
+ /**
+ * Class constructor used by the JSON library to create an object.
+ *
+ * @param name the name of the BGP speaker inside SDN network
+ * @param attachmentDpid the DPID where the BGP speaker is attached to
+ * @param attachmentPort the port where the BGP speaker is attached to
+ * @param macAddress the MAC address of the BGP speaker
+ */
+ @JsonCreator
+ public BgpSpeaker(@JsonProperty("name") String name,
+ @JsonProperty("attachmentDpid") String attachmentDpid,
+ @JsonProperty("attachmentPort") int attachmentPort,
+ @JsonProperty("macAddress") String macAddress) {
+
+ this.name = name;
+ this.macAddress = MacAddress.valueOf(macAddress);
+ this.connectPoint = new ConnectPoint(
+ DeviceId.deviceId(SdnIpConfigReader.dpidToUri(attachmentDpid)),
+ PortNumber.portNumber(attachmentPort));
+ }
+
+ /**
+ * Sets the addresses we configured for the BGP speaker on all virtual
+ * {@link Interface}s.
+ *
+ * @param interfaceAddresses a list of IP addresses of the BGP speaker
+ * configured on all virtual interfaces
+ */
+ @JsonProperty("interfaceAddresses")
+ public void setInterfaceAddresses(
+ List<InterfaceAddress> interfaceAddresses) {
+ this.interfaceAddresses = interfaceAddresses;
+ }
+
+ /**
+ * Gets the BGP speaker name.
+ *
+ * @return the BGP speaker name
+ */
+ public String name() {
+ return name;
+ }
+
+ /**
+ * Gets the connect point where the BGP speaker is attached.
+ *
+ * @return the connect point
+ */
+ public ConnectPoint connectPoint() {
+ return connectPoint;
+ }
+
+ /**
+ * Gets the MAC address of the BGP speaker.
+ *
+ * @return the MAC address
+ */
+ public MacAddress macAddress() {
+ return macAddress;
+ }
+
+ /**
+ * Gets all IP addresses configured on all {@link Interface}s of the
+ * BGP speaker.
+ *
+ * @return a list of IP addresses of the BGP speaker configured on all
+ * virtual interfaces
+ */
+ public List<InterfaceAddress> interfaceAddresses() {
+ return interfaceAddresses;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof BgpSpeaker)) {
+ return false;
+ }
+
+ BgpSpeaker otherBgpSpeaker = (BgpSpeaker) other;
+
+ return name.equals(otherBgpSpeaker.name) &&
+ connectPoint.equals(
+ otherBgpSpeaker.connectPoint) &&
+ macAddress.equals(otherBgpSpeaker.macAddress) &&
+ interfaceAddresses.equals(otherBgpSpeaker.interfaceAddresses);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, connectPoint, macAddress,
+ interfaceAddresses);
+
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("speakerName", name)
+ .add("connectPoint", connectPoint)
+ .add("macAddress", macAddress)
+ .add("interfaceAddresses", interfaceAddresses)
+ .toString();
+ }
+}
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/Configuration.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/Configuration.java
new file mode 100644
index 0000000..e6ed36a
--- /dev/null
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/Configuration.java
@@ -0,0 +1,64 @@
+package org.onlab.onos.sdnip.config;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * Contains the configuration data for SDN-IP that has been read from a
+ * JSON-formatted configuration file.
+ */
+public class Configuration {
+ // We call the BGP routers in our SDN network the BGP speakers, and call
+ // the BGP routers outside our SDN network the BGP peers.
+ private List<BgpSpeaker> bgpSpeakers;
+ private List<BgpPeer> peers;
+
+ /**
+ * Default constructor.
+ */
+ public Configuration() {
+ }
+
+ /**
+ * Gets a list of bgpSpeakers in the system, represented by
+ * {@link BgpSpeaker} objects.
+ *
+ * @return the list of BGP speakers
+ */
+ public List<BgpSpeaker> getBgpSpeakers() {
+ return Collections.unmodifiableList(bgpSpeakers);
+ }
+
+ /**
+ * Sets a list of bgpSpeakers in the system.
+ *
+ * @param bgpSpeakers the list of BGP speakers
+ */
+ @JsonProperty("bgpSpeakers")
+ public void setBgpSpeakers(List<BgpSpeaker> bgpSpeakers) {
+ this.bgpSpeakers = bgpSpeakers;
+ }
+
+ /**
+ * Gets a list of BGP peers we are configured to peer with. Peers are
+ * represented by {@link BgpPeer} objects.
+ *
+ * @return the list of BGP peers
+ */
+ public List<BgpPeer> getPeers() {
+ return Collections.unmodifiableList(peers);
+ }
+
+ /**
+ * Sets a list of BGP peers we are configured to peer with.
+ *
+ * @param peers the list of BGP peers
+ */
+ @JsonProperty("bgpPeers")
+ public void setPeers(List<BgpPeer> peers) {
+ this.peers = peers;
+ }
+
+}
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/Interface.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/Interface.java
new file mode 100644
index 0000000..88de952
--- /dev/null
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/Interface.java
@@ -0,0 +1,102 @@
+package org.onlab.onos.sdnip.config;
+
+import java.util.Objects;
+import java.util.Set;
+
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.host.PortAddresses;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.Sets;
+
+/**
+ * An Interface is a set of addresses that are logically mapped to a switch
+ * port in the network.
+ */
+public class Interface {
+ private final ConnectPoint connectPoint;
+ private final Set<IpPrefix> ipAddresses;
+ private final MacAddress macAddress;
+
+ /**
+ * Creates an Interface based on a connection point, a set of IP addresses
+ * and a MAC address.
+ *
+ * @param connectPoint the connect point this interface is mapped to
+ * @param prefixAddress the IP addresses for the interface
+ * @param macAddress the MAC address of the interface
+ */
+ public Interface(ConnectPoint connectPoint, Set<IpPrefix> prefixAddress,
+ MacAddress macAddress) {
+ this.connectPoint = connectPoint;
+ this.ipAddresses = Sets.newHashSet(prefixAddress);
+ this.macAddress = macAddress;
+ }
+
+ /**
+ * Creates an Interface based on a PortAddresses object.
+ *
+ * @param portAddresses the PortAddresses object to turn into an Interface
+ */
+ public Interface(PortAddresses portAddresses) {
+ connectPoint = portAddresses.connectPoint();
+ ipAddresses = Sets.newHashSet(portAddresses.ips());
+ macAddress = portAddresses.mac();
+ }
+
+ /**
+ * Retrieves the connection point that this interface maps to.
+ *
+ * @return the connection point
+ */
+ public ConnectPoint connectPoint() {
+ return connectPoint;
+ }
+
+ /**
+ * Retrieves the set of IP addresses that are assigned to the interface.
+ *
+ * @return the set of IP addresses
+ */
+ public Set<IpPrefix> ips() {
+ return ipAddresses;
+ }
+
+ /**
+ * Retrieves the MAC address that is assigned to the interface.
+ *
+ * @return the MAC address
+ */
+ public MacAddress mac() {
+ return macAddress;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof Interface)) {
+ return false;
+ }
+
+ Interface otherInterface = (Interface) other;
+
+ return connectPoint.equals(otherInterface.connectPoint) &&
+ ipAddresses.equals(otherInterface.ipAddresses) &&
+ macAddress.equals(otherInterface.macAddress);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(connectPoint, ipAddresses, macAddress);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("connectPoint", connectPoint)
+ .add("ipAddresses", ipAddresses)
+ .add("macAddress", macAddress)
+ .toString();
+ }
+}
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/InterfaceAddress.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/InterfaceAddress.java
new file mode 100644
index 0000000..1ae204f
--- /dev/null
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/InterfaceAddress.java
@@ -0,0 +1,85 @@
+package org.onlab.onos.sdnip.config;
+
+import java.util.Objects;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.PortNumber;
+import org.onlab.packet.IpAddress;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Represents an address of a {@link BgpSpeaker} configured on an
+ * {@link Interface}.
+ * <p/>
+ * Each InterfaceAddress includes the interface name and an IP address.
+ */
+public class InterfaceAddress {
+ private final ConnectPoint connectPoint;
+ private final IpAddress ipAddress;
+
+ /**
+ * Creates an InterfaceAddress object.
+ *
+ * @param dpid the DPID of the interface as a String
+ * @param port the port of the interface
+ * @param ipAddress the IP address of a {@link BgpSpeaker} configured on
+ * the interface
+ */
+ public InterfaceAddress(@JsonProperty("interfaceDpid") String dpid,
+ @JsonProperty("interfacePort") int port,
+ @JsonProperty("ipAddress") String ipAddress) {
+ this.connectPoint = new ConnectPoint(
+ DeviceId.deviceId(SdnIpConfigReader.dpidToUri(dpid)),
+ PortNumber.portNumber(port));
+ this.ipAddress = IpAddress.valueOf(ipAddress);
+ }
+
+ /**
+ * Gets the connection point of the peer.
+ *
+ * @return the connection point
+ */
+ public ConnectPoint connectPoint() {
+ return connectPoint;
+ }
+
+ /**
+ * Gets the IP address of a BGP speaker configured on an {@link Interface}.
+ *
+ * @return the IP address
+ */
+ public IpAddress ipAddress() {
+ return ipAddress;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(connectPoint, ipAddress);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+
+ if (!(obj instanceof InterfaceAddress)) {
+ return false;
+ }
+
+ InterfaceAddress that = (InterfaceAddress) obj;
+ return Objects.equals(this.connectPoint, that.connectPoint)
+ && Objects.equals(this.ipAddress, that.ipAddress);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("connectPoint", connectPoint)
+ .add("ipAddress", ipAddress)
+ .toString();
+ }
+}
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/SdnIpConfigReader.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/SdnIpConfigReader.java
new file mode 100644
index 0000000..50034ed
--- /dev/null
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/SdnIpConfigReader.java
@@ -0,0 +1,132 @@
+package org.onlab.onos.sdnip.config;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.codehaus.jackson.map.ObjectMapper;
+import org.onlab.packet.IpAddress;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * SDN-IP Config Reader provides IConfigInfoService
+ * by reading from an SDN-IP configuration file.
+ * It must be enabled on the nodes within the cluster
+ * not running SDN-IP.
+ * <p/>
+ * TODO: As a long term solution, a module providing
+ * general network configuration to ONOS nodes should be used.
+ */
+public class SdnIpConfigReader implements SdnIpConfigService {
+
+ private static final Logger log = LoggerFactory.getLogger(SdnIpConfigReader.class);
+
+ private static final String DEFAULT_CONFIG_FILE = "config/sdnip.json";
+ private String configFileName = DEFAULT_CONFIG_FILE;
+ //private Map<String, Interface> interfaces;
+ // We call the BGP routers in our SDN network the BGP speakers, and call
+ // the BGP routers outside our SDN network the BGP peers.
+ private Map<String, BgpSpeaker> bgpSpeakers;
+ private Map<IpAddress, BgpPeer> bgpPeers;
+ //private InvertedRadixTree<Interface> interfaceRoutes;
+
+ /**
+ * Reads the info contained in the configuration file.
+ *
+ * @param configFilename The name of configuration file for SDN-IP application.
+ */
+ private void readConfiguration(String configFilename) {
+ File gatewaysFile = new File(configFilename);
+ ObjectMapper mapper = new ObjectMapper();
+
+ try {
+ Configuration config = mapper.readValue(gatewaysFile, Configuration.class);
+ /*interfaces = new ConcurrentHashMap<>();
+ for (Interface intf : config.getInterfaces()) {
+ interfaces.put(intf.getName(), intf);
+ }*/
+ bgpSpeakers = new ConcurrentHashMap<>();
+ for (BgpSpeaker speaker : config.getBgpSpeakers()) {
+ bgpSpeakers.put(speaker.name(), speaker);
+ }
+ bgpPeers = new ConcurrentHashMap<>();
+ for (BgpPeer peer : config.getPeers()) {
+ bgpPeers.put(peer.ipAddress(), peer);
+ }
+ } catch (IOException e) {
+ log.error("Error reading JSON file", e);
+ //throw new ConfigurationRuntimeException("Error in JSON file", e);
+ }
+
+ // Populate the interface InvertedRadixTree
+ /*for (Interface intf : interfaces.values()) {
+ Ip4Prefix prefix = intf.getIp4Prefix();
+ String binaryString = RouteEntry.createBinaryString(prefix);
+ interfaceRoutes.put(binaryString, intf);
+ }*/
+ }
+
+ /**
+ * To find the Interface which has longest matchable IP prefix (sub-network
+ * prefix) to next hop IP address.
+ *
+ * @param address the IP address of next hop router
+ * @return the Interface which has longest matchable IP prefix
+ */
+ /*private Interface longestInterfacePrefixMatch(IpAddress address) {
+ Ip4Prefix prefixToSearchFor =
+ new Ip4Prefix(address, (short) Ip4Address.BIT_LENGTH);
+ String binaryString = RouteEntry.createBinaryString(prefixToSearchFor);
+
+ Iterator<Interface> it =
+ interfaceRoutes.getValuesForKeysPrefixing(binaryString).iterator();
+ Interface intf = null;
+ // Find the last prefix, which will be the longest prefix
+ while (it.hasNext()) {
+ intf = it.next();
+ }
+
+ return intf;
+ }*/
+
+ /*@Override
+ public Interface getOutgoingInterface(IpAddress dstIpAddress) {
+ return longestInterfacePrefixMatch(dstIpAddress);
+ }*/
+
+ public void init() {
+ //interfaceRoutes = new ConcurrentInvertedRadixTree<>(
+ //new DefaultByteArrayNodeFactory());
+
+ // Reading config values
+ /*String configFilenameParameter = context.getConfigParams(this).get("configfile");
+ if (configFilenameParameter != null) {
+ currentConfigFilename = configFilenameParameter;
+ }*/
+ log.debug("Config file set to {}", configFileName);
+
+ readConfiguration(configFileName);
+ }
+
+ /*@Override
+ public Map<String, Interface> getInterfaces() {
+ return Collections.unmodifiableMap(interfaces);
+ }*/
+
+ @Override
+ public Map<String, BgpSpeaker> getBgpSpeakers() {
+ return Collections.unmodifiableMap(bgpSpeakers);
+ }
+
+ @Override
+ public Map<IpAddress, BgpPeer> getBgpPeers() {
+ return Collections.unmodifiableMap(bgpPeers);
+ }
+
+ static String dpidToUri(String dpid) {
+ return "of:" + dpid.replace(":", "");
+ }
+}
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/SdnIpConfigService.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/SdnIpConfigService.java
new file mode 100644
index 0000000..62b2ae3
--- /dev/null
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/SdnIpConfigService.java
@@ -0,0 +1,45 @@
+package org.onlab.onos.sdnip.config;
+
+import java.util.Map;
+
+import org.onlab.packet.IpAddress;
+
+/**
+ * Provides information about the layer 3 properties of the network.
+ * This is based on IP addresses configured on ports in the network.
+ */
+public interface SdnIpConfigService {
+
+ /**
+ * Gets the list of virtual external-facing interfaces.
+ *
+ * @return the map of interface names to interface objects
+ */
+ //public Map<String, Interface> getInterfaces();
+
+ /**
+ * Gets the list of BGP speakers inside the SDN network.
+ *
+ * @return the map of BGP speaker names to BGP speaker objects
+ */
+ public Map<String, BgpSpeaker> getBgpSpeakers();
+
+ /**
+ * Gets the list of configured BGP peers.
+ *
+ * @return the map from peer IP address to BgpPeer object
+ */
+ public Map<IpAddress, BgpPeer> getBgpPeers();
+
+ /**
+ * Gets the Interface object for the interface that packets
+ * to dstIpAddress will be sent out of. Returns null if dstIpAddress is not
+ * in a directly connected network, or if no interfaces are configured.
+ *
+ * @param dstIpAddress destination IP address that we want to match to
+ * an outgoing interface
+ * @return the Interface object if one is found, otherwise null
+ */
+ //public Interface getOutgoingInterface(IpAddress dstIpAddress);
+
+}
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/package-info.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/package-info.java
new file mode 100644
index 0000000..1103dbc
--- /dev/null
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * SDN-IP configuration.
+ */
+package org.onlab.onos.sdnip.config;
\ No newline at end of file
diff --git a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 101cc82..3c75bf9 100644
--- a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -45,7 +45,7 @@
<action class="org.onlab.onos.cli.net.DeviceRoleCommand"/>
<completers>
<ref component-id="deviceIdCompleter"/>
- <ref component-id="roleCompleter"/>
+ <ref component-id="nodeIdCompleter"/>
<ref component-id="roleCompleter"/>
<null/>
</completers>
diff --git a/core/api/src/main/java/org/onlab/onos/net/DefaultHost.java b/core/api/src/main/java/org/onlab/onos/net/DefaultHost.java
index 85712e8..cb2e292 100644
--- a/core/api/src/main/java/org/onlab/onos/net/DefaultHost.java
+++ b/core/api/src/main/java/org/onlab/onos/net/DefaultHost.java
@@ -20,6 +20,7 @@
private final MacAddress mac;
private final VlanId vlan;
private final HostLocation location;
+ // FIXME: should be IpAddress
private final Set<IpPrefix> ips;
/**
diff --git a/core/api/src/main/java/org/onlab/onos/net/Host.java b/core/api/src/main/java/org/onlab/onos/net/Host.java
index 087db36..3a9bfa5 100644
--- a/core/api/src/main/java/org/onlab/onos/net/Host.java
+++ b/core/api/src/main/java/org/onlab/onos/net/Host.java
@@ -38,6 +38,7 @@
*
* @return set of IP addresses; empty if no IP address is bound
*/
+ // FIXME: Switch to IpAddress
Set<IpPrefix> ipAddresses();
/**
diff --git a/core/api/src/main/java/org/onlab/onos/net/LinkKey.java b/core/api/src/main/java/org/onlab/onos/net/LinkKey.java
index 56c96e0..f751938 100644
--- a/core/api/src/main/java/org/onlab/onos/net/LinkKey.java
+++ b/core/api/src/main/java/org/onlab/onos/net/LinkKey.java
@@ -4,8 +4,6 @@
import java.util.Objects;
-import org.onlab.onos.net.link.LinkDescription;
-
import com.google.common.base.MoreObjects;
// TODO Consider renaming.
@@ -69,16 +67,6 @@
return new LinkKey(link.src(), link.dst());
}
- /**
- * Creates a link identifier for the specified link.
- *
- * @param desc link description
- * @return a link identifier
- */
- public static LinkKey linkKey(LinkDescription desc) {
- return new LinkKey(desc.src(), desc.dst());
- }
-
@Override
public int hashCode() {
return Objects.hash(src(), dst);
diff --git a/core/api/src/main/java/org/onlab/onos/net/host/HostAdminService.java b/core/api/src/main/java/org/onlab/onos/net/host/HostAdminService.java
index 645f729..38cfa2e 100644
--- a/core/api/src/main/java/org/onlab/onos/net/host/HostAdminService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/host/HostAdminService.java
@@ -1,7 +1,5 @@
package org.onlab.onos.net.host;
-import java.util.Set;
-
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.HostId;
@@ -47,20 +45,4 @@
*/
void clearAddresses(ConnectPoint connectPoint);
- /**
- * Returns the addresses information for all connection points.
- *
- * @return the set of address bindings for all connection points
- */
- Set<PortAddresses> getAddressBindings();
-
- /**
- * Retrieves the addresses that have been bound to the given connection
- * point.
- *
- * @param connectPoint the connection point to retrieve address bindings
- * for
- * @return addresses bound to the port
- */
- PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint);
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/host/HostDescription.java b/core/api/src/main/java/org/onlab/onos/net/host/HostDescription.java
index f45a383..fc16854 100644
--- a/core/api/src/main/java/org/onlab/onos/net/host/HostDescription.java
+++ b/core/api/src/main/java/org/onlab/onos/net/host/HostDescription.java
@@ -37,6 +37,7 @@
*
* @return host IP address
*/
+ // FIXME: Switch to IpAddress
IpPrefix ipAddress();
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/host/HostService.java b/core/api/src/main/java/org/onlab/onos/net/host/HostService.java
index a0f51b3..09034eb 100644
--- a/core/api/src/main/java/org/onlab/onos/net/host/HostService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/host/HostService.java
@@ -110,6 +110,23 @@
void requestMac(IpAddress ip);
/**
+ * Returns the addresses information for all connection points.
+ *
+ * @return the set of address bindings for all connection points
+ */
+ Set<PortAddresses> getAddressBindings();
+
+ /**
+ * Retrieves the addresses that have been bound to the given connection
+ * point.
+ *
+ * @param connectPoint the connection point to retrieve address bindings
+ * for
+ * @return addresses bound to the port
+ */
+ PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint);
+
+ /**
* Adds the specified host listener.
*
* @param listener host listener
diff --git a/core/api/src/main/java/org/onlab/onos/net/host/HostStore.java b/core/api/src/main/java/org/onlab/onos/net/host/HostStore.java
index 3f1cb23..b27b697 100644
--- a/core/api/src/main/java/org/onlab/onos/net/host/HostStore.java
+++ b/core/api/src/main/java/org/onlab/onos/net/host/HostStore.java
@@ -29,6 +29,7 @@
HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId,
HostDescription hostDescription);
+ // FIXME: API to remove only IpAddress is missing
/**
* Removes the specified host from the inventory.
*
@@ -81,6 +82,7 @@
* @param ip ip address
* @return set of hosts with the given IP
*/
+ // FIXME: Switch to IpAddress
Set<Host> getHosts(IpPrefix ip);
/**
diff --git a/core/api/src/main/java/org/onlab/onos/net/host/PortAddresses.java b/core/api/src/main/java/org/onlab/onos/net/host/PortAddresses.java
index 728d922..4804daf 100644
--- a/core/api/src/main/java/org/onlab/onos/net/host/PortAddresses.java
+++ b/core/api/src/main/java/org/onlab/onos/net/host/PortAddresses.java
@@ -17,6 +17,7 @@
public class PortAddresses {
private final ConnectPoint connectPoint;
+ // TODO: Should this be IpAddress or IpPrefix?
private final Set<IpPrefix> ipAddresses;
private final MacAddress macAddress;
diff --git a/core/api/src/main/java/org/onlab/onos/net/proxyarp/ProxyArpService.java b/core/api/src/main/java/org/onlab/onos/net/proxyarp/ProxyArpService.java
index 77d1208..1e29a02 100644
--- a/core/api/src/main/java/org/onlab/onos/net/proxyarp/ProxyArpService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/proxyarp/ProxyArpService.java
@@ -1,5 +1,6 @@
package org.onlab.onos.net.proxyarp;
+import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.packet.PacketContext;
import org.onlab.packet.Ethernet;
import org.onlab.packet.IpPrefix;
@@ -23,8 +24,9 @@
* will be flooded at all edge ports.
*
* @param eth an arp request
+ * @param inPort the port the request was received on
*/
- void reply(Ethernet eth);
+ void reply(Ethernet eth, ConnectPoint inPort);
/**
* Forwards an ARP request to its destination. Floods at the edge the ARP request if the
diff --git a/core/api/src/test/java/org/onlab/onos/net/host/HostServiceAdapter.java b/core/api/src/test/java/org/onlab/onos/net/host/HostServiceAdapter.java
index f03621c..2394302 100644
--- a/core/api/src/test/java/org/onlab/onos/net/host/HostServiceAdapter.java
+++ b/core/api/src/test/java/org/onlab/onos/net/host/HostServiceAdapter.java
@@ -75,4 +75,14 @@
public void removeListener(HostListener listener) {
}
+ @Override
+ public Set<PortAddresses> getAddressBindings() {
+ return null;
+ }
+
+ @Override
+ public PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint) {
+ return null;
+ }
+
}
diff --git a/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java b/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
index ac10384..8a86544 100644
--- a/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
@@ -5,6 +5,7 @@
import static org.slf4j.LoggerFactory.getLogger;
import java.nio.ByteBuffer;
+import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
@@ -15,6 +16,7 @@
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.Device;
import org.onlab.onos.net.Host;
import org.onlab.onos.net.HostId;
@@ -27,6 +29,7 @@
import org.onlab.onos.net.flow.DefaultTrafficTreatment;
import org.onlab.onos.net.flow.TrafficTreatment;
import org.onlab.onos.net.host.HostService;
+import org.onlab.onos.net.host.PortAddresses;
import org.onlab.onos.net.link.LinkEvent;
import org.onlab.onos.net.link.LinkListener;
import org.onlab.onos.net.link.LinkService;
@@ -37,7 +40,9 @@
import org.onlab.onos.net.proxyarp.ProxyArpService;
import org.onlab.packet.ARP;
import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.slf4j.Logger;
@@ -101,12 +106,46 @@
}
@Override
- public void reply(Ethernet eth) {
+ public void reply(Ethernet eth, ConnectPoint inPort) {
checkNotNull(eth, REQUEST_NULL);
checkArgument(eth.getEtherType() == Ethernet.TYPE_ARP,
REQUEST_NOT_ARP);
ARP arp = (ARP) eth.getPayload();
checkArgument(arp.getOpCode() == ARP.OP_REQUEST, NOT_ARP_REQUEST);
+ checkNotNull(inPort);
+
+ // If the source address matches one of our external addresses
+ // it could be a request from an internal host to an external
+ // address. Forward it over to the correct port.
+ IpAddress source = IpAddress.valueOf(arp.getSenderProtocolAddress());
+ PortAddresses sourceAddresses = findOutsidePortInSubnet(source);
+ if (sourceAddresses != null && !isOutsidePort(inPort)) {
+ for (IpPrefix subnet : sourceAddresses.ips()) {
+ if (subnet.toIpAddress().equals(source)) {
+ sendTo(eth, sourceAddresses.connectPoint());
+ return;
+ }
+ }
+ }
+
+ // If the request came from outside the network, only reply if it was
+ // for one of our external addresses.
+ if (isOutsidePort(inPort)) {
+ IpAddress target = IpAddress.valueOf(arp.getTargetProtocolAddress());
+ PortAddresses addresses = hostService.getAddressBindingsForPort(inPort);
+
+ for (IpPrefix interfaceAddress : addresses.ips()) {
+ if (interfaceAddress.toIpAddress().equals(target)) {
+ Ethernet arpReply = buildArpReply(interfaceAddress,
+ addresses.mac(), eth);
+ sendTo(arpReply, inPort);
+ }
+ }
+
+ return;
+ }
+
+ // Continue with normal proxy ARP case
VlanId vlan = VlanId.vlanId(eth.getVlanID());
Set<Host> hosts = hostService.getHostsByIp(IpPrefix.valueOf(arp
@@ -128,12 +167,62 @@
return;
}
- Ethernet arpReply = buildArpReply(dst, eth);
+ Ethernet arpReply = buildArpReply(dst.ipAddresses().iterator().next(),
+ dst.mac(), eth);
// TODO: check send status with host service.
+ sendTo(arpReply, src.location());
+ }
+
+ /**
+ * Outputs the given packet out the given port.
+ *
+ * @param packet the packet to send
+ * @param outPort the port to send it out
+ */
+ private void sendTo(Ethernet packet, ConnectPoint outPort) {
+ if (internalPorts.containsEntry(
+ deviceService.getDevice(outPort.deviceId()), outPort.port())) {
+ // Sanity check to make sure we don't send the packet out an
+ // internal port and create a loop (could happen due to
+ // misconfiguration).
+ return;
+ }
+
TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
- builder.setOutput(src.location().port());
- packetService.emit(new DefaultOutboundPacket(src.location().deviceId(),
- builder.build(), ByteBuffer.wrap(arpReply.serialize())));
+ builder.setOutput(outPort.port());
+ packetService.emit(new DefaultOutboundPacket(outPort.deviceId(),
+ builder.build(), ByteBuffer.wrap(packet.serialize())));
+ }
+
+ /**
+ * Finds the port with an address in the subnet of the target address, if
+ * one exists.
+ *
+ * @param target the target address to find a matching external port for
+ * @return a PortAddresses object containing the external addresses if one
+ * was found, otherwise null.
+ */
+ private PortAddresses findOutsidePortInSubnet(IpAddress target) {
+ for (PortAddresses addresses : hostService.getAddressBindings()) {
+ for (IpPrefix prefix : addresses.ips()) {
+ if (prefix.contains(target)) {
+ return new PortAddresses(addresses.connectPoint(),
+ Collections.singleton(prefix), addresses.mac());
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns whether the given port is an outside-facing port with an IP
+ * address configured.
+ *
+ * @param port the port to check
+ * @return true if the port is an outside-facing port, otherwise false
+ */
+ private boolean isOutsidePort(ConnectPoint port) {
+ return !hostService.getAddressBindingsForPort(port).ips().isEmpty();
}
@Override
@@ -167,7 +256,7 @@
if (arp.getOpCode() == ARP.OP_REPLY) {
forward(ethPkt);
} else if (arp.getOpCode() == ARP.OP_REQUEST) {
- reply(ethPkt);
+ reply(ethPkt, context.inPacket().receivedFrom());
}
context.block();
return true;
@@ -185,12 +274,16 @@
synchronized (externalPorts) {
for (Entry<Device, PortNumber> entry : externalPorts.entries()) {
+ ConnectPoint cp = new ConnectPoint(entry.getKey().id(), entry.getValue());
+ if (isOutsidePort(cp)) {
+ continue;
+ }
+
builder = DefaultTrafficTreatment.builder();
builder.setOutput(entry.getValue());
packetService.emit(new DefaultOutboundPacket(entry.getKey().id(),
builder.build(), buf));
}
-
}
}
@@ -234,15 +327,19 @@
}
/**
- * Builds an arp reply based on a request.
- * @param h the host we want to send to
- * @param request the arp request we got
- * @return an ethernet frame containing the arp reply
+ * Builds an ARP reply based on a request.
+ *
+ * @param srcIp the IP address to use as the reply source
+ * @param srcMac the MAC address to use as the reply source
+ * @param request the ARP request we got
+ * @return an Ethernet frame containing the ARP reply
*/
- private Ethernet buildArpReply(Host h, Ethernet request) {
+ private Ethernet buildArpReply(IpPrefix srcIp, MacAddress srcMac,
+ Ethernet request) {
+
Ethernet eth = new Ethernet();
eth.setDestinationMACAddress(request.getSourceMACAddress());
- eth.setSourceMACAddress(h.mac().getAddress());
+ eth.setSourceMACAddress(srcMac.getAddress());
eth.setEtherType(Ethernet.TYPE_ARP);
eth.setVlanID(request.getVlanID());
@@ -253,12 +350,12 @@
arp.setProtocolAddressLength((byte) IpPrefix.INET_LEN);
arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
- arp.setSenderHardwareAddress(h.mac().getAddress());
+ arp.setSenderHardwareAddress(srcMac.getAddress());
arp.setTargetHardwareAddress(request.getSourceMACAddress());
arp.setTargetProtocolAddress(((ARP) request.getPayload())
.getSenderProtocolAddress());
- arp.setSenderProtocolAddress(h.ipAddresses().iterator().next().toRealInt());
+ arp.setSenderProtocolAddress(srcIp.toRealInt());
eth.setPayload(arp);
return eth;
}
diff --git a/core/net/src/test/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManagerTest.java b/core/net/src/test/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManagerTest.java
index ddd4827..fa68761 100644
--- a/core/net/src/test/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManagerTest.java
+++ b/core/net/src/test/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManagerTest.java
@@ -13,6 +13,7 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.Set;
import org.junit.Before;
import org.junit.Test;
@@ -31,6 +32,7 @@
import org.onlab.onos.net.flow.instructions.Instruction;
import org.onlab.onos.net.flow.instructions.Instructions.OutputInstruction;
import org.onlab.onos.net.host.HostService;
+import org.onlab.onos.net.host.PortAddresses;
import org.onlab.onos.net.link.LinkListener;
import org.onlab.onos.net.link.LinkService;
import org.onlab.onos.net.packet.OutboundPacket;
@@ -50,12 +52,13 @@
*/
public class ProxyArpManagerTest {
- private static final int NUM_DEVICES = 4;
+ private static final int NUM_DEVICES = 6;
private static final int NUM_PORTS_PER_DEVICE = 3;
- private static final int NUM_FLOOD_PORTS = 4;
+ private static final int NUM_ADDRESS_PORTS = NUM_DEVICES / 2;
+ private static final int NUM_FLOOD_PORTS = 3;
- private static final IpPrefix IP1 = IpPrefix.valueOf("10.0.0.1/24");
- private static final IpPrefix IP2 = IpPrefix.valueOf("10.0.0.2/24");
+ private static final IpPrefix IP1 = IpPrefix.valueOf("192.168.1.1/24");
+ private static final IpPrefix IP2 = IpPrefix.valueOf("192.168.1.2/24");
private static final ProviderId PID = new ProviderId("of", "foo");
@@ -104,6 +107,9 @@
* The default topology is a unidirectional ring topology. Each switch has
* 3 ports. Ports 2 and 3 have the links to neighbor switches, and port 1
* is free (edge port).
+ * The first half of the switches have IP addresses configured on their
+ * free ports (port 1). The second half of the switches have no IP
+ * addresses configured.
*/
private void createTopology() {
deviceService = createMock(DeviceService.class);
@@ -114,6 +120,7 @@
createDevices(NUM_DEVICES, NUM_PORTS_PER_DEVICE);
createLinks(NUM_DEVICES);
+ addAddressBindings();
}
/**
@@ -138,10 +145,11 @@
ports.add(port);
}
- expect(deviceService.getPorts(devId)).andReturn(ports);
+ expect(deviceService.getPorts(devId)).andReturn(ports).anyTimes();
+ expect(deviceService.getDevice(devId)).andReturn(device).anyTimes();
}
- expect(deviceService.getDevices()).andReturn(devices);
+ expect(deviceService.getDevices()).andReturn(devices).anyTimes();
replay(deviceService);
}
@@ -173,6 +181,31 @@
replay(linkService);
}
+ private void addAddressBindings() {
+ Set<PortAddresses> addresses = Sets.newHashSet();
+
+ for (int i = 1; i <= NUM_ADDRESS_PORTS; i++) {
+ ConnectPoint cp = new ConnectPoint(getDeviceId(i), P1);
+ IpPrefix prefix1 = IpPrefix.valueOf("10.0." + (2 * i - 1) + ".1/24");
+ IpPrefix prefix2 = IpPrefix.valueOf("10.0." + (2 * i) + ".1/24");
+ PortAddresses pa = new PortAddresses(cp,
+ Sets.newHashSet(prefix1, prefix2), MacAddress.valueOf(i));
+ addresses.add(pa);
+
+ expect(hostService.getAddressBindingsForPort(cp))
+ .andReturn(pa).anyTimes();
+ }
+
+ expect(hostService.getAddressBindings()).andReturn(addresses).anyTimes();
+
+ for (int i = 1; i <= NUM_FLOOD_PORTS; i++) {
+ ConnectPoint cp = new ConnectPoint(getDeviceId(i + NUM_ADDRESS_PORTS),
+ P1);
+ expect(hostService.getAddressBindingsForPort(cp))
+ .andReturn(new PortAddresses(cp, null, null)).anyTimes();
+ }
+ }
+
/**
* Tests {@link ProxyArpManager#known(IpPrefix)} in the case where the
* IP address is not known.
@@ -210,10 +243,10 @@
*/
@Test
public void testReplyKnown() {
- Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN1, LOC2,
+ Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN1, getLocation(4),
Collections.singleton(IP1));
- Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1,
+ Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Collections.singleton(IP2));
expect(hostService.getHostsByIp(IpPrefix.valueOf(IP1.toOctets())))
@@ -224,11 +257,11 @@
Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
- proxyArp.reply(arpRequest);
+ proxyArp.reply(arpRequest, getLocation(5));
assertEquals(1, packetService.packets.size());
Ethernet arpReply = buildArp(ARP.OP_REPLY, MAC1, MAC2, IP1, IP2);
- verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
+ verifyPacketOut(arpReply, getLocation(5), packetService.packets.get(0));
}
/**
@@ -238,7 +271,7 @@
*/
@Test
public void testReplyUnknown() {
- Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1,
+ Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Collections.singleton(IP2));
expect(hostService.getHostsByIp(IpPrefix.valueOf(IP1.toOctets())))
@@ -249,7 +282,7 @@
Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
- proxyArp.reply(arpRequest);
+ proxyArp.reply(arpRequest, getLocation(5));
verifyFlood(arpRequest);
}
@@ -262,10 +295,10 @@
*/
@Test
public void testReplyDifferentVlan() {
- Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN2, LOC2,
+ Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN2, getLocation(4),
Collections.singleton(IP1));
- Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1,
+ Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Collections.singleton(IP2));
expect(hostService.getHostsByIp(IpPrefix.valueOf(IP1.toOctets())))
@@ -276,11 +309,84 @@
Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
- proxyArp.reply(arpRequest);
+ proxyArp.reply(arpRequest, getLocation(5));
verifyFlood(arpRequest);
}
+ @Test
+ public void testReplyToRequestForUs() {
+ IpPrefix theirIp = IpPrefix.valueOf("10.0.1.254/24");
+ IpPrefix ourFirstIp = IpPrefix.valueOf("10.0.1.1/24");
+ IpPrefix ourSecondIp = IpPrefix.valueOf("10.0.2.1/24");
+ MacAddress ourMac = MacAddress.valueOf(1L);
+
+ Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1,
+ Collections.singleton(theirIp));
+
+ expect(hostService.getHost(HID2)).andReturn(requestor);
+ replay(hostService);
+
+ Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, theirIp, ourFirstIp);
+
+ proxyArp.reply(arpRequest, LOC1);
+
+ assertEquals(1, packetService.packets.size());
+ Ethernet arpReply = buildArp(ARP.OP_REPLY, ourMac, MAC2, ourFirstIp, theirIp);
+ verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
+
+ // Test a request for the second address on that port
+ packetService.packets.clear();
+ arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, theirIp, ourSecondIp);
+
+ proxyArp.reply(arpRequest, LOC1);
+
+ assertEquals(1, packetService.packets.size());
+ arpReply = buildArp(ARP.OP_REPLY, ourMac, MAC2, ourSecondIp, theirIp);
+ verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
+ }
+
+ @Test
+ public void testReplyExternalPortBadRequest() {
+ replay(hostService); // no further host service expectations
+
+ IpPrefix theirIp = IpPrefix.valueOf("10.0.1.254/24");
+
+ // Request for a valid external IP address but coming in the wrong port
+ Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC1, null, theirIp,
+ IpPrefix.valueOf("10.0.3.1"));
+ proxyArp.reply(arpRequest, LOC1);
+ assertEquals(0, packetService.packets.size());
+
+ // Request for a valid internal IP address but coming in an external port
+ packetService.packets.clear();
+ arpRequest = buildArp(ARP.OP_REQUEST, MAC1, null, theirIp, IP1);
+ proxyArp.reply(arpRequest, LOC1);
+ assertEquals(0, packetService.packets.size());
+ }
+
+ @Test
+ public void testReplyToRequestFromUs() {
+ replay(hostService); // no further host service expectations
+
+ IpPrefix ourIp = IpPrefix.valueOf("10.0.1.1/24");
+ MacAddress ourMac = MacAddress.valueOf(1L);
+ IpPrefix theirIp = IpPrefix.valueOf("10.0.1.100/24");
+
+ // This is a request from something inside our network (like a BGP
+ // daemon) to an external host.
+ Ethernet arpRequest = buildArp(ARP.OP_REQUEST, ourMac, null, ourIp, theirIp);
+ proxyArp.reply(arpRequest, getLocation(5));
+
+ assertEquals(1, packetService.packets.size());
+ verifyPacketOut(arpRequest, getLocation(1), packetService.packets.get(0));
+
+ // The same request from a random external port should fail
+ packetService.packets.clear();
+ proxyArp.reply(arpRequest, getLocation(2));
+ assertEquals(0, packetService.packets.size());
+ }
+
/**
* Tests {@link ProxyArpManager#forward(Ethernet)} in the case where the
* destination host is known.
@@ -338,7 +444,8 @@
});
for (int i = 0; i < NUM_FLOOD_PORTS; i++) {
- ConnectPoint cp = new ConnectPoint(getDeviceId(i + 1), PortNumber.portNumber(1));
+ ConnectPoint cp = new ConnectPoint(getDeviceId(NUM_ADDRESS_PORTS + i + 1),
+ PortNumber.portNumber(1));
OutboundPacket outboundPacket = packetService.packets.get(i);
verifyPacketOut(packet, cp, outboundPacket);
@@ -372,6 +479,10 @@
return DeviceId.deviceId("" + i);
}
+ private static HostLocation getLocation(int i) {
+ return new HostLocation(new ConnectPoint(getDeviceId(i), P1), 123L);
+ }
+
/**
* Builds an ARP packet with the given parameters.
*
diff --git a/core/net/src/test/java/org/onlab/onos/net/topology/impl/DefaultTopologyProviderTest.java b/core/net/src/test/java/org/onlab/onos/net/topology/impl/DefaultTopologyProviderTest.java
index c9fd96d..4a8a3e9 100644
--- a/core/net/src/test/java/org/onlab/onos/net/topology/impl/DefaultTopologyProviderTest.java
+++ b/core/net/src/test/java/org/onlab/onos/net/topology/impl/DefaultTopologyProviderTest.java
@@ -1,6 +1,7 @@
package org.onlab.onos.net.topology.impl;
import com.google.common.collect.ImmutableSet;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -21,10 +22,12 @@
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Phaser;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.onlab.junit.TestTools.assertAfter;
+import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.*;
import static org.onlab.onos.net.NetTestTools.device;
import static org.onlab.onos.net.NetTestTools.link;
import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED;
@@ -41,6 +44,9 @@
private TestLinkService linkService = new TestLinkService();
private TestTopoProviderService providerService;
+ // phase corresponds to number of topologyChanged called
+ private Phaser topologyChangedCounts = new Phaser(1);
+
@Before
public void setUp() {
provider.deviceService = deviceService;
@@ -66,26 +72,23 @@
}
@Test
- public void basics() {
- assertAfter(100, new Runnable() {
- @Override
- public void run() {
- validateSubmission();
- }
- });
+ public void basics() throws InterruptedException, TimeoutException {
+ assertEquals(1, topologyChangedCounts.awaitAdvanceInterruptibly(0, 1, TimeUnit.SECONDS));
+ validateSubmission();
}
@Test
- public void eventDriven() {
- assertAfter(100, new Runnable() {
- @Override
- public void run() {
- validateSubmission();
- deviceService.post(new DeviceEvent(DEVICE_ADDED, device("z"), null));
- linkService.post(new LinkEvent(LINK_ADDED, link("z", 1, "a", 4)));
- validateSubmission();
- }
- });
+ public void eventDriven() throws InterruptedException, TimeoutException {
+ assertEquals(1, topologyChangedCounts.awaitAdvanceInterruptibly(0, 1, TimeUnit.SECONDS));
+ validateSubmission();
+
+ deviceService.post(new DeviceEvent(DEVICE_ADDED, device("z"), null));
+ linkService.post(new LinkEvent(LINK_ADDED, link("z", 1, "a", 4)));
+ assertThat(topologyChangedCounts.awaitAdvanceInterruptibly(1, 1, TimeUnit.SECONDS),
+ is(greaterThanOrEqualTo(2)));
+ // Note: posting event, to trigger topologyChanged call,
+ // but dummy topology will not change.
+ validateSubmission();
}
@@ -119,6 +122,7 @@
@Override
public void topologyChanged(GraphDescription graphDescription, List<Event> reasons) {
graphDesc = graphDescription;
+ topologyChangedCounts.arrive();
}
}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/DistributedClusterStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/DistributedClusterStore.java.bak
similarity index 96%
rename from core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/DistributedClusterStore.java
rename to core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/DistributedClusterStore.java.bak
index 8f48890..c781b23 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/DistributedClusterStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/DistributedClusterStore.java.bak
@@ -18,7 +18,6 @@
import org.onlab.onos.cluster.NodeId;
import org.onlab.onos.store.AbstractStore;
import org.onlab.onos.store.cluster.messaging.ClusterCommunicationAdminService;
-import org.onlab.onos.store.cluster.messaging.impl.ClusterCommunicationManager;
import org.onlab.packet.IpPrefix;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -48,7 +47,7 @@
private final Map<NodeId, State> states = new ConcurrentHashMap<>();
private final Cache<NodeId, ControllerNode> livenessCache = CacheBuilder.newBuilder()
.maximumSize(1000)
- .expireAfterWrite(ClusterCommunicationManager.HEART_BEAT_INTERVAL_MILLIS * 3, TimeUnit.MILLISECONDS)
+ .expireAfterWrite(/*ClusterCommunicationManager.HEART_BEAT_INTERVAL_MILLIS * */3, TimeUnit.MILLISECONDS)
.removalListener(new LivenessCacheRemovalListener()).build();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/ClusterCommunicationAdminService.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/ClusterCommunicationAdminService.java
deleted file mode 100644
index 0bc31fa..0000000
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/ClusterCommunicationAdminService.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package org.onlab.onos.store.cluster.messaging;
-
-import org.onlab.onos.cluster.ControllerNode;
-import org.onlab.onos.store.cluster.impl.ClusterNodesDelegate;
-
-// TODO: This service interface can be removed, once we properly start
-// using ClusterService
-/**
- * Service for administering communications manager.
- */
-public interface ClusterCommunicationAdminService {
-
- /**
- * Initialize.
- */
- void initialize(ControllerNode localNode, ClusterNodesDelegate nodesDelegate);
-
- /**
- * Adds the node to the list of monitored nodes.
- *
- * @param node node to be added
- */
- void addNode(ControllerNode node);
-
- /**
- * Removes the node from the list of monitored nodes.
- *
- * @param node node to be removed
- */
- void removeNode(ControllerNode node);
-}
\ No newline at end of file
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/SerializationService.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/SerializationService.java
deleted file mode 100644
index 3082718..0000000
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/SerializationService.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package org.onlab.onos.store.cluster.messaging;
-
-// FIXME: not used any more? remove
-/**
- * Service for encoding & decoding intra-cluster message payload.
- */
-public interface SerializationService {
-
- /**
- * Decodes the specified byte buffer to obtain the message within.
- *
- * @param buffer byte buffer with message(s)
- * @return parsed message
- */
- <T> T decode(byte[] data);
-
- /**
- * Encodes the specified message into the given byte buffer.
- *
- * @param message message to be encoded
- * @param buffer byte buffer to receive the message data
- */
- byte[] encode(Object message);
-
-}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManager.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManager.java
index 1b11873..c7852ae 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManager.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManager.java
@@ -4,8 +4,6 @@
import java.io.IOException;
import java.util.Set;
-import java.util.Timer;
-import java.util.TimerTask;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
@@ -16,9 +14,6 @@
import org.onlab.onos.cluster.ControllerNode;
import org.onlab.onos.cluster.NodeId;
import org.onlab.onos.store.cluster.impl.ClusterMembershipEvent;
-import org.onlab.onos.store.cluster.impl.ClusterMembershipEventType;
-import org.onlab.onos.store.cluster.impl.ClusterNodesDelegate;
-import org.onlab.onos.store.cluster.messaging.ClusterCommunicationAdminService;
import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
import org.onlab.onos.store.cluster.messaging.ClusterMessage;
import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
@@ -39,19 +34,13 @@
@Component(immediate = true)
@Service
public class ClusterCommunicationManager
- implements ClusterCommunicationService, ClusterCommunicationAdminService {
+ implements ClusterCommunicationService {
private final Logger log = LoggerFactory.getLogger(getClass());
- private ControllerNode localNode;
-
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private ClusterService clusterService;
- private ClusterNodesDelegate nodesDelegate;
- private final Timer timer = new Timer("onos-controller-heatbeats");
- public static final long HEART_BEAT_INTERVAL_MILLIS = 1000L;
-
// TODO: This probably should not be a OSGi service.
private MessagingService messagingService;
@@ -72,7 +61,7 @@
@Activate
public void activate() {
- localNode = clusterService.getLocalNode();
+ ControllerNode localNode = clusterService.getLocalNode();
NettyMessagingService netty = new NettyMessagingService(localNode.tcpPort());
// FIXME: workaround until it becomes a service.
try {
@@ -92,8 +81,9 @@
}
@Override
- public boolean broadcast(ClusterMessage message) {
+ public boolean broadcast(ClusterMessage message) throws IOException {
boolean ok = true;
+ final ControllerNode localNode = clusterService.getLocalNode();
for (ControllerNode node : clusterService.getNodes()) {
if (!node.equals(localNode)) {
ok = unicast(message, node.id()) && ok;
@@ -103,8 +93,9 @@
}
@Override
- public boolean multicast(ClusterMessage message, Set<NodeId> nodes) {
+ public boolean multicast(ClusterMessage message, Set<NodeId> nodes) throws IOException {
boolean ok = true;
+ final ControllerNode localNode = clusterService.getLocalNode();
for (NodeId nodeId : nodes) {
if (!nodeId.equals(localNode.id())) {
ok = unicast(message, nodeId) && ok;
@@ -114,7 +105,7 @@
}
@Override
- public boolean unicast(ClusterMessage message, NodeId toNodeId) {
+ public boolean unicast(ClusterMessage message, NodeId toNodeId) throws IOException {
ControllerNode node = clusterService.getNode(toNodeId);
checkArgument(node != null, "Unknown nodeId: %s", toNodeId);
Endpoint nodeEp = new Endpoint(node.ip().toString(), node.tcpPort());
@@ -124,9 +115,8 @@
return true;
} catch (IOException e) {
log.error("Failed to send cluster message to nodeId: " + toNodeId, e);
+ throw e;
}
-
- return false;
}
@Override
@@ -135,61 +125,6 @@
messagingService.registerHandler(subject.value(), new InternalClusterMessageHandler(subscriber));
}
- @Override
- public void initialize(ControllerNode localNode,
- ClusterNodesDelegate delegate) {
- this.localNode = localNode;
- this.nodesDelegate = delegate;
- this.addSubscriber(new MessageSubject("CLUSTER_MEMBERSHIP_EVENT"), new ClusterMemebershipEventHandler());
- timer.schedule(new KeepAlive(), 0, HEART_BEAT_INTERVAL_MILLIS);
- }
-
- @Override
- public void addNode(ControllerNode node) {
- //members.put(node.id(), node);
- }
-
- @Override
- public void removeNode(ControllerNode node) {
- broadcast(new ClusterMessage(
- localNode.id(),
- new MessageSubject("CLUSTER_MEMBERSHIP_EVENT"),
- SERIALIZER.encode(new ClusterMembershipEvent(ClusterMembershipEventType.LEAVING_MEMBER, node))));
- //members.remove(node.id());
- }
-
- // Sends a heart beat to all peers.
- private class KeepAlive extends TimerTask {
-
- @Override
- public void run() {
- broadcast(new ClusterMessage(
- localNode.id(),
- new MessageSubject("CLUSTER_MEMBERSHIP_EVENT"),
- SERIALIZER.encode(new ClusterMembershipEvent(ClusterMembershipEventType.HEART_BEAT, localNode))));
- }
- }
-
- private class ClusterMemebershipEventHandler implements ClusterMessageHandler {
-
- @Override
- public void handle(ClusterMessage message) {
-
- ClusterMembershipEvent event = SERIALIZER.decode(message.payload());
- ControllerNode node = event.node();
- if (event.type() == ClusterMembershipEventType.HEART_BEAT) {
- log.info("Node {} sent a hearbeat", node.id());
- nodesDelegate.nodeDetected(node.id(), node.ip(), node.tcpPort());
- } else if (event.type() == ClusterMembershipEventType.LEAVING_MEMBER) {
- log.info("Node {} is leaving", node.id());
- nodesDelegate.nodeRemoved(node.id());
- } else if (event.type() == ClusterMembershipEventType.UNREACHABLE_MEMBER) {
- log.info("Node {} is unreachable", node.id());
- nodesDelegate.nodeVanished(node.id());
- }
- }
- }
-
private final class InternalClusterMessageHandler implements MessageHandler {
private final ClusterMessageHandler handler;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/MessageSerializer.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/MessageSerializer.java
deleted file mode 100644
index 07a97bc..0000000
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/MessageSerializer.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package org.onlab.onos.store.cluster.messaging.impl;
-
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Service;
-import org.onlab.onos.store.cluster.messaging.MessageSubject;
-import org.onlab.onos.store.cluster.messaging.SerializationService;
-import org.onlab.onos.store.serializers.KryoPoolUtil;
-import org.onlab.util.KryoPool;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-//FIXME: not used any more? remove
-/**
- * Factory for parsing messages sent between cluster members.
- */
-@Component(immediate = true)
-@Service
-public class MessageSerializer implements SerializationService {
-
- private final Logger log = LoggerFactory.getLogger(getClass());
-
- private static final int METADATA_LENGTH = 12; // 8 + 4
- private static final int LENGTH_OFFSET = 8;
-
- private static final long MARKER = 0xfeedcafebeaddeadL;
-
- private KryoPool serializerPool;
-
- @Activate
- public void activate() {
- setupKryoPool();
- log.info("Started");
- }
-
- @Deactivate
- public void deactivate() {
- log.info("Stopped");
- }
-
- /**
- * Sets up the common serialzers pool.
- */
- protected void setupKryoPool() {
- serializerPool = KryoPool.newBuilder()
- .register(KryoPoolUtil.API)
- // TODO: Should MessageSubject be in API bundle?
- .register(MessageSubject.class)
- .build()
- .populate(1);
- }
-
-
- @Override
- public <T> T decode(byte[] data) {
- return serializerPool.deserialize(data);
- }
-
- @Override
- public byte[] encode(Object payload) {
- return serializerPool.serialize(payload);
- }
-}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStore.java
index f5f1d3e..d923075 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStore.java
@@ -86,14 +86,11 @@
private final Logger log = getLogger(getClass());
- public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
+ private static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
- // TODO: Check if inner Map can be replaced with plain Map.
// innerMap is used to lock a Device, thus instance should never be replaced.
-
// collection of Description given from various providers
- private final ConcurrentMap<DeviceId,
- ConcurrentMap<ProviderId, DeviceDescriptions>>
+ private final ConcurrentMap<DeviceId, Map<ProviderId, DeviceDescriptions>>
deviceDescs = Maps.newConcurrentMap();
// cache of Device and Ports generated by compositing descriptions from providers
@@ -208,9 +205,9 @@
final Timestamped<DeviceDescription> deltaDesc = new Timestamped<>(deviceDescription, newTimestamp);
final DeviceEvent event;
final Timestamped<DeviceDescription> mergedDesc;
- synchronized (getDeviceDescriptions(deviceId)) {
+ synchronized (getOrCreateDeviceDescriptionsMap(deviceId)) {
event = createOrUpdateDeviceInternal(providerId, deviceId, deltaDesc);
- mergedDesc = getDeviceDescriptions(deviceId).get(providerId).getDeviceDesc();
+ mergedDesc = getOrCreateDeviceDescriptionsMap(deviceId).get(providerId).getDeviceDesc();
}
if (event != null) {
log.info("Notifying peers of a device update topology event for providerId: {} and deviceId: {}",
@@ -230,8 +227,8 @@
Timestamped<DeviceDescription> deltaDesc) {
// Collection of DeviceDescriptions for a Device
- ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
- = getDeviceDescriptions(deviceId);
+ Map<ProviderId, DeviceDescriptions> providerDescs
+ = getOrCreateDeviceDescriptionsMap(deviceId);
synchronized (providerDescs) {
// locking per device
@@ -241,9 +238,7 @@
return null;
}
- DeviceDescriptions descs
- = createIfAbsentUnchecked(providerDescs, providerId,
- new InitDeviceDescs(deltaDesc));
+ DeviceDescriptions descs = getOrCreateProviderDeviceDescriptions(providerDescs, providerId, deltaDesc);
final Device oldDevice = devices.get(deviceId);
final Device newDevice;
@@ -338,7 +333,7 @@
private DeviceEvent markOfflineInternal(DeviceId deviceId, Timestamp timestamp) {
Map<ProviderId, DeviceDescriptions> providerDescs
- = getDeviceDescriptions(deviceId);
+ = getOrCreateDeviceDescriptionsMap(deviceId);
// locking device
synchronized (providerDescs) {
@@ -401,9 +396,9 @@
final List<DeviceEvent> events;
final Timestamped<List<PortDescription>> merged;
- synchronized (getDeviceDescriptions(deviceId)) {
+ synchronized (getOrCreateDeviceDescriptionsMap(deviceId)) {
events = updatePortsInternal(providerId, deviceId, timestampedInput);
- final DeviceDescriptions descs = getDeviceDescriptions(deviceId).get(providerId);
+ final DeviceDescriptions descs = getOrCreateDeviceDescriptionsMap(deviceId).get(providerId);
List<PortDescription> mergedList =
FluentIterable.from(portDescriptions)
.transform(new Function<PortDescription, PortDescription>() {
@@ -435,7 +430,7 @@
Device device = devices.get(deviceId);
checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
- ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
+ Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
List<DeviceEvent> events = new ArrayList<>();
@@ -539,10 +534,34 @@
NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
}
- private ConcurrentMap<ProviderId, DeviceDescriptions> getDeviceDescriptions(
+ private Map<ProviderId, DeviceDescriptions> getOrCreateDeviceDescriptionsMap(
DeviceId deviceId) {
- return createIfAbsentUnchecked(deviceDescs, deviceId,
- NewConcurrentHashMap.<ProviderId, DeviceDescriptions>ifNeeded());
+ Map<ProviderId, DeviceDescriptions> r;
+ r = deviceDescs.get(deviceId);
+ if (r == null) {
+ r = new HashMap<ProviderId, DeviceDescriptions>();
+ final Map<ProviderId, DeviceDescriptions> concurrentlyAdded;
+ concurrentlyAdded = deviceDescs.putIfAbsent(deviceId, r);
+ if (concurrentlyAdded != null) {
+ r = concurrentlyAdded;
+ }
+ }
+ return r;
+ }
+
+ // Guarded by deviceDescs value (=Device lock)
+ private DeviceDescriptions getOrCreateProviderDeviceDescriptions(
+ Map<ProviderId, DeviceDescriptions> device,
+ ProviderId providerId, Timestamped<DeviceDescription> deltaDesc) {
+
+ synchronized (device) {
+ DeviceDescriptions r = device.get(providerId);
+ if (r == null) {
+ r = new DeviceDescriptions(deltaDesc);
+ device.put(providerId, r);
+ }
+ return r;
+ }
}
@Override
@@ -555,9 +574,9 @@
= new Timestamped<>(portDescription, newTimestamp);
final DeviceEvent event;
final Timestamped<PortDescription> mergedDesc;
- synchronized (getDeviceDescriptions(deviceId)) {
+ synchronized (getOrCreateDeviceDescriptionsMap(deviceId)) {
event = updatePortStatusInternal(providerId, deviceId, deltaDesc);
- mergedDesc = getDeviceDescriptions(deviceId).get(providerId)
+ mergedDesc = getOrCreateDeviceDescriptionsMap(deviceId).get(providerId)
.getPortDesc(portDescription.portNumber());
}
if (event != null) {
@@ -579,7 +598,7 @@
Device device = devices.get(deviceId);
checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
- ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
+ Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
synchronized (descsMap) {
@@ -591,7 +610,7 @@
DeviceDescriptions descs = descsMap.get(providerId);
// assuming all providers must to give DeviceDescription
- checkArgument(descs != null,
+ verify(descs != null,
"Device description for Device ID %s from Provider %s was not found",
deviceId, providerId);
@@ -661,7 +680,7 @@
private DeviceEvent removeDeviceInternal(DeviceId deviceId,
Timestamp timestamp) {
- Map<ProviderId, DeviceDescriptions> descs = getDeviceDescriptions(deviceId);
+ Map<ProviderId, DeviceDescriptions> descs = getOrCreateDeviceDescriptionsMap(deviceId);
synchronized (descs) {
// accept removal request if given timestamp is newer than
// the latest Timestamp from Primary provider
@@ -751,14 +770,14 @@
*
* @param device device the port is on
* @param number port number
- * @param providerDescs Collection of Descriptions from multiple providers
+ * @param descsMap Collection of Descriptions from multiple providers
* @return Port instance
*/
private Port composePort(Device device, PortNumber number,
- ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
+ Map<ProviderId, DeviceDescriptions> descsMap) {
- ProviderId primary = pickPrimaryPID(providerDescs);
- DeviceDescriptions primDescs = providerDescs.get(primary);
+ ProviderId primary = pickPrimaryPID(descsMap);
+ DeviceDescriptions primDescs = descsMap.get(primary);
// if no primary, assume not enabled
// TODO: revisit this default port enabled/disabled behavior
boolean isEnabled = false;
@@ -770,7 +789,7 @@
annotations = merge(annotations, portDesc.value().annotations());
}
- for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
+ for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
if (e.getKey().equals(primary)) {
continue;
}
@@ -893,41 +912,48 @@
private DeviceAntiEntropyAdvertisement createAdvertisement() {
final NodeId self = clusterService.getLocalNode().id();
- Map<DeviceFragmentId, Timestamp> devices = new HashMap<>(deviceDescs.size());
- final int portsPerDevice = 8; // random guess to minimize reallocation
- Map<PortFragmentId, Timestamp> ports = new HashMap<>(devices.size() * portsPerDevice);
- Map<DeviceId, Timestamp> offline = new HashMap<>(devices.size());
+ final int numDevices = deviceDescs.size();
+ Map<DeviceFragmentId, Timestamp> adDevices = new HashMap<>(numDevices);
+ final int portsPerDevice = 8; // random factor to minimize reallocation
+ Map<PortFragmentId, Timestamp> adPorts = new HashMap<>(numDevices * portsPerDevice);
+ Map<DeviceId, Timestamp> adOffline = new HashMap<>(numDevices);
- for (Entry<DeviceId, ConcurrentMap<ProviderId, DeviceDescriptions>>
+ for (Entry<DeviceId, Map<ProviderId, DeviceDescriptions>>
provs : deviceDescs.entrySet()) {
+ // for each Device...
final DeviceId deviceId = provs.getKey();
- final ConcurrentMap<ProviderId, DeviceDescriptions> devDescs = provs.getValue();
+ final Map<ProviderId, DeviceDescriptions> devDescs = provs.getValue();
synchronized (devDescs) {
- offline.put(deviceId, this.offline.get(deviceId));
+ // send device offline timestamp
+ Timestamp lOffline = this.offline.get(deviceId);
+ if (lOffline != null) {
+ adOffline.put(deviceId, lOffline);
+ }
for (Entry<ProviderId, DeviceDescriptions>
prov : devDescs.entrySet()) {
+ // for each Provider Descriptions...
final ProviderId provId = prov.getKey();
final DeviceDescriptions descs = prov.getValue();
- devices.put(new DeviceFragmentId(deviceId, provId),
+ adDevices.put(new DeviceFragmentId(deviceId, provId),
descs.getDeviceDesc().timestamp());
for (Entry<PortNumber, Timestamped<PortDescription>>
portDesc : descs.getPortDescs().entrySet()) {
final PortNumber number = portDesc.getKey();
- ports.put(new PortFragmentId(deviceId, provId, number),
+ adPorts.put(new PortFragmentId(deviceId, provId, number),
portDesc.getValue().timestamp());
}
}
}
}
- return new DeviceAntiEntropyAdvertisement(self, devices, ports, offline);
+ return new DeviceAntiEntropyAdvertisement(self, adDevices, adPorts, adOffline);
}
/**
@@ -950,7 +976,7 @@
Collection<DeviceFragmentId> reqDevices = new ArrayList<>();
Collection<PortFragmentId> reqPorts = new ArrayList<>();
- for (Entry<DeviceId, ConcurrentMap<ProviderId, DeviceDescriptions>> de : deviceDescs.entrySet()) {
+ for (Entry<DeviceId, Map<ProviderId, DeviceDescriptions>> de : deviceDescs.entrySet()) {
final DeviceId deviceId = de.getKey();
final Map<ProviderId, DeviceDescriptions> lDevice = de.getValue();
@@ -1199,7 +1225,7 @@
@Override
public void handle(ClusterMessage message) {
- log.info("Received Device advertisement from peer: {}", message.sender());
+ log.debug("Received Device Anti-Entropy advertisement from peer: {}", message.sender());
DeviceAntiEntropyAdvertisement advertisement = SERIALIZER.decode(message.payload());
handleAdvertisement(advertisement);
}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/host/impl/DistributedHostStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/host/impl/DistributedHostStore.java
deleted file mode 100644
index 9362156..0000000
--- a/core/store/dist/src/main/java/org/onlab/onos/store/host/impl/DistributedHostStore.java
+++ /dev/null
@@ -1,302 +0,0 @@
-package org.onlab.onos.store.host.impl;
-
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Service;
-import org.onlab.onos.net.Annotations;
-import org.onlab.onos.net.ConnectPoint;
-import org.onlab.onos.net.DefaultHost;
-import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.Host;
-import org.onlab.onos.net.HostId;
-import org.onlab.onos.net.HostLocation;
-import org.onlab.onos.net.host.HostDescription;
-import org.onlab.onos.net.host.HostEvent;
-import org.onlab.onos.net.host.HostStore;
-import org.onlab.onos.net.host.HostStoreDelegate;
-import org.onlab.onos.net.host.PortAddresses;
-import org.onlab.onos.net.provider.ProviderId;
-import org.onlab.onos.store.AbstractStore;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.slf4j.Logger;
-
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import static org.onlab.onos.net.host.HostEvent.Type.*;
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * Manages inventory of end-station hosts using trivial in-memory
- * implementation.
- */
-//FIXME: I LIE I AM NOT DISTRIBUTED
-@Component(immediate = true)
-@Service
-public class DistributedHostStore
- extends AbstractStore<HostEvent, HostStoreDelegate>
- implements HostStore {
-
- private final Logger log = getLogger(getClass());
-
- // Host inventory
- private final Map<HostId, StoredHost> hosts = new ConcurrentHashMap<>(2000000, 0.75f, 16);
-
- // Hosts tracked by their location
- private final Multimap<ConnectPoint, Host> locations = HashMultimap.create();
-
- private final Map<ConnectPoint, PortAddresses> portAddresses =
- new ConcurrentHashMap<>();
-
- @Activate
- public void activate() {
- log.info("Started");
- }
-
- @Deactivate
- public void deactivate() {
- log.info("Stopped");
- }
-
- @Override
- public HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId,
- HostDescription hostDescription) {
- StoredHost host = hosts.get(hostId);
- if (host == null) {
- return createHost(providerId, hostId, hostDescription);
- }
- return updateHost(providerId, host, hostDescription);
- }
-
- // creates a new host and sends HOST_ADDED
- private HostEvent createHost(ProviderId providerId, HostId hostId,
- HostDescription descr) {
- StoredHost newhost = new StoredHost(providerId, hostId,
- descr.hwAddress(),
- descr.vlan(),
- descr.location(),
- ImmutableSet.of(descr.ipAddress()));
- synchronized (this) {
- hosts.put(hostId, newhost);
- locations.put(descr.location(), newhost);
- }
- return new HostEvent(HOST_ADDED, newhost);
- }
-
- // checks for type of update to host, sends appropriate event
- private HostEvent updateHost(ProviderId providerId, StoredHost host,
- HostDescription descr) {
- HostEvent event;
- if (!host.location().equals(descr.location())) {
- host.setLocation(descr.location());
- return new HostEvent(HOST_MOVED, host);
- }
-
- if (host.ipAddresses().contains(descr.ipAddress())) {
- return null;
- }
-
- Set<IpPrefix> addresses = new HashSet<>(host.ipAddresses());
- addresses.add(descr.ipAddress());
- StoredHost updated = new StoredHost(providerId, host.id(),
- host.mac(), host.vlan(),
- descr.location(), addresses);
- event = new HostEvent(HOST_UPDATED, updated);
- synchronized (this) {
- hosts.put(host.id(), updated);
- locations.remove(host.location(), host);
- locations.put(updated.location(), updated);
- }
- return event;
- }
-
- @Override
- public HostEvent removeHost(HostId hostId) {
- synchronized (this) {
- Host host = hosts.remove(hostId);
- if (host != null) {
- locations.remove((host.location()), host);
- return new HostEvent(HOST_REMOVED, host);
- }
- return null;
- }
- }
-
- @Override
- public int getHostCount() {
- return hosts.size();
- }
-
- @Override
- public Iterable<Host> getHosts() {
- return ImmutableSet.<Host>copyOf(hosts.values());
- }
-
- @Override
- public Host getHost(HostId hostId) {
- return hosts.get(hostId);
- }
-
- @Override
- public Set<Host> getHosts(VlanId vlanId) {
- Set<Host> vlanset = new HashSet<>();
- for (Host h : hosts.values()) {
- if (h.vlan().equals(vlanId)) {
- vlanset.add(h);
- }
- }
- return vlanset;
- }
-
- @Override
- public Set<Host> getHosts(MacAddress mac) {
- Set<Host> macset = new HashSet<>();
- for (Host h : hosts.values()) {
- if (h.mac().equals(mac)) {
- macset.add(h);
- }
- }
- return macset;
- }
-
- @Override
- public Set<Host> getHosts(IpPrefix ip) {
- Set<Host> ipset = new HashSet<>();
- for (Host h : hosts.values()) {
- if (h.ipAddresses().contains(ip)) {
- ipset.add(h);
- }
- }
- return ipset;
- }
-
- @Override
- public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
- return ImmutableSet.copyOf(locations.get(connectPoint));
- }
-
- @Override
- public Set<Host> getConnectedHosts(DeviceId deviceId) {
- Set<Host> hostset = new HashSet<>();
- for (ConnectPoint p : locations.keySet()) {
- if (p.deviceId().equals(deviceId)) {
- hostset.addAll(locations.get(p));
- }
- }
- return hostset;
- }
-
- @Override
- public void updateAddressBindings(PortAddresses addresses) {
- synchronized (portAddresses) {
- PortAddresses existing = portAddresses.get(addresses.connectPoint());
- if (existing == null) {
- portAddresses.put(addresses.connectPoint(), addresses);
- } else {
- Set<IpPrefix> union = Sets.union(existing.ips(), addresses.ips())
- .immutableCopy();
-
- MacAddress newMac = (addresses.mac() == null) ? existing.mac()
- : addresses.mac();
-
- PortAddresses newAddresses =
- new PortAddresses(addresses.connectPoint(), union, newMac);
-
- portAddresses.put(newAddresses.connectPoint(), newAddresses);
- }
- }
- }
-
- @Override
- public void removeAddressBindings(PortAddresses addresses) {
- synchronized (portAddresses) {
- PortAddresses existing = portAddresses.get(addresses.connectPoint());
- if (existing != null) {
- Set<IpPrefix> difference =
- Sets.difference(existing.ips(), addresses.ips()).immutableCopy();
-
- // If they removed the existing mac, set the new mac to null.
- // Otherwise, keep the existing mac.
- MacAddress newMac = existing.mac();
- if (addresses.mac() != null && addresses.mac().equals(existing.mac())) {
- newMac = null;
- }
-
- PortAddresses newAddresses =
- new PortAddresses(addresses.connectPoint(), difference, newMac);
-
- portAddresses.put(newAddresses.connectPoint(), newAddresses);
- }
- }
- }
-
- @Override
- public void clearAddressBindings(ConnectPoint connectPoint) {
- synchronized (portAddresses) {
- portAddresses.remove(connectPoint);
- }
- }
-
- @Override
- public Set<PortAddresses> getAddressBindings() {
- synchronized (portAddresses) {
- return new HashSet<>(portAddresses.values());
- }
- }
-
- @Override
- public PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint) {
- PortAddresses addresses;
-
- synchronized (portAddresses) {
- addresses = portAddresses.get(connectPoint);
- }
-
- if (addresses == null) {
- addresses = new PortAddresses(connectPoint, null, null);
- }
-
- return addresses;
- }
-
- // Auxiliary extension to allow location to mutate.
- private class StoredHost extends DefaultHost {
- private HostLocation location;
-
- /**
- * Creates an end-station host using the supplied information.
- *
- * @param providerId provider identity
- * @param id host identifier
- * @param mac host MAC address
- * @param vlan host VLAN identifier
- * @param location host location
- * @param ips host IP addresses
- * @param annotations optional key/value annotations
- */
- public StoredHost(ProviderId providerId, HostId id,
- MacAddress mac, VlanId vlan, HostLocation location,
- Set<IpPrefix> ips, Annotations... annotations) {
- super(providerId, id, mac, vlan, location, ips, annotations);
- this.location = location;
- }
-
- void setLocation(HostLocation location) {
- this.location = location;
- }
-
- @Override
- public HostLocation location() {
- return location;
- }
- }
-}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/host/impl/GossipHostStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/host/impl/GossipHostStore.java
new file mode 100644
index 0000000..39bc770
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/host/impl/GossipHostStore.java
@@ -0,0 +1,437 @@
+package org.onlab.onos.store.host.impl;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.cluster.ClusterService;
+import org.onlab.onos.net.Annotations;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DefaultHost;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.Host;
+import org.onlab.onos.net.HostId;
+import org.onlab.onos.net.HostLocation;
+import org.onlab.onos.net.host.HostClockService;
+import org.onlab.onos.net.host.HostDescription;
+import org.onlab.onos.net.host.HostEvent;
+import org.onlab.onos.net.host.HostStore;
+import org.onlab.onos.net.host.HostStoreDelegate;
+import org.onlab.onos.net.host.PortAddresses;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.store.AbstractStore;
+import org.onlab.onos.store.Timestamp;
+import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
+import org.onlab.onos.store.cluster.messaging.ClusterMessage;
+import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
+import org.onlab.onos.store.cluster.messaging.MessageSubject;
+import org.onlab.onos.store.common.impl.Timestamped;
+import org.onlab.onos.store.serializers.DistributedStoreSerializers;
+import org.onlab.onos.store.serializers.KryoSerializer;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onlab.util.KryoPool;
+import org.slf4j.Logger;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.onlab.onos.net.host.HostEvent.Type.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+//TODO: multi-provider, annotation not supported.
+/**
+ * Manages inventory of end-station hosts in distributed data store
+ * that uses optimistic replication and gossip based techniques.
+ */
+@Component(immediate = true)
+@Service
+public class GossipHostStore
+ extends AbstractStore<HostEvent, HostStoreDelegate>
+ implements HostStore {
+
+ private final Logger log = getLogger(getClass());
+
+ // Host inventory
+ private final Map<HostId, StoredHost> hosts = new ConcurrentHashMap<>(2000000, 0.75f, 16);
+
+ private final Map<HostId, Timestamped<Host>> removedHosts = new ConcurrentHashMap<>(2000000, 0.75f, 16);
+
+ // Hosts tracked by their location
+ private final Multimap<ConnectPoint, Host> locations = HashMultimap.create();
+
+ private final Map<ConnectPoint, PortAddresses> portAddresses =
+ new ConcurrentHashMap<>();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostClockService hostClockService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterCommunicationService clusterCommunicator;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
+ private static final KryoSerializer SERIALIZER = new KryoSerializer() {
+ @Override
+ protected void setupKryoPool() {
+ serializerPool = KryoPool.newBuilder()
+ .register(DistributedStoreSerializers.COMMON)
+ .register(InternalHostRemovedEvent.class)
+ .build()
+ .populate(1);
+ }
+ };
+
+ @Activate
+ public void activate() {
+ clusterCommunicator.addSubscriber(
+ GossipHostStoreMessageSubjects.HOST_UPDATED, new InternalHostEventListener());
+ clusterCommunicator.addSubscriber(
+ GossipHostStoreMessageSubjects.HOST_REMOVED, new InternalHostRemovedEventListener());
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ log.info("Stopped");
+ }
+
+ @Override
+ public HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId,
+ HostDescription hostDescription) {
+ Timestamp timestamp = hostClockService.getTimestamp(hostId);
+ HostEvent event = createOrUpdateHostInternal(providerId, hostId, hostDescription, timestamp);
+ if (event != null) {
+ log.info("Notifying peers of a host topology event for providerId: "
+ + "{}; hostId: {}; hostDescription: {}", providerId, hostId, hostDescription);
+ try {
+ notifyPeers(new InternalHostEvent(providerId, hostId, hostDescription, timestamp));
+ } catch (IOException e) {
+ log.error("Failed to notify peers of a host topology event for providerId: "
+ + "{}; hostId: {}; hostDescription: {}", providerId, hostId, hostDescription);
+ }
+ }
+ return event;
+ }
+
+ private HostEvent createOrUpdateHostInternal(ProviderId providerId, HostId hostId,
+ HostDescription hostDescription, Timestamp timestamp) {
+ StoredHost host = hosts.get(hostId);
+ if (host == null) {
+ return createHost(providerId, hostId, hostDescription, timestamp);
+ }
+ return updateHost(providerId, host, hostDescription, timestamp);
+ }
+
+ // creates a new host and sends HOST_ADDED
+ private HostEvent createHost(ProviderId providerId, HostId hostId,
+ HostDescription descr, Timestamp timestamp) {
+ synchronized (this) {
+ // If this host was previously removed, first ensure
+ // this new request is "newer"
+ if (removedHosts.containsKey(hostId)) {
+ if (removedHosts.get(hostId).isNewer(timestamp)) {
+ return null;
+ } else {
+ removedHosts.remove(hostId);
+ }
+ }
+ StoredHost newhost = new StoredHost(providerId, hostId,
+ descr.hwAddress(),
+ descr.vlan(),
+ new Timestamped<>(descr.location(), timestamp),
+ ImmutableSet.of(descr.ipAddress()));
+ hosts.put(hostId, newhost);
+ locations.put(descr.location(), newhost);
+ return new HostEvent(HOST_ADDED, newhost);
+ }
+ }
+
+ // checks for type of update to host, sends appropriate event
+ private HostEvent updateHost(ProviderId providerId, StoredHost host,
+ HostDescription descr, Timestamp timestamp) {
+ HostEvent event;
+ if (!host.location.isNewer(timestamp) && !host.location().equals(descr.location())) {
+ host.setLocation(new Timestamped<>(descr.location(), timestamp));
+ return new HostEvent(HOST_MOVED, host);
+ }
+
+ if (host.ipAddresses().contains(descr.ipAddress())) {
+ return null;
+ }
+
+ Set<IpPrefix> addresses = new HashSet<>(host.ipAddresses());
+ addresses.add(descr.ipAddress());
+ StoredHost updated = new StoredHost(providerId, host.id(),
+ host.mac(), host.vlan(),
+ host.location, addresses);
+ event = new HostEvent(HOST_UPDATED, updated);
+ synchronized (this) {
+ hosts.put(host.id(), updated);
+ locations.remove(host.location(), host);
+ locations.put(updated.location(), updated);
+ }
+ return event;
+ }
+
+ @Override
+ public HostEvent removeHost(HostId hostId) {
+ Timestamp timestamp = hostClockService.getTimestamp(hostId);
+ HostEvent event = removeHostInternal(hostId, timestamp);
+ if (event != null) {
+ log.info("Notifying peers of a host removed topology event for hostId: {}", hostId);
+ try {
+ notifyPeers(new InternalHostRemovedEvent(hostId, timestamp));
+ } catch (IOException e) {
+ log.info("Failed to notify peers of a host removed topology event for hostId: {}", hostId);
+ }
+ }
+ return event;
+ }
+
+ private HostEvent removeHostInternal(HostId hostId, Timestamp timestamp) {
+ synchronized (this) {
+ Host host = hosts.remove(hostId);
+ if (host != null) {
+ locations.remove((host.location()), host);
+ removedHosts.put(hostId, new Timestamped<>(host, timestamp));
+ return new HostEvent(HOST_REMOVED, host);
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public int getHostCount() {
+ return hosts.size();
+ }
+
+ @Override
+ public Iterable<Host> getHosts() {
+ return ImmutableSet.<Host>copyOf(hosts.values());
+ }
+
+ @Override
+ public Host getHost(HostId hostId) {
+ return hosts.get(hostId);
+ }
+
+ @Override
+ public Set<Host> getHosts(VlanId vlanId) {
+ Set<Host> vlanset = new HashSet<>();
+ for (Host h : hosts.values()) {
+ if (h.vlan().equals(vlanId)) {
+ vlanset.add(h);
+ }
+ }
+ return vlanset;
+ }
+
+ @Override
+ public Set<Host> getHosts(MacAddress mac) {
+ Set<Host> macset = new HashSet<>();
+ for (Host h : hosts.values()) {
+ if (h.mac().equals(mac)) {
+ macset.add(h);
+ }
+ }
+ return macset;
+ }
+
+ @Override
+ public Set<Host> getHosts(IpPrefix ip) {
+ Set<Host> ipset = new HashSet<>();
+ for (Host h : hosts.values()) {
+ if (h.ipAddresses().contains(ip)) {
+ ipset.add(h);
+ }
+ }
+ return ipset;
+ }
+
+ @Override
+ public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
+ return ImmutableSet.copyOf(locations.get(connectPoint));
+ }
+
+ @Override
+ public Set<Host> getConnectedHosts(DeviceId deviceId) {
+ Set<Host> hostset = new HashSet<>();
+ for (ConnectPoint p : locations.keySet()) {
+ if (p.deviceId().equals(deviceId)) {
+ hostset.addAll(locations.get(p));
+ }
+ }
+ return hostset;
+ }
+
+ @Override
+ public void updateAddressBindings(PortAddresses addresses) {
+ synchronized (portAddresses) {
+ PortAddresses existing = portAddresses.get(addresses.connectPoint());
+ if (existing == null) {
+ portAddresses.put(addresses.connectPoint(), addresses);
+ } else {
+ Set<IpPrefix> union = Sets.union(existing.ips(), addresses.ips())
+ .immutableCopy();
+
+ MacAddress newMac = (addresses.mac() == null) ? existing.mac()
+ : addresses.mac();
+
+ PortAddresses newAddresses =
+ new PortAddresses(addresses.connectPoint(), union, newMac);
+
+ portAddresses.put(newAddresses.connectPoint(), newAddresses);
+ }
+ }
+ }
+
+ @Override
+ public void removeAddressBindings(PortAddresses addresses) {
+ synchronized (portAddresses) {
+ PortAddresses existing = portAddresses.get(addresses.connectPoint());
+ if (existing != null) {
+ Set<IpPrefix> difference =
+ Sets.difference(existing.ips(), addresses.ips()).immutableCopy();
+
+ // If they removed the existing mac, set the new mac to null.
+ // Otherwise, keep the existing mac.
+ MacAddress newMac = existing.mac();
+ if (addresses.mac() != null && addresses.mac().equals(existing.mac())) {
+ newMac = null;
+ }
+
+ PortAddresses newAddresses =
+ new PortAddresses(addresses.connectPoint(), difference, newMac);
+
+ portAddresses.put(newAddresses.connectPoint(), newAddresses);
+ }
+ }
+ }
+
+ @Override
+ public void clearAddressBindings(ConnectPoint connectPoint) {
+ synchronized (portAddresses) {
+ portAddresses.remove(connectPoint);
+ }
+ }
+
+ @Override
+ public Set<PortAddresses> getAddressBindings() {
+ synchronized (portAddresses) {
+ return new HashSet<>(portAddresses.values());
+ }
+ }
+
+ @Override
+ public PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint) {
+ PortAddresses addresses;
+
+ synchronized (portAddresses) {
+ addresses = portAddresses.get(connectPoint);
+ }
+
+ if (addresses == null) {
+ addresses = new PortAddresses(connectPoint, null, null);
+ }
+
+ return addresses;
+ }
+
+ // Auxiliary extension to allow location to mutate.
+ private class StoredHost extends DefaultHost {
+ private Timestamped<HostLocation> location;
+
+ /**
+ * Creates an end-station host using the supplied information.
+ *
+ * @param providerId provider identity
+ * @param id host identifier
+ * @param mac host MAC address
+ * @param vlan host VLAN identifier
+ * @param location host location
+ * @param ips host IP addresses
+ * @param annotations optional key/value annotations
+ */
+ public StoredHost(ProviderId providerId, HostId id,
+ MacAddress mac, VlanId vlan, Timestamped<HostLocation> location,
+ Set<IpPrefix> ips, Annotations... annotations) {
+ super(providerId, id, mac, vlan, location.value(), ips, annotations);
+ this.location = location;
+ }
+
+ void setLocation(Timestamped<HostLocation> location) {
+ this.location = location;
+ }
+
+ @Override
+ public HostLocation location() {
+ return location.value();
+ }
+ }
+
+ private void notifyPeers(InternalHostRemovedEvent event) throws IOException {
+ broadcastMessage(GossipHostStoreMessageSubjects.HOST_REMOVED, event);
+ }
+
+ private void notifyPeers(InternalHostEvent event) throws IOException {
+ broadcastMessage(GossipHostStoreMessageSubjects.HOST_UPDATED, event);
+ }
+
+ private void broadcastMessage(MessageSubject subject, Object event) throws IOException {
+ ClusterMessage message = new ClusterMessage(
+ clusterService.getLocalNode().id(),
+ subject,
+ SERIALIZER.encode(event));
+ clusterCommunicator.broadcast(message);
+ }
+
+ private void notifyDelegateIfNotNull(HostEvent event) {
+ if (event != null) {
+ notifyDelegate(event);
+ }
+ }
+
+ private class InternalHostEventListener implements ClusterMessageHandler {
+ @Override
+ public void handle(ClusterMessage message) {
+
+ log.info("Received host update event from peer: {}", message.sender());
+ InternalHostEvent event = (InternalHostEvent) SERIALIZER.decode(message.payload());
+
+ ProviderId providerId = event.providerId();
+ HostId hostId = event.hostId();
+ HostDescription hostDescription = event.hostDescription();
+ Timestamp timestamp = event.timestamp();
+
+ notifyDelegateIfNotNull(createOrUpdateHostInternal(providerId, hostId, hostDescription, timestamp));
+ }
+ }
+
+ private class InternalHostRemovedEventListener implements ClusterMessageHandler {
+ @Override
+ public void handle(ClusterMessage message) {
+
+ log.info("Received host removed event from peer: {}", message.sender());
+ InternalHostRemovedEvent event = (InternalHostRemovedEvent) SERIALIZER.decode(message.payload());
+
+ HostId hostId = event.hostId();
+ Timestamp timestamp = event.timestamp();
+
+ notifyDelegateIfNotNull(removeHostInternal(hostId, timestamp));
+ }
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/host/impl/GossipHostStoreMessageSubjects.java b/core/store/dist/src/main/java/org/onlab/onos/store/host/impl/GossipHostStoreMessageSubjects.java
new file mode 100644
index 0000000..27cf4ce
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/host/impl/GossipHostStoreMessageSubjects.java
@@ -0,0 +1,9 @@
+package org.onlab.onos.store.host.impl;
+
+import org.onlab.onos.store.cluster.messaging.MessageSubject;
+
+public final class GossipHostStoreMessageSubjects {
+ private GossipHostStoreMessageSubjects() {}
+ public static final MessageSubject HOST_UPDATED = new MessageSubject("peer-host-updated");
+ public static final MessageSubject HOST_REMOVED = new MessageSubject("peer-host-removed");
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/host/impl/InternalHostEvent.java b/core/store/dist/src/main/java/org/onlab/onos/store/host/impl/InternalHostEvent.java
new file mode 100644
index 0000000..f5ca63e
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/host/impl/InternalHostEvent.java
@@ -0,0 +1,51 @@
+package org.onlab.onos.store.host.impl;
+
+import org.onlab.onos.net.HostId;
+import org.onlab.onos.net.host.HostDescription;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.store.Timestamp;
+
+/**
+ * Information published by GossipHostStore to notify peers of a host
+ * change (create/update) event.
+ */
+public class InternalHostEvent {
+
+ private final ProviderId providerId;
+ private final HostId hostId;
+ private final HostDescription hostDescription;
+ private final Timestamp timestamp;
+
+ public InternalHostEvent(ProviderId providerId, HostId hostId,
+ HostDescription hostDescription, Timestamp timestamp) {
+ this.providerId = providerId;
+ this.hostId = hostId;
+ this.hostDescription = hostDescription;
+ this.timestamp = timestamp;
+ }
+
+ public ProviderId providerId() {
+ return providerId;
+ }
+
+ public HostId hostId() {
+ return hostId;
+ }
+
+ public HostDescription hostDescription() {
+ return hostDescription;
+ }
+
+ public Timestamp timestamp() {
+ return timestamp;
+ }
+
+ // Needed for serialization.
+ @SuppressWarnings("unused")
+ private InternalHostEvent() {
+ providerId = null;
+ hostId = null;
+ hostDescription = null;
+ timestamp = null;
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/host/impl/InternalHostRemovedEvent.java b/core/store/dist/src/main/java/org/onlab/onos/store/host/impl/InternalHostRemovedEvent.java
new file mode 100644
index 0000000..8dd3b44
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/host/impl/InternalHostRemovedEvent.java
@@ -0,0 +1,34 @@
+package org.onlab.onos.store.host.impl;
+
+import org.onlab.onos.net.HostId;
+import org.onlab.onos.store.Timestamp;
+
+/**
+ * Information published by GossipHostStore to notify peers of a host
+ * removed event.
+ */
+public class InternalHostRemovedEvent {
+
+ private final HostId hostId;
+ private final Timestamp timestamp;
+
+ public InternalHostRemovedEvent(HostId hostId, Timestamp timestamp) {
+ this.hostId = hostId;
+ this.timestamp = timestamp;
+ }
+
+ public HostId hostId() {
+ return hostId;
+ }
+
+ public Timestamp timestamp() {
+ return timestamp;
+ }
+
+ // for serialization.
+ @SuppressWarnings("unused")
+ private InternalHostRemovedEvent() {
+ hostId = null;
+ timestamp = null;
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/GossipLinkStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/GossipLinkStore.java
index e59d65d..a6c1660 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/GossipLinkStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/GossipLinkStore.java
@@ -9,7 +9,6 @@
import com.google.common.collect.SetMultimap;
import org.apache.commons.lang3.RandomUtils;
-import org.apache.commons.lang3.concurrent.ConcurrentUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
@@ -46,7 +45,6 @@
import org.onlab.onos.store.serializers.DistributedStoreSerializers;
import org.onlab.onos.store.serializers.KryoSerializer;
import org.onlab.util.KryoPool;
-import org.onlab.util.NewConcurrentHashMap;
import org.slf4j.Logger;
import java.io.IOException;
@@ -87,7 +85,7 @@
private final Logger log = getLogger(getClass());
// Link inventory
- private final ConcurrentMap<LinkKey, ConcurrentMap<ProviderId, Timestamped<LinkDescription>>> linkDescs =
+ private final ConcurrentMap<LinkKey, Map<ProviderId, Timestamped<LinkDescription>>> linkDescs =
new ConcurrentHashMap<>();
// Link instance cache
@@ -238,7 +236,7 @@
final Timestamped<LinkDescription> deltaDesc = new Timestamped<>(linkDescription, newTimestamp);
- LinkKey key = linkKey(linkDescription);
+ LinkKey key = linkKey(linkDescription.src(), linkDescription.dst());
final LinkEvent event;
final Timestamped<LinkDescription> mergedDesc;
synchronized (getLinkDescriptions(key)) {
@@ -265,8 +263,9 @@
ProviderId providerId,
Timestamped<LinkDescription> linkDescription) {
- LinkKey key = linkKey(linkDescription.value());
- ConcurrentMap<ProviderId, Timestamped<LinkDescription>> descs = getLinkDescriptions(key);
+ LinkKey key = linkKey(linkDescription.value().src(),
+ linkDescription.value().dst());
+ Map<ProviderId, Timestamped<LinkDescription>> descs = getLinkDescriptions(key);
synchronized (descs) {
// if the link was previously removed, we should proceed if and
@@ -293,12 +292,12 @@
// Guarded by linkDescs value (=locking each Link)
private Timestamped<LinkDescription> createOrUpdateLinkDescription(
- ConcurrentMap<ProviderId, Timestamped<LinkDescription>> existingLinkDescriptions,
+ Map<ProviderId, Timestamped<LinkDescription>> descs,
ProviderId providerId,
Timestamped<LinkDescription> linkDescription) {
// merge existing attributes and merge
- Timestamped<LinkDescription> existingLinkDescription = existingLinkDescriptions.get(providerId);
+ Timestamped<LinkDescription> existingLinkDescription = descs.get(providerId);
if (existingLinkDescription != null && existingLinkDescription.isNewer(linkDescription)) {
return null;
}
@@ -313,7 +312,7 @@
linkDescription.value().type(), merged),
linkDescription.timestamp());
}
- return existingLinkDescriptions.put(providerId, newLinkDescription);
+ return descs.put(providerId, newLinkDescription);
}
// Creates and stores the link and returns the appropriate event.
@@ -379,7 +378,7 @@
}
private LinkEvent removeLinkInternal(LinkKey key, Timestamp timestamp) {
- ConcurrentMap<ProviderId, Timestamped<LinkDescription>> linkDescriptions =
+ Map<ProviderId, Timestamped<LinkDescription>> linkDescriptions =
getLinkDescriptions(key);
synchronized (linkDescriptions) {
// accept removal request if given timestamp is newer than
@@ -408,10 +407,10 @@
* @return primary ProviderID, or randomly chosen one if none exists
*/
private ProviderId pickPrimaryProviderId(
- ConcurrentMap<ProviderId, Timestamped<LinkDescription>> providerDescs) {
+ Map<ProviderId, Timestamped<LinkDescription>> linkDescriptions) {
ProviderId fallBackPrimary = null;
- for (Entry<ProviderId, Timestamped<LinkDescription>> e : providerDescs.entrySet()) {
+ for (Entry<ProviderId, Timestamped<LinkDescription>> e : linkDescriptions.entrySet()) {
if (!e.getKey().isAncillary()) {
return e.getKey();
} else if (fallBackPrimary == null) {
@@ -422,9 +421,9 @@
return fallBackPrimary;
}
- private Link composeLink(ConcurrentMap<ProviderId, Timestamped<LinkDescription>> linkDescriptions) {
- ProviderId primaryProviderId = pickPrimaryProviderId(linkDescriptions);
- Timestamped<LinkDescription> base = linkDescriptions.get(primaryProviderId);
+ private Link composeLink(Map<ProviderId, Timestamped<LinkDescription>> descs) {
+ ProviderId primaryProviderId = pickPrimaryProviderId(descs);
+ Timestamped<LinkDescription> base = descs.get(primaryProviderId);
ConnectPoint src = base.value().src();
ConnectPoint dst = base.value().dst();
@@ -432,7 +431,7 @@
DefaultAnnotations annotations = DefaultAnnotations.builder().build();
annotations = merge(annotations, base.value().annotations());
- for (Entry<ProviderId, Timestamped<LinkDescription>> e : linkDescriptions.entrySet()) {
+ for (Entry<ProviderId, Timestamped<LinkDescription>> e : descs.entrySet()) {
if (primaryProviderId.equals(e.getKey())) {
continue;
}
@@ -449,9 +448,20 @@
return new DefaultLink(primaryProviderId , src, dst, type, annotations);
}
- private ConcurrentMap<ProviderId, Timestamped<LinkDescription>> getLinkDescriptions(LinkKey key) {
- return ConcurrentUtils.createIfAbsentUnchecked(linkDescs, key,
- NewConcurrentHashMap.<ProviderId, Timestamped<LinkDescription>>ifNeeded());
+ private Map<ProviderId, Timestamped<LinkDescription>> getLinkDescriptions(LinkKey key) {
+ Map<ProviderId, Timestamped<LinkDescription>> r;
+ r = linkDescs.get(key);
+ if (r != null) {
+ return r;
+ }
+ r = new HashMap<>();
+ final Map<ProviderId, Timestamped<LinkDescription>> concurrentlyAdded;
+ concurrentlyAdded = linkDescs.putIfAbsent(key, r);
+ if (concurrentlyAdded != null) {
+ return concurrentlyAdded;
+ } else {
+ return r;
+ }
}
private Timestamped<LinkDescription> getLinkDescription(LinkKey key, ProviderId providerId) {
@@ -470,13 +480,13 @@
}
}
- private static final Predicate<Provided> IS_PRIMARY = new IsPrimary();
- private static final Predicate<Provided> isPrimary() {
- return IS_PRIMARY;
- }
-
private static final class IsPrimary implements Predicate<Provided> {
+ private static final Predicate<Provided> IS_PRIMARY = new IsPrimary();
+ public static final Predicate<Provided> isPrimary() {
+ return IS_PRIMARY;
+ }
+
@Override
public boolean apply(Provided input) {
return !input.providerId().isAncillary();
@@ -581,11 +591,11 @@
Map<LinkFragmentId, Timestamp> linkTimestamps = new HashMap<>(linkDescs.size());
Map<LinkKey, Timestamp> linkTombstones = new HashMap<>(removedLinks.size());
- for (Entry<LinkKey, ConcurrentMap<ProviderId, Timestamped<LinkDescription>>>
+ for (Entry<LinkKey, Map<ProviderId, Timestamped<LinkDescription>>>
provs : linkDescs.entrySet()) {
final LinkKey linkKey = provs.getKey();
- final ConcurrentMap<ProviderId, Timestamped<LinkDescription>> linkDesc = provs.getValue();
+ final Map<ProviderId, Timestamped<LinkDescription>> linkDesc = provs.getValue();
synchronized (linkDesc) {
for (Map.Entry<ProviderId, Timestamped<LinkDescription>> e : linkDesc.entrySet()) {
linkTimestamps.put(new LinkFragmentId(linkKey, e.getKey()), e.getValue().timestamp());
@@ -670,7 +680,7 @@
@Override
public void handle(ClusterMessage message) {
- log.info("Received Link Anti-Entropy advertisement from peer: {}", message.sender());
+ log.debug("Received Link Anti-Entropy advertisement from peer: {}", message.sender());
LinkAntiEntropyAdvertisement advertisement = SERIALIZER.decode(message.payload());
handleAntiEntropyAdvertisement(advertisement);
}
diff --git a/core/store/dist/src/test/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManagerTest.java b/core/store/dist/src/test/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManagerTest.java
index dcbe437..34e5b3b 100644
--- a/core/store/dist/src/test/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManagerTest.java
+++ b/core/store/dist/src/test/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManagerTest.java
@@ -40,22 +40,18 @@
@Before
public void setUp() throws Exception {
- MessageSerializer messageSerializer = new MessageSerializer();
- messageSerializer.activate();
NettyMessagingService messagingService = new NettyMessagingService();
messagingService.activate();
ccm1 = new ClusterCommunicationManager();
-// ccm1.serializationService = messageSerializer;
ccm1.activate();
ccm2 = new ClusterCommunicationManager();
-// ccm2.serializationService = messageSerializer;
ccm2.activate();
- ccm1.initialize(node1, cnd1);
- ccm2.initialize(node2, cnd2);
+// ccm1.initialize(node1, cnd1);
+// ccm2.initialize(node2, cnd2);
}
@After
@@ -70,7 +66,7 @@
cnd1.latch = new CountDownLatch(1);
cnd2.latch = new CountDownLatch(1);
- ccm1.addNode(node2);
+// ccm1.addNode(node2);
validateDelegateEvent(cnd1, Op.DETECTED, node2.id());
validateDelegateEvent(cnd2, Op.DETECTED, node1.id());
}
@@ -81,7 +77,7 @@
cnd1.latch = new CountDownLatch(1);
cnd2.latch = new CountDownLatch(1);
- ccm1.addNode(node2);
+// ccm1.addNode(node2);
validateDelegateEvent(cnd1, Op.DETECTED, node2.id());
validateDelegateEvent(cnd2, Op.DETECTED, node1.id());
diff --git a/core/store/hz/net/src/main/java/org/onlab/onos/store/link/impl/DistributedLinkStore.java b/core/store/hz/net/src/main/java/org/onlab/onos/store/link/impl/DistributedLinkStore.java
index 5e1ff1a..90ae6fe 100644
--- a/core/store/hz/net/src/main/java/org/onlab/onos/store/link/impl/DistributedLinkStore.java
+++ b/core/store/hz/net/src/main/java/org/onlab/onos/store/link/impl/DistributedLinkStore.java
@@ -151,7 +151,7 @@
@Override
public LinkEvent createOrUpdateLink(ProviderId providerId,
LinkDescription linkDescription) {
- LinkKey key = linkKey(linkDescription);
+ LinkKey key = linkKey(linkDescription.src(), linkDescription.dst());
Optional<DefaultLink> link = links.getUnchecked(key);
if (!link.isPresent()) {
return createLink(providerId, key, linkDescription);
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleHostStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleHostStore.java
index 67ed050..bf99227 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleHostStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleHostStore.java
@@ -35,6 +35,7 @@
import static org.onlab.onos.net.host.HostEvent.Type.*;
import static org.slf4j.LoggerFactory.getLogger;
+// TODO: multi-provider, annotation not supported.
/**
* Manages inventory of end-station hosts using trivial in-memory
* implementation.
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java
index 47ef6fa..bcddda3 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java
@@ -149,7 +149,7 @@
@Override
public LinkEvent createOrUpdateLink(ProviderId providerId,
LinkDescription linkDescription) {
- LinkKey key = linkKey(linkDescription);
+ LinkKey key = linkKey(linkDescription.src(), linkDescription.dst());
ConcurrentMap<ProviderId, LinkDescription> descs = getLinkDescriptions(key);
synchronized (descs) {
diff --git a/features/features.xml b/features/features.xml
index c9e6bdd..34c70bc 100644
--- a/features/features.xml
+++ b/features/features.xml
@@ -67,16 +67,6 @@
<bundle>mvn:org.onlab.onos/onos-core-hz-cluster/1.0.0-SNAPSHOT</bundle>
</feature>
- <feature name="onos-core-hazelcast" version="1.0.0"
- description="ONOS core components built on hazelcast">
- <feature>onos-api</feature>
- <bundle>mvn:org.onlab.onos/onos-core-net/1.0.0-SNAPSHOT</bundle>
- <bundle>mvn:org.onlab.onos/onos-core-hz-common/1.0.0-SNAPSHOT</bundle>
- <bundle>mvn:org.onlab.onos/onos-core-serializers/1.0.0-SNAPSHOT</bundle>
- <bundle>mvn:org.onlab.onos/onos-core-hz-cluster/1.0.0-SNAPSHOT</bundle>
- <bundle>mvn:org.onlab.onos/onos-core-hz-net/1.0.0-SNAPSHOT</bundle>
- </feature>
-
<feature name="onos-core-trivial" version="1.0.0"
description="ONOS core components">
<feature>onos-api</feature>
@@ -163,4 +153,10 @@
<bundle>mvn:org.onlab.onos/onos-app-config/1.0.0-SNAPSHOT</bundle>
</feature>
+ <feature name="onos-app-sdnip" version="1.0.0"
+ description="SDN-IP peering application">
+ <feature>onos-api</feature>
+ <bundle>mvn:org.onlab.onos/onos-app-sdnip/1.0.0-SNAPSHOT</bundle>
+ </feature>
+
</features>
diff --git a/openflow/ctl/src/main/java/org/onlab/onos/openflow/drivers/impl/OFOpticalSwitchImplLINC13.java b/openflow/ctl/src/main/java/org/onlab/onos/openflow/drivers/impl/OFOpticalSwitchImplLINC13.java
index 71a7ab8..79a162e 100644
--- a/openflow/ctl/src/main/java/org/onlab/onos/openflow/drivers/impl/OFOpticalSwitchImplLINC13.java
+++ b/openflow/ctl/src/main/java/org/onlab/onos/openflow/drivers/impl/OFOpticalSwitchImplLINC13.java
@@ -5,6 +5,7 @@
import org.onlab.onos.openflow.controller.driver.SwitchDriverSubHandshakeNotStarted;
import org.onlab.onos.openflow.controller.Dpid;
import org.onlab.onos.openflow.controller.driver.AbstractOpenFlowSwitch;
+import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
import org.projectfloodlight.openflow.protocol.OFCircuitPortsReply;
import org.projectfloodlight.openflow.protocol.OFCircuitPortsRequest;
import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
@@ -21,7 +22,6 @@
import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigid;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigidBasic;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigtype;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigtypeBasic;
import org.projectfloodlight.openflow.types.CircuitSignalID;
import org.projectfloodlight.openflow.types.OFPort;
import org.projectfloodlight.openflow.types.U8;
@@ -119,11 +119,12 @@
processHandshakeOFExperimenterPortDescRequest(
(OFCircuitPortsReply) m);
driverHandshakeComplete.set(true);
- /* try {
+ try {
testMA();
+ testReverseMA();
} catch (IOException e) {
e.printStackTrace();
- }*/
+ }
break;
default:
log.debug("Received message {} during switch-driver " +
@@ -163,32 +164,23 @@
"message " +
"{}",
circuitPortsRequest.toString());
- channel.write(Collections.singletonList(circuitPortsRequest));
+ sendMsg(Collections.<OFMessage>singletonList(circuitPortsRequest));
}
-
- //todo for testing
- public static final U8 SIGNAL_TYPE = U8.of((short) 1);
+ public static final U8 SIGNAL_TYPE = U8.of((short) 10);
private void testMA() throws IOException {
log.debug("LINC OE *** Testing MA ");
- short lambda = 100;
- if (getId() == 0x0000ffffffffff02L) {
+ short lambda = 1;
+ if (getId() == 0x0000ffffffffff01L) {
final int inport = 10;
final int outport = 20;
//Circuit signal id
CircuitSignalID sigID = getSignalID(lambda);
- OFOxmOchSigid fieldSigIDMatch = factory().oxms().ochSigid(sigID);
- OFOxmOchSigtype fieldSigType = factory()
- .oxms()
- .ochSigtype(SIGNAL_TYPE);
-
OFOxmOchSigidBasic ofOxmOchSigidBasic =
factory().oxms().ochSigidBasic(sigID);
- OFOxmOchSigtypeBasic ofOxmOchSigtypeBasic =
- factory().oxms().ochSigtypeBasic(SIGNAL_TYPE);
//Match Port
OFOxmInPort fieldPort = factory().oxms()
@@ -196,27 +188,21 @@
OFMatchV3 matchPort =
factory()
.buildMatchV3().
- setOxmList(OFOxmList.of(fieldPort,
- fieldSigType,
- fieldSigIDMatch)).build();
+ setOxmList(OFOxmList.of(fieldPort)).build();
// Set Action outport ,sigType and sigID
List<OFAction> actionList = new ArrayList<>();
OFAction actionOutPort =
factory().actions().output(OFPort.of(outport),
- Short.MAX_VALUE);
+ 0xffff);
OFActionCircuit actionCircuit = factory()
.actions()
.circuit(ofOxmOchSigidBasic);
- OFActionCircuit setActionSigType = factory()
- .actions()
- .circuit(ofOxmOchSigtypeBasic);
- actionList.add(actionOutPort);
- actionList.add(setActionSigType);
actionList.add(actionCircuit);
+ actionList.add(actionOutPort);
OFInstruction instructionAction =
factory().instructions().buildApplyActions()
@@ -228,6 +214,7 @@
OFMessage opticalFlowEntry =
factory().buildFlowAdd()
.setMatch(matchPort)
+ .setPriority(100)
.setInstructions(instructions)
.setXid(getNextTransactionId())
.build();
@@ -235,9 +222,10 @@
List<OFMessage> msglist = new ArrayList<>(1);
msglist.add(opticalFlowEntry);
write(msglist);
+ sendBarrier(true);
} else if (getId() == 0x0000ffffffffff03L) {
- final int inport = 21;
- final int outport = 22;
+ final int inport = 30;
+ final int outport = 31;
//Circuit signal id
CircuitSignalID sigID = getSignalID(lambda);
@@ -249,9 +237,6 @@
OFOxmOchSigidBasic ofOxmOchSigidBasic =
factory().oxms().ochSigidBasic(sigID);
- OFOxmOchSigtypeBasic ofOxmOchSigtypeBasic =
- factory().oxms().ochSigtypeBasic(SIGNAL_TYPE);
-
//Match Port,SigType,SigID
OFOxmInPort fieldPort = factory()
.oxms()
@@ -259,27 +244,26 @@
OFMatchV3 matchPort = factory()
.buildMatchV3()
.setOxmList(OFOxmList.of(fieldPort,
- fieldSigType,
- fieldSigIDMatch))
+ fieldSigIDMatch,
+ fieldSigType
+ ))
.build();
// Set Action outport ,SigType, sigID
List<OFAction> actionList = new ArrayList<>();
OFAction actionOutPort =
factory().actions().output(OFPort.of(outport),
- Short.MAX_VALUE);
+ 0xffff);
- OFActionCircuit setActionSigType = factory()
- .actions()
- .circuit(ofOxmOchSigtypeBasic);
OFActionCircuit actionCircuit = factory()
.actions()
.circuit(ofOxmOchSigidBasic);
- actionList.add(actionOutPort);
- actionList.add(setActionSigType);
+
+ //actionList.add(setActionSigType);
actionList.add(actionCircuit);
+ actionList.add(actionOutPort);
OFInstruction instructionAction =
factory().instructions().buildApplyActions()
@@ -290,17 +274,19 @@
OFMessage opticalFlowEntry =
factory().buildFlowAdd()
- .setMatch(matchPort)
- .setInstructions(instructions)
- .setXid(getNextTransactionId())
- .build();
+ .setMatch(matchPort)
+ .setPriority(100)
+ .setInstructions(instructions)
+ .setXid(getNextTransactionId())
+ .build();
log.debug("Adding optical flow in sw {}", getStringId());
List<OFMessage> msglist = new ArrayList<>(1);
msglist.add(opticalFlowEntry);
write(msglist);
+ sendBarrier(true);
- } else if (getId() == 0x0000ffffffffff04L) {
- final int inport = 23;
+ } else if (getId() == 0x0000ffffffffff02L) {
+ final int inport = 21;
final int outport = 11;
//Circuit signal id
CircuitSignalID sigID = getSignalID(lambda);
@@ -318,15 +304,16 @@
OFMatchV3 matchPort =
factory().buildMatchV3()
.setOxmList(OFOxmList.of(fieldPort,
- fieldSigType,
- fieldSigIDMatch))
+ fieldSigIDMatch,
+ fieldSigType
+ ))
.build();
// Set Action outport
List<OFAction> actionList = new ArrayList<>();
OFAction actionOutPort =
factory().actions().output(OFPort.of(outport),
- Short.MAX_VALUE);
+ 0xffff);
actionList.add(actionOutPort);
@@ -339,17 +326,184 @@
OFMessage opticalFlowEntry =
factory().buildFlowAdd()
- .setMatch(matchPort)
- .setInstructions(instructions)
- .setXid(getNextTransactionId())
- .build();
+ .setMatch(matchPort)
+ .setPriority(100)
+ .setInstructions(instructions)
+ .setXid(getNextTransactionId())
+ .build();
log.debug("Adding optical flow in sw {}", getStringId());
List<OFMessage> msglist = new ArrayList<>(1);
msglist.add(opticalFlowEntry);
write(msglist);
+ sendBarrier(true);
}
}
+ private void testReverseMA() throws IOException {
+ log.debug("LINC OE *** Testing MA ");
+ short lambda = 1;
+ if (getId() == 0x0000ffffffffff02L) {
+ final int inport = 11;
+ final int outport = 21;
+ //Circuit signal id
+ CircuitSignalID sigID = getSignalID(lambda);
+
+ OFOxmOchSigidBasic ofOxmOchSigidBasic =
+ factory().oxms().ochSigidBasic(sigID);
+
+ //Match Port
+ OFOxmInPort fieldPort = factory().oxms()
+ .inPort(OFPort.of(inport));
+ OFMatchV3 matchPort =
+ factory()
+ .buildMatchV3().
+ setOxmList(OFOxmList.of(fieldPort)).build();
+
+
+ // Set Action outport ,sigType and sigID
+ List<OFAction> actionList = new ArrayList<>();
+ OFAction actionOutPort =
+ factory().actions().output(OFPort.of(outport),
+ 0xffff);
+
+ OFActionCircuit actionCircuit = factory()
+ .actions()
+ .circuit(ofOxmOchSigidBasic);
+ actionList.add(actionCircuit);
+ actionList.add(actionOutPort);
+
+ OFInstruction instructionAction =
+ factory().instructions().buildApplyActions()
+ .setActions(actionList)
+ .build();
+ List<OFInstruction> instructions =
+ Collections.singletonList(instructionAction);
+
+ OFMessage opticalFlowEntry =
+ factory().buildFlowAdd()
+ .setMatch(matchPort)
+ .setPriority(100)
+ .setInstructions(instructions)
+ .setXid(getNextTransactionId())
+ .build();
+ log.debug("Adding optical flow in sw {}", getStringId());
+ List<OFMessage> msglist = new ArrayList<>(1);
+ msglist.add(opticalFlowEntry);
+ write(msglist);
+ sendBarrier(true);
+ } else if (getId() == 0x0000ffffffffff03L) {
+ final int inport = 31;
+ final int outport = 30;
+ //Circuit signal id
+ CircuitSignalID sigID = getSignalID(lambda);
+
+ OFOxmOchSigid fieldSigIDMatch = factory().oxms().ochSigid(sigID);
+ OFOxmOchSigtype fieldSigType = factory()
+ .oxms()
+ .ochSigtype(SIGNAL_TYPE);
+
+ OFOxmOchSigidBasic ofOxmOchSigidBasic =
+ factory().oxms().ochSigidBasic(sigID);
+
+ //Match Port,SigType,SigID
+ OFOxmInPort fieldPort = factory()
+ .oxms()
+ .inPort(OFPort.of(inport));
+ OFMatchV3 matchPort = factory()
+ .buildMatchV3()
+ .setOxmList(OFOxmList.of(fieldPort,
+ fieldSigIDMatch,
+ fieldSigType
+ ))
+ .build();
+
+ // Set Action outport ,SigType, sigID
+ List<OFAction> actionList = new ArrayList<>();
+ OFAction actionOutPort =
+ factory().actions().output(OFPort.of(outport),
+ 0xffff);
+ OFActionCircuit actionCircuit = factory()
+ .actions()
+ .circuit(ofOxmOchSigidBasic);
+
+ actionList.add(actionCircuit);
+ actionList.add(actionOutPort);
+
+ OFInstruction instructionAction =
+ factory().instructions().buildApplyActions()
+ .setActions(actionList)
+ .build();
+ List<OFInstruction> instructions =
+ Collections.singletonList(instructionAction);
+
+ OFMessage opticalFlowEntry =
+ factory().buildFlowAdd()
+ .setMatch(matchPort)
+ .setPriority(100)
+ .setInstructions(instructions)
+ .setXid(getNextTransactionId())
+ .build();
+ log.debug("Adding optical flow in sw {}", getStringId());
+ List<OFMessage> msglist = new ArrayList<>(1);
+ msglist.add(opticalFlowEntry);
+ write(msglist);
+ sendBarrier(true);
+
+ } else if (getId() == 0x0000ffffffffff01L) {
+ final int inport = 20;
+ final int outport = 10;
+ //Circuit signal id
+ CircuitSignalID sigID = getSignalID(lambda);
+
+ OFOxmOchSigid fieldSigIDMatch = factory().oxms().ochSigid(sigID);
+ OFOxmOchSigtype fieldSigType = factory()
+ .oxms()
+ .ochSigtype(SIGNAL_TYPE);
+
+
+ //Match Port, sig type and sig id
+ OFOxmInPort fieldPort = factory()
+ .oxms()
+ .inPort(OFPort.of(inport));
+ OFMatchV3 matchPort =
+ factory().buildMatchV3()
+ .setOxmList(OFOxmList.of(fieldPort,
+ fieldSigIDMatch,
+ fieldSigType
+ ))
+ .build();
+
+ // Set Action outport
+ List<OFAction> actionList = new ArrayList<>();
+ OFAction actionOutPort =
+ factory().actions().output(OFPort.of(outport),
+ 0xffff);
+
+ actionList.add(actionOutPort);
+
+ OFInstruction instructionAction =
+ factory().instructions().buildApplyActions()
+ .setActions(actionList)
+ .build();
+ List<OFInstruction> instructions =
+ Collections.singletonList(instructionAction);
+
+ OFMessage opticalFlowEntry =
+ factory().buildFlowAdd()
+ .setMatch(matchPort)
+ .setPriority(100)
+ .setInstructions(instructions)
+ .setXid(getNextTransactionId())
+ .build();
+ log.debug("Adding optical flow in sw {}", getStringId());
+ List<OFMessage> msglist = new ArrayList<>(1);
+ msglist.add(opticalFlowEntry);
+ write(msglist);
+ sendBarrier(true);
+ }
+
+ }
+
// Todo remove - for testing purpose only
private static CircuitSignalID getSignalID(short lambda) {
@@ -365,9 +519,21 @@
return signalID;
}
+ private void sendBarrier(boolean finalBarrier) throws IOException {
+ int xid = getNextTransactionId();
+ if (finalBarrier) {
+ barrierXidToWaitFor = xid;
+ }
+ OFBarrierRequest br = factory()
+ .buildBarrierRequest()
+ .setXid(xid)
+ .build();
+ sendMsg(br);
+ }
+
@Override
public void write(OFMessage msg) {
- this.channel.write(msg);
+ this.sendMsg(msg);
}
@Override
diff --git a/pom.xml b/pom.xml
index 1b75b52..9b275ab 100644
--- a/pom.xml
+++ b/pom.xml
@@ -304,7 +304,9 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
- <version>3.1</version>
+ <!-- TODO: update once following issue is fixed. -->
+ <!-- https://jira.codehaus.org/browse/MCOMPILER-205 -->
+ <version>2.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
diff --git a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowModBuilder.java b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowModBuilder.java
index 9568f1f..c1c56c3 100644
--- a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowModBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowModBuilder.java
@@ -161,10 +161,10 @@
switch (l3m.subtype()) {
case IP_DST:
ip = (ModIPInstruction) i;
- return factory.actions().setNwDst(IPv4Address.of(ip.ip().toInt()));
+ return factory.actions().setNwDst(IPv4Address.of(ip.ip().toRealInt()));
case IP_SRC:
ip = (ModIPInstruction) i;
- return factory.actions().setNwSrc(IPv4Address.of(ip.ip().toInt()));
+ return factory.actions().setNwSrc(IPv4Address.of(ip.ip().toRealInt()));
default:
log.warn("Unimplemented action type {}.", l3m.subtype());
break;
@@ -220,21 +220,21 @@
case IPV4_DST:
ip = (IPCriterion) c;
if (ip.ip().isMasked()) {
- Masked<IPv4Address> maskedIp = Masked.of(IPv4Address.of(ip.ip().toInt()),
- IPv4Address.of(ip.ip().netmask().toInt()));
+ Masked<IPv4Address> maskedIp = Masked.of(IPv4Address.of(ip.ip().toRealInt()),
+ IPv4Address.of(ip.ip().netmask().toRealInt()));
mBuilder.setMasked(MatchField.IPV4_DST, maskedIp);
} else {
- mBuilder.setExact(MatchField.IPV4_DST, IPv4Address.of(ip.ip().toInt()));
+ mBuilder.setExact(MatchField.IPV4_DST, IPv4Address.of(ip.ip().toRealInt()));
}
break;
case IPV4_SRC:
ip = (IPCriterion) c;
if (ip.ip().isMasked()) {
- Masked<IPv4Address> maskedIp = Masked.of(IPv4Address.of(ip.ip().toInt()),
- IPv4Address.of(ip.ip().netmask().toInt()));
+ Masked<IPv4Address> maskedIp = Masked.of(IPv4Address.of(ip.ip().toRealInt()),
+ IPv4Address.of(ip.ip().netmask().toRealInt()));
mBuilder.setMasked(MatchField.IPV4_SRC, maskedIp);
} else {
- mBuilder.setExact(MatchField.IPV4_SRC, IPv4Address.of(ip.ip().toInt()));
+ mBuilder.setExact(MatchField.IPV4_SRC, IPv4Address.of(ip.ip().toRealInt()));
}
break;
case IP_PROTO:
diff --git a/tools/build/envDefaults b/tools/build/envDefaults
index cbc6577..f1e1346 100644
--- a/tools/build/envDefaults
+++ b/tools/build/envDefaults
@@ -9,6 +9,10 @@
export KARAF_TAR=${KARAF_TAR:-~/Downloads/apache-karaf-3.0.1.tar.gz}
export KARAF_DIST=$(basename $KARAF_ZIP .zip)
+# Add ONOS-specific directories to the exectable PATH
+export PATH="$PATH:$ONOS_ROOT/tools/dev/bin:$ONOS_ROOT/tools/test/bin"
+export PATH="$PATH:$ONOS_ROOT/tools/build"
+
# Fallback build number us derived from from the user name & time
export BUILD_NUMBER=${BUILD_NUMBER:-$(id -un)~$(date +'%Y/%m/%d@%H:%M')}
diff --git a/tools/build/onos-build b/tools/build/onos-build
index decf892..cf2ebe4 100755
--- a/tools/build/onos-build
+++ b/tools/build/onos-build
@@ -1,10 +1,10 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# Builds the ONOS from source.
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
. $ONOS_ROOT/tools/build/envDefaults
cd $ONOS_ROOT
-mvn clean install && mvn javadoc:aggregate
\ No newline at end of file
+mvn clean install && mvn javadoc:aggregate
diff --git a/tools/build/onos-package b/tools/build/onos-package
index a55a613..5ae80a2 100755
--- a/tools/build/onos-package
+++ b/tools/build/onos-package
@@ -1,7 +1,7 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# Packages ONOS distributable into onos.tar.gz
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
. $ONOS_ROOT/tools/build/envDefaults
diff --git a/tools/build/onos-test b/tools/build/onos-test
index 1526e1b..740e370 100755
--- a/tools/build/onos-test
+++ b/tools/build/onos-test
@@ -1,7 +1,7 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# Launches the ONOS tests on the current cell environment.
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
. $ONOS_ROOT/tools/build/envDefaults
diff --git a/tools/dev/bash_profile b/tools/dev/bash_profile
index 6b66ff1..e14c43b 100644
--- a/tools/dev/bash_profile
+++ b/tools/dev/bash_profile
@@ -61,15 +61,14 @@
if [ -n "$1" ]; then
[ ! -f $ONOS_ROOT/tools/test/cells/$1 ] && \
echo "No such cell: $1" >&2 && return 1
+ unset ONOS_CELL ONOS_NIC ONOS_FEATURES
unset OC1 OC2 OC3 OC4 OC5 OC6 OC7 OC8 OC9 OCN OCI
. $ONOS_ROOT/tools/test/cells/$1
- export OCI=$OC1
- export ONOS_CELL=$1
cell
else
env | egrep "ONOS_CELL"
env | egrep "OCI"
- env | egrep "OC[0-9]+" | sort
+ env | egrep "OC[1-9]+" | sort
env | egrep "OCN"
env | egrep "ONOS_" | egrep -v 'ONOS_ROOT|ONOS_CELL'
fi
diff --git a/tools/dev/bin/onos-build-selective b/tools/dev/bin/onos-build-selective
index 2fe0188..32b75e7 100755
--- a/tools/dev/bin/onos-build-selective
+++ b/tools/dev/bin/onos-build-selective
@@ -1,7 +1,7 @@
#!/bin/bash
-#------------------------------------------------------------------------------
+# ----------------------------------------------------------------------------
# Selectively builds only those projects that contained modified Java files.
-#------------------------------------------------------------------------------
+# ----------------------------------------------------------------------------
projects=$(find $ONOS_ROOT -name '*.java' \
-not -path '*/openflowj/*' -and -not -path '.git/*' \
@@ -9,4 +9,4 @@
sort -u | sed "s:$ONOS_ROOT::g" | tr '\n' ',' | \
sed 's:/,:,:g;s:,/:,:g;s:^/::g;s:,$::g')
-[ -n "$projects" ] && cd $ONOS_ROOT && mvn --projects $projects ${@:-clean install}
\ No newline at end of file
+[ -n "$projects" ] && cd $ONOS_ROOT && mvn --projects $projects ${@:-clean install}
diff --git a/tools/dev/bin/onos-build-selective-hook b/tools/dev/bin/onos-build-selective-hook
index eb4e482..c27d747 100755
--- a/tools/dev/bin/onos-build-selective-hook
+++ b/tools/dev/bin/onos-build-selective-hook
@@ -1,8 +1,8 @@
#!/bin/bash
-#------------------------------------------------------------------------------
+# ----------------------------------------------------------------------------
# Echoes project-level directory if a Java file within is newer than the
# target directory.
-#------------------------------------------------------------------------------
+# ----------------------------------------------------------------------------
javaFile=${1#*\/src\/*\/java/}
basename=${1/*\//}
@@ -14,4 +14,3 @@
target=$project/target
[ $target -nt ${src}$javaFile ] || echo ${src/src*/}
-
diff --git a/tools/dev/bin/onos-local-log b/tools/dev/bin/onos-local-log
index f138b73..83bdf9c 100755
--- a/tools/dev/bin/onos-local-log
+++ b/tools/dev/bin/onos-local-log
@@ -1,11 +1,10 @@
#!/bin/bash
-#------------------------------------------------------------------------------
+# ----------------------------------------------------------------------------
# Continuously watches the Apache Karaf log; survives 'karaf clean'
-#------------------------------------------------------------------------------
+# ----------------------------------------------------------------------------
KARAF_LOG=${KARAF_LOG:-~/apache-karaf-3.0.1/data/log/karaf.log}
while true; do
[ ! -f $KARAF_LOG ] && sleep 2 && continue
tail -n 512 -f -F $KARAF_LOG
done
-
diff --git a/tools/package/bin/onos b/tools/package/bin/onos
index 2c37588..0489318 100755
--- a/tools/package/bin/onos
+++ b/tools/package/bin/onos
@@ -1,10 +1,9 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# ONOS command-line client
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-7-openjdk-amd64/}
cd $(dirname $0)/../apache-karaf-*/bin
./client -h localhost "$@"
-
diff --git a/tools/package/bin/onos-service b/tools/package/bin/onos-service
index c030887..7c8850f 100755
--- a/tools/package/bin/onos-service
+++ b/tools/package/bin/onos-service
@@ -1,11 +1,10 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# Starts ONOS Apache Karaf container
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-7-openjdk-amd64/}
export JAVA_OPTS="-Xms256M -Xmx2048M"
cd /opt/onos
/opt/onos/apache-karaf-3.0.1/bin/karaf "$@"
-
diff --git a/tools/test/bin/onos b/tools/test/bin/onos
index 76b5c15..5bd5de6 100755
--- a/tools/test/bin/onos
+++ b/tools/test/bin/onos
@@ -1,7 +1,10 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# ONOS remote command-line client.
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
+
+[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
+. $ONOS_ROOT/tools/build/envDefaults
[ "$1" = "-w" ] && shift && onos-wait-for-start $1
diff --git a/tools/test/bin/onos-check-logs b/tools/test/bin/onos-check-logs
index 19091ec..a1d2ca9 100755
--- a/tools/test/bin/onos-check-logs
+++ b/tools/test/bin/onos-check-logs
@@ -1,7 +1,7 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# Checks the logs of the remote ONOS instance and makes sure they are clean.
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
. $ONOS_ROOT/tools/build/envDefaults
diff --git a/tools/test/bin/onos-config b/tools/test/bin/onos-config
index 4c4f7e1..a8ef4fe 100755
--- a/tools/test/bin/onos-config
+++ b/tools/test/bin/onos-config
@@ -1,7 +1,7 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# Remotely configures & starts ONOS for the first time.
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
. $ONOS_ROOT/tools/build/envDefaults
@@ -24,5 +24,4 @@
echo \"onos.ip = \$(ifconfig | grep $ONOS_NIC | cut -d: -f2 | cut -d\\ -f1)\" \
>> $ONOS_INSTALL_DIR/$KARAF_DIST/etc/system.properties
"
-
-scp -q $CDEF_FILE $remote:$ONOS_INSTALL_DIR/config/
\ No newline at end of file
+scp -q $CDEF_FILE $remote:$ONOS_INSTALL_DIR/config/
diff --git a/tools/test/bin/onos-fetch-vms b/tools/test/bin/onos-fetch-vms
index 2fd4602..1a11a02 100755
--- a/tools/test/bin/onos-fetch-vms
+++ b/tools/test/bin/onos-fetch-vms
@@ -1,7 +1,7 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# Remotely fetches the ONOS test VMs from a local share into ~/Downloads.
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
. $ONOS_ROOT/tools/build/envDefaults
diff --git a/tools/test/bin/onos-gui b/tools/test/bin/onos-gui
index f782751..a0c843d 100755
--- a/tools/test/bin/onos-gui
+++ b/tools/test/bin/onos-gui
@@ -1,9 +1,12 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# Launches ONOS GUI on the specified node.
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
+
+[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
+. $ONOS_ROOT/tools/build/envDefaults
host=${1:-$OCI}
host=${host:-localhost}
-open http://$host:8181/onos/tvue
\ No newline at end of file
+open http://$host:8181/onos/tvue
diff --git a/tools/test/bin/onos-install b/tools/test/bin/onos-install
index 38934fd..d999f36 100755
--- a/tools/test/bin/onos-install
+++ b/tools/test/bin/onos-install
@@ -1,7 +1,7 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# Remotely pushes bits to a remote node and installs ONOS on it.
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
. $ONOS_ROOT/tools/build/envDefaults
@@ -18,7 +18,7 @@
[ -d $ONOS_INSTALL_DIR/bin ] && echo \"ONOS is already installed\" && exit 1
# Prepare a landing zone and unroll the bits
- sudo mkdir -p $ONOS_INSTALL_DIR && sudo chown sdn:sdn $ONOS_INSTALL_DIR
+ sudo mkdir -p $ONOS_INSTALL_DIR && sudo chown ${ONOS_USER}:${ONOS_USER} $ONOS_INSTALL_DIR
tar zxmf /tmp/$ONOS_BITS.tar.gz -C $ONOS_INSTALL_DIR --strip-components=1
# Make a link to the log file directory and make a home for auxiliaries
diff --git a/tools/test/bin/onos-kill b/tools/test/bin/onos-kill
index 6b849d8..a6c2333 100755
--- a/tools/test/bin/onos-kill
+++ b/tools/test/bin/onos-kill
@@ -1,9 +1,9 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# Remotely kills the ONOS service on the specified node.
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
. $ONOS_ROOT/tools/build/envDefaults
-ssh $ONOS_USER@${1:-$OCI} "kill -9 \$(ps -ef | grep karaf.jar | grep -v grep | cut -c10-15)"
\ No newline at end of file
+ssh $ONOS_USER@${1:-$OCI} "kill -9 \$(ps -ef | grep karaf.jar | grep -v grep | cut -c10-15)"
diff --git a/tools/test/bin/onos-list-cells b/tools/test/bin/onos-list-cells
new file mode 100755
index 0000000..39a70ee
--- /dev/null
+++ b/tools/test/bin/onos-list-cells
@@ -0,0 +1,18 @@
+#!/bin/bash
+# -----------------------------------------------------------------------------
+# List available ONOS cells configuration.
+# -----------------------------------------------------------------------------
+
+[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
+. $ONOS_ROOT/tools/build/envDefaults
+
+# Lists available cells
+for cell in $(ls -1 $ONOS_ROOT/tools/test/cells); do
+ if [ ${cell} = "${ONOS_CELL}" ]; then
+ cell_id="${cell} *"
+ else
+ cell_id="${cell}"
+ fi
+ cell_descr="$(grep '^#' $ONOS_ROOT/tools/test/cells/$cell | head -n 1)"
+ printf "%-12s %s\n" "${cell_id}" "${cell_descr}"
+done
diff --git a/tools/test/bin/onos-log b/tools/test/bin/onos-log
index 3e0c945..4dc77c0 100755
--- a/tools/test/bin/onos-log
+++ b/tools/test/bin/onos-log
@@ -1,7 +1,7 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# Monitors remote ONOS log file on the specified node.
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
. $ONOS_ROOT/tools/build/envDefaults
diff --git a/tools/test/bin/onos-patch-vm b/tools/test/bin/onos-patch-vm
index ccc4007..39f1c60 100755
--- a/tools/test/bin/onos-patch-vm
+++ b/tools/test/bin/onos-patch-vm
@@ -1,7 +1,7 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# Remotely patches the ONOS VM to tailor its hostname.
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
. $ONOS_ROOT/tools/build/envDefaults
diff --git a/tools/test/bin/onos-push-keys b/tools/test/bin/onos-push-keys
index 247d331..cf9fd02 100755
--- a/tools/test/bin/onos-push-keys
+++ b/tools/test/bin/onos-push-keys
@@ -1,7 +1,7 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# Pushes the local id_rsa.pub to the authorized_keys on a remote ONOS node.
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
. $ONOS_ROOT/tools/build/envDefaults
diff --git a/tools/test/bin/onos-push-update-bundle b/tools/test/bin/onos-push-update-bundle
index ab6e604..1539467 100755
--- a/tools/test/bin/onos-push-update-bundle
+++ b/tools/test/bin/onos-push-update-bundle
@@ -1,7 +1,7 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# Pushes the specified bundle to the remote ONOS cell machines and updates it.
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
. $ONOS_ROOT/tools/build/envDefaults
diff --git a/tools/test/bin/onos-service b/tools/test/bin/onos-service
index d148f67..1c62ae8 100755
--- a/tools/test/bin/onos-service
+++ b/tools/test/bin/onos-service
@@ -1,9 +1,9 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# Remotely administers the ONOS service on the specified node.
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
. $ONOS_ROOT/tools/build/envDefaults
-ssh $ONOS_USER@${1:-$OCI} "sudo service onos ${2:-status}"
\ No newline at end of file
+ssh $ONOS_USER@${1:-$OCI} "sudo service onos ${2:-status}"
diff --git a/tools/test/bin/onos-show-cell b/tools/test/bin/onos-show-cell
new file mode 100755
index 0000000..d7e56c3
--- /dev/null
+++ b/tools/test/bin/onos-show-cell
@@ -0,0 +1,53 @@
+#!/bin/bash
+# -----------------------------------------------------------------------------
+# Print the configuration of an ONOS cell.
+# -----------------------------------------------------------------------------
+
+[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
+. $ONOS_ROOT/tools/build/envDefaults
+
+function print_usage {
+ echo "Print the configuration of an ONOS cell."
+ echo "If no arguments are specified, it will print the configuration for the default"
+ echo "ONOS cell as specified in the 'ONOS_CELL' environmental variable."
+ echo
+ echo "Optional arguments:"
+ echo " [cell-name] Print the configuration of 'cell-name'"
+ echo " [-h | --help] Print this help"
+}
+
+if [ "${1}" = "-h" -o "${1}" = "--help" ]; then
+ print_usage
+ exit 0
+fi
+
+if [ -n "${1}" ]; then
+ cell="${1}"
+else
+ if [ -z "${ONOS_CELL}" ]; then
+ echo "Environmental variable 'ONOS_CELL' is not defiled"
+ exit 1
+ else
+ cell="${ONOS_CELL}"
+ fi
+fi
+
+if [ ! -f $ONOS_ROOT/tools/test/cells/${cell} ]; then
+ echo "No such cell: ${cell}"
+ exit 1
+fi
+
+# Load the cell setup
+. $ONOS_ROOT/tools/test/cells/${cell}
+
+echo "ONOS_CELL=${ONOS_CELL}"
+echo "ONOS_NIC=${ONOS_NIC}"
+for n in {1..9}; do
+ ocn="OC${n}"
+ if [ -n "${!ocn}" ]; then
+ echo "$ocn=${!ocn}"
+ fi
+done
+echo "OCN=${OCN}"
+echo "OCI=${OCI}"
+echo "ONOS_FEATURES=${ONOS_FEATURES}"
diff --git a/tools/test/bin/onos-ssh b/tools/test/bin/onos-ssh
index f475f25..a7be77a 100755
--- a/tools/test/bin/onos-ssh
+++ b/tools/test/bin/onos-ssh
@@ -1,7 +1,7 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# Logs in to the remote ONOS node.
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
. $ONOS_ROOT/tools/build/envDefaults
diff --git a/tools/test/bin/onos-start-network b/tools/test/bin/onos-start-network
index c8245ab..1e162fb 100755
--- a/tools/test/bin/onos-start-network
+++ b/tools/test/bin/onos-start-network
@@ -1,7 +1,7 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# Verifies connectivity to each node in ONOS cell.
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
. $ONOS_ROOT/tools/build/envDefaults
diff --git a/tools/test/bin/onos-uninstall b/tools/test/bin/onos-uninstall
index 99588c3..e2a2816 100755
--- a/tools/test/bin/onos-uninstall
+++ b/tools/test/bin/onos-uninstall
@@ -1,7 +1,7 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# Remotely stops & uninstalls ONOS on the specified node.
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
. $ONOS_ROOT/tools/build/envDefaults
diff --git a/tools/test/bin/onos-update-bundle b/tools/test/bin/onos-update-bundle
index 9998ea3..6f6422a 100755
--- a/tools/test/bin/onos-update-bundle
+++ b/tools/test/bin/onos-update-bundle
@@ -1,7 +1,7 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# Update bundle on locally running karaf.
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
. $ONOS_ROOT/tools/build/envDefaults
diff --git a/tools/test/bin/onos-verify-cell b/tools/test/bin/onos-verify-cell
index 9a5f5a9..4b3a0ee 100755
--- a/tools/test/bin/onos-verify-cell
+++ b/tools/test/bin/onos-verify-cell
@@ -1,11 +1,11 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# Verifies connectivity to each node in ONOS cell.
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
. $ONOS_ROOT/tools/build/envDefaults
for node in $(env | sort | egrep "OC[0-9N]+" | cut -d= -f2); do
printf "%s: " $node; ssh -n -o PasswordAuthentication=no $ONOS_USER@$node date
-done
\ No newline at end of file
+done
diff --git a/tools/test/bin/onos-wait-for-start b/tools/test/bin/onos-wait-for-start
index 442a07d..62cf360 100755
--- a/tools/test/bin/onos-wait-for-start
+++ b/tools/test/bin/onos-wait-for-start
@@ -1,7 +1,7 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# Waits for ONOS to reach run-level 100 on the specified remote node.
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
. $ONOS_ROOT/tools/build/envDefaults
diff --git a/tools/test/bin/onos-watch b/tools/test/bin/onos-watch
index a9eb0e3..28e88c2 100755
--- a/tools/test/bin/onos-watch
+++ b/tools/test/bin/onos-watch
@@ -1,7 +1,7 @@
#!/bin/bash
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# Monitors selected set of ONOS commands using the system watch command.
-#-------------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
. $ONOS_ROOT/tools/build/envDefaults
diff --git a/tools/test/cells/cbench b/tools/test/cells/cbench
index 692bb53..2e0b4783 100644
--- a/tools/test/cells/cbench
+++ b/tools/test/cells/cbench
@@ -1,7 +1,10 @@
# Local VirtualBox-based single ONOS instance & ONOS mininet box
+export ONOS_CELL="cbench"
+
export ONOS_NIC=192.168.56.*
export OC1="192.168.56.103"
export OCN="192.168.56.103"
+export OCI="${OC1}"
export ONOS_FEATURES="webconsole,onos-api,onos-core-trivial,onos-cli,onos-openflow,onos-app-fwd"
diff --git a/tools/test/cells/local b/tools/test/cells/local
index b535934..2edb074 100644
--- a/tools/test/cells/local
+++ b/tools/test/cells/local
@@ -1,9 +1,11 @@
# Local VirtualBox-based ONOS instances 1,2 & ONOS mininet box
+export ONOS_CELL="local"
+
export ONOS_NIC=192.168.56.*
export OC1="192.168.56.101"
export OC2="192.168.56.102"
-
export OCN="192.168.56.103"
+export OCI="${OC1}"
-
+export ONOS_FEATURES=""
diff --git a/tools/test/cells/office b/tools/test/cells/office
index a5f8cdd..72520a0 100644
--- a/tools/test/cells/office
+++ b/tools/test/cells/office
@@ -1,7 +1,9 @@
# ProxMox-based cell of ONOS instance; no mininet-box
-export ONOS_FEATURES="webconsole,onos-api,onos-core-trivial,onos-cli,onos-openflow,onos-app-fwd,onos-app-mobility,onos-app-tvue,onos-app-proxyarp"
+export ONOS_CELL="office"
export ONOS_NIC="10.128.4.*"
export OC1="10.128.4.60"
+export OCI="${OC1}"
+export ONOS_FEATURES="webconsole,onos-api,onos-core-trivial,onos-cli,onos-openflow,onos-app-fwd,onos-app-mobility,onos-app-tvue,onos-app-proxyarp"
diff --git a/tools/test/cells/prox b/tools/test/cells/prox
index 1731eb8..557388f 100644
--- a/tools/test/cells/prox
+++ b/tools/test/cells/prox
@@ -1,7 +1,11 @@
# ProxMox-based cell of ONOS instances 1,2 & ONOS mininet box
+export ONOS_CELL="prox"
+
export ONOS_NIC="10.1.9.*"
export OC1="10.1.9.94"
export OC2="10.1.9.82"
-
export OCN="10.1.9.93"
+export OCI="${OC1}"
+
+export ONOS_FEATURES=""
diff --git a/tools/test/cells/single b/tools/test/cells/single
index bc969f3..7c03ef4 100644
--- a/tools/test/cells/single
+++ b/tools/test/cells/single
@@ -1,6 +1,10 @@
# Local VirtualBox-based single ONOS instance & ONOS mininet box
+export ONOS_CELL="single"
+
export ONOS_NIC=192.168.56.*
export OC1="192.168.56.101"
export OCN="192.168.56.103"
+export OCI="${OC1}"
+export ONOS_FEATURES=""
diff --git a/tools/test/cells/triple b/tools/test/cells/triple
index baae31a..104eb05 100644
--- a/tools/test/cells/triple
+++ b/tools/test/cells/triple
@@ -1,9 +1,12 @@
# Local VirtualBox-based ONOS instances 1,2,3 & ONOS mininet box
+export ONOS_CELL="triple"
+
export ONOS_NIC=192.168.56.*
export OC1="192.168.56.101"
export OC2="192.168.56.102"
export OC3="192.168.56.104"
-
export OCN="192.168.56.103"
+export OCI="${OC1}"
+export ONOS_FEATURES=""
diff --git a/utils/misc/src/main/java/org/onlab/packet/IpAddress.java b/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
index 44a018e..fef0cfd 100644
--- a/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
+++ b/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
@@ -181,6 +181,15 @@
return address;
}
+ public int toRealInt() {
+ int val = 0;
+ for (int i = 0; i < octets.length; i++) {
+ val <<= 8;
+ val |= octets[i] & 0xff;
+ }
+ return val;
+ }
+
/**
* Helper for computing the mask value from CIDR.
*
diff --git a/utils/misc/src/main/java/org/onlab/util/NewConcurrentHashMap.java b/utils/misc/src/main/java/org/onlab/util/NewConcurrentHashMap.java
index bd17867..85a912a 100644
--- a/utils/misc/src/main/java/org/onlab/util/NewConcurrentHashMap.java
+++ b/utils/misc/src/main/java/org/onlab/util/NewConcurrentHashMap.java
@@ -3,7 +3,6 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
-import org.apache.commons.lang3.concurrent.ConcurrentException;
import org.apache.commons.lang3.concurrent.ConcurrentInitializer;
/**
@@ -27,7 +26,7 @@
}
@Override
- public ConcurrentMap<K, V> get() throws ConcurrentException {
+ public ConcurrentMap<K, V> get() {
return new ConcurrentHashMap<>();
}
}
diff --git a/utils/misc/src/test/java/org/onlab/packet/IpPrefixTest.java b/utils/misc/src/test/java/org/onlab/packet/IpPrefixTest.java
index 297a0f3..5f2bd52 100644
--- a/utils/misc/src/test/java/org/onlab/packet/IpPrefixTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/IpPrefixTest.java
@@ -108,5 +108,10 @@
IpAddress addr = IpAddress.valueOf("192.168.10.1");
assertTrue(intf.contains(addr));
+
+ IpPrefix intf1 = IpPrefix.valueOf("10.0.0.101/24");
+ IpAddress addr1 = IpAddress.valueOf("10.0.0.4");
+
+ assertTrue(intf1.contains(addr1));
}
}
diff --git a/utils/nio/src/test/java/org/onlab/nio/AbstractLoopTest.java b/utils/nio/src/test/java/org/onlab/nio/AbstractLoopTest.java
index 91fc0d7..094a093 100644
--- a/utils/nio/src/test/java/org/onlab/nio/AbstractLoopTest.java
+++ b/utils/nio/src/test/java/org/onlab/nio/AbstractLoopTest.java
@@ -15,7 +15,7 @@
*/
public abstract class AbstractLoopTest {
- protected static final long MAX_MS_WAIT = 500;
+ protected static final long MAX_MS_WAIT = 1500;
/** Block on specified countdown latch. Return when countdown reaches
* zero, or fail the test if the {@value #MAX_MS_WAIT} ms timeout expires.
diff --git a/web/gui/src/main/webapp/index.html b/web/gui/src/main/webapp/index.html
index f959f93..d68a706 100644
--- a/web/gui/src/main/webapp/index.html
+++ b/web/gui/src/main/webapp/index.html
@@ -2,6 +2,9 @@
<html>
<head>
<title>ONOS GUI</title>
+
+ <script src="libs/d3.min.js"></script>
+ <script src="libs/jquery-2.1.1.min.js"></script>
</head>
<body>
<h1>ONOS GUI</h1>