Ayaka Koshibe

Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next

Showing 88 changed files with 1450 additions and 438 deletions
......@@ -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>
......
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");
}
}
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);
}
......@@ -5,6 +5,11 @@ import static org.slf4j.LoggerFactory.getLogger;
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 @@ public class SdnIp {
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
......
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();
}
}
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();
}
}
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;
}
}
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();
}
}
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();
}
}
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(":", "");
}
}
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);
}
/**
* SDN-IP configuration.
*/
package org.onlab.onos.sdnip.config;
\ No newline at end of file
......@@ -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>
......
......@@ -20,6 +20,7 @@ public class DefaultHost extends AbstractElement implements Host {
private final MacAddress mac;
private final VlanId vlan;
private final HostLocation location;
// FIXME: should be IpAddress
private final Set<IpPrefix> ips;
/**
......
......@@ -38,6 +38,7 @@ public interface Host extends Element {
*
* @return set of IP addresses; empty if no IP address is bound
*/
// FIXME: Switch to IpAddress
Set<IpPrefix> ipAddresses();
/**
......
......@@ -4,8 +4,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Objects;
import org.onlab.onos.net.link.LinkDescription;
import com.google.common.base.MoreObjects;
// TODO Consider renaming.
......@@ -69,16 +67,6 @@ public final class LinkKey {
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);
......
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 @@ public interface HostAdminService {
*/
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);
}
......
......@@ -37,6 +37,7 @@ public interface HostDescription extends Description {
*
* @return host IP address
*/
// FIXME: Switch to IpAddress
IpPrefix ipAddress();
}
......
......@@ -110,6 +110,23 @@ public interface HostService {
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
......
......@@ -29,6 +29,7 @@ public interface HostStore extends Store<HostEvent, HostStoreDelegate> {
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 @@ public interface HostStore extends Store<HostEvent, HostStoreDelegate> {
* @param ip ip address
* @return set of hosts with the given IP
*/
// FIXME: Switch to IpAddress
Set<Host> getHosts(IpPrefix ip);
/**
......
......@@ -17,6 +17,7 @@ import com.google.common.base.MoreObjects;
public class PortAddresses {
private final ConnectPoint connectPoint;
// TODO: Should this be IpAddress or IpPrefix?
private final Set<IpPrefix> ipAddresses;
private final MacAddress macAddress;
......
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 @@ public interface ProxyArpService {
* 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
......
......@@ -75,4 +75,14 @@ public class HostServiceAdapter implements HostService {
public void removeListener(HostListener listener) {
}
@Override
public Set<PortAddresses> getAddressBindings() {
return null;
}
@Override
public PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint) {
return null;
}
}
......
......@@ -5,6 +5,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
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.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.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.device.DeviceService;
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.packet.PacketService;
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 @@ public class ProxyArpManager implements ProxyArpService {
}
@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 @@ public class ProxyArpManager implements ProxyArpService {
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 @@ public class ProxyArpManager implements ProxyArpService {
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 @@ public class ProxyArpManager implements ProxyArpService {
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 @@ public class ProxyArpManager implements ProxyArpService {
}
/**
* 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 @@ public class ProxyArpManager implements ProxyArpService {
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;
}
......
......@@ -13,6 +13,7 @@ import java.util.Arrays;
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.device.DeviceService;
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 @@ import com.google.common.collect.Sets;
*/
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 @@ public class ProxyArpManagerTest {
* 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 @@ public class ProxyArpManagerTest {
createDevices(NUM_DEVICES, NUM_PORTS_PER_DEVICE);
createLinks(NUM_DEVICES);
addAddressBindings();
}
/**
......@@ -138,10 +145,11 @@ public class ProxyArpManagerTest {
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 @@ public class ProxyArpManagerTest {
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 @@ public class ProxyArpManagerTest {
*/
@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 @@ public class ProxyArpManagerTest {
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 @@ public class ProxyArpManagerTest {
*/
@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 @@ public class ProxyArpManagerTest {
Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
proxyArp.reply(arpRequest);
proxyArp.reply(arpRequest, getLocation(5));
verifyFlood(arpRequest);
}
......@@ -262,10 +295,10 @@ public class ProxyArpManagerTest {
*/
@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 @@ public class ProxyArpManagerTest {
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 @@ public class ProxyArpManagerTest {
});
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 @@ public class ProxyArpManagerTest {
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.
*
......
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 org.onlab.onos.net.topology.TopologyProviderService;
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 @@ public class DefaultTopologyProviderTest {
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 @@ public class DefaultTopologyProviderTest {
}
@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 @@ public class DefaultTopologyProviderTest {
@Override
public void topologyChanged(GraphDescription graphDescription, List<Event> reasons) {
graphDesc = graphDescription;
topologyChangedCounts.arrive();
}
}
......
......@@ -18,7 +18,6 @@ import org.onlab.onos.cluster.DefaultControllerNode;
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 @@ public class DistributedClusterStore
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)
......
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
package org.onlab.onos.store.cluster.messaging;
// FIXME: not used any more? remove
/**
* Service for encoding &amp; 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);
}
......@@ -4,8 +4,6 @@ import static com.google.common.base.Preconditions.checkArgument;
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.ClusterService;
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 @@ import org.slf4j.LoggerFactory;
@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 @@ public class ClusterCommunicationManager
@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 @@ public class ClusterCommunicationManager
}
@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 @@ public class ClusterCommunicationManager
}
@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 @@ public class ClusterCommunicationManager
}
@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 @@ public class ClusterCommunicationManager
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 @@ public class ClusterCommunicationManager
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;
......
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);
}
}
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");
}
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;
}
}
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;
}
}
......@@ -9,7 +9,6 @@ import com.google.common.collect.Maps;
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.common.impl.Timestamped;
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 @@ public class GossipLinkStore
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 @@ public class GossipLinkStore
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 @@ public class GossipLinkStore
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 @@ public class GossipLinkStore
// 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 @@ public class GossipLinkStore
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 @@ public class GossipLinkStore
}
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 @@ public class GossipLinkStore
* @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 @@ public class GossipLinkStore
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 @@ public class GossipLinkStore
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 @@ public class GossipLinkStore
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 @@ public class GossipLinkStore
}
}
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 @@ public class GossipLinkStore
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 @@ public class GossipLinkStore
@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);
}
......
......@@ -40,22 +40,18 @@ public class ClusterCommunicationManagerTest {
@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 @@ public class ClusterCommunicationManagerTest {
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 @@ public class ClusterCommunicationManagerTest {
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());
......
......@@ -151,7 +151,7 @@ public class DistributedLinkStore
@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);
......
......@@ -35,6 +35,7 @@ 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 using trivial in-memory
* implementation.
......
......@@ -149,7 +149,7 @@ public class SimpleLinkStore
@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) {
......
......@@ -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>
......
......@@ -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>
......
......@@ -161,10 +161,10 @@ public class FlowModBuilder {
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 @@ public class FlowModBuilder {
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:
......
......@@ -9,6 +9,10 @@ export KARAF_ZIP=${KARAF_ZIP:-~/Downloads/apache-karaf-3.0.1.zip}
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')}
......
#!/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
......
#!/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
......
#!/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
......
......@@ -61,15 +61,14 @@ function cell {
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
......
#!/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 @@ projects=$(find $ONOS_ROOT -name '*.java' \
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}
......
#!/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 @@ project=${src/src*/}
target=$project/target
[ $target -nt ${src}$javaFile ] || echo ${src/src*/}
......
#!/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
......
#!/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 "$@"
......
#!/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 "$@"
......
#!/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
......
#!/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
......
#!/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 @@ ssh $remote "
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/
......
#!/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
......
#!/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
......
#!/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 @@ ssh $remote "
[ -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
......
#!/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)"
......
#!/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
#!/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
......
#!/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
......
#!/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
......
#!/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
......
#!/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}"
......
#!/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}"
#!/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
......
#!/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
......
#!/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
......
#!/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
......
#!/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
......
#!/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
......
#!/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
......
# 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"
......
# 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=""
......
# 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"
......
# 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=""
......
# 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=""
......
# 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=""
......
......@@ -181,6 +181,15 @@ public final class IpAddress {
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.
*
......
......@@ -3,7 +3,6 @@ package org.onlab.util;
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 @@ public final class NewConcurrentHashMap<K, V>
}
@Override
public ConcurrentMap<K, V> get() throws ConcurrentException {
public ConcurrentMap<K, V> get() {
return new ConcurrentHashMap<>();
}
}
......
......@@ -108,5 +108,10 @@ public class IpPrefixTest {
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));
}
}
......
......@@ -15,7 +15,7 @@ import static org.onlab.util.Tools.namedThreads;
*/
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.
......
......@@ -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>
......