alshabib

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

Conflicts:
	core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/DistributedDeviceStore.java
	core/store/hz/net/src/test/java/org/onlab/onos/store/device/impl/DistributedDeviceStoreTest.java
	features/features.xml
	tools/test/cells/office

Change-Id: I08e6d7c6a0bdaae072dd50ff7ac36d94f16d77e1
Showing 151 changed files with 3442 additions and 221 deletions
<?xml version="1.0" encoding="UTF-8"?>
<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>
<parent>
<groupId>org.onlab.onos</groupId>
<artifactId>onos-apps</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-app-calendar</artifactId>
<packaging>bundle</packaging>
<description>ONOS simple calendaring REST interface for intents</description>
<properties>
<web.context>/onos/calendar</web.context>
</properties>
<dependencies>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-rest</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-servlet</artifactId>
</dependency>
<dependency>
<groupId>com.sun.jersey.jersey-test-framework</groupId>
<artifactId>jersey-test-framework-core</artifactId>
<version>1.18.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.sun.jersey.jersey-test-framework</groupId>
<artifactId>jersey-test-framework-grizzly2</artifactId>
<version>1.18.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<_wab>src/main/webapp/</_wab>
<Bundle-SymbolicName>
${project.groupId}.${project.artifactId}
</Bundle-SymbolicName>
<Import-Package>
org.osgi.framework,
javax.ws.rs,javax.ws.rs.core,
com.sun.jersey.api.core,
com.sun.jersey.spi.container.servlet,
com.sun.jersey.server.impl.container.servlet,
org.onlab.packet.*,
org.onlab.rest.*,
org.onlab.onos.*
</Import-Package>
<Web-ContextPath>${web.context}</Web-ContextPath>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
package org.onlab.onos.calendar;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.intent.IntentService;
import org.onlab.rest.BaseResource;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;
import java.net.URI;
import static org.onlab.onos.net.PortNumber.portNumber;
/**
* Web resource for triggering calendared intents.
*/
@Path("intent")
public class BandwidthCalendarResource extends BaseResource {
@POST
@Path("{src}/{dst}/{srcPort}/{dstPort}/{bandwidth}")
public Response createIntent(@PathParam("src") String src,
@PathParam("dst") String dst,
@PathParam("srcPort") String srcPort,
@PathParam("dstPort") String dstPort,
@PathParam("bandwidth") String bandwidth) {
// TODO: implement calls to intent framework
IntentService service = get(IntentService.class);
ConnectPoint srcPoint = new ConnectPoint(deviceId(src), portNumber(srcPort));
ConnectPoint dstPoint = new ConnectPoint(deviceId(dst), portNumber(dstPort));
return Response.ok("Yo! We got src=" + srcPoint + "; dst=" + dstPoint +
"; bw=" + bandwidth + "; intent service " + service).build();
}
private DeviceId deviceId(String dpid) {
return DeviceId.deviceId(URI.create("of:" + dpid));
}
}
/**
* Application providing integration between OSCARS and ONOS intent
* framework via REST API.
*/
package org.onlab.onos.calendar;
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="ONOS" version="2.5">
<display-name>ONOS GUI</display-name>
<servlet>
<servlet-name>JAX-RS Service</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>org.onlab.onos.calendar</param-value>
</init-param>
<load-on-startup>10</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>JAX-RS Service</servlet-name>
<url-pattern>/rs/*</url-pattern>
</servlet-mapping>
</web-app>
\ No newline at end of file
......@@ -16,4 +16,11 @@
<description>ONOS simple reactive forwarding app</description>
<dependencies>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.compendium</artifactId>
</dependency>
</dependencies>
</project>
......
package org.onlab.onos.fwd;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.Set;
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.Modified;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.onos.ApplicationId;
......@@ -29,8 +27,14 @@ import org.onlab.onos.net.packet.PacketProcessor;
import org.onlab.onos.net.packet.PacketService;
import org.onlab.onos.net.topology.TopologyService;
import org.onlab.packet.Ethernet;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import java.util.Dictionary;
import java.util.Set;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Sample reactive forwarding application.
*/
......@@ -61,6 +65,10 @@ public class ReactiveForwarding {
private ApplicationId appId;
@Property(name = "enabled", boolValue = true,
label = "Enable forwarding; default is true")
private boolean isEnabled = true;
@Activate
public void activate() {
appId = coreService.registerApplication("org.onlab.onos.fwd");
......@@ -76,6 +84,22 @@ public class ReactiveForwarding {
log.info("Stopped");
}
@Modified
public void modified(ComponentContext context) {
Dictionary properties = context.getProperties();
String flag = (String) properties.get("enabled");
if (flag != null) {
boolean enabled = flag.equals("true");
if (isEnabled != enabled) {
isEnabled = enabled;
if (!isEnabled) {
flowRuleService.removeFlowRulesById(appId);
}
log.info("Reconfigured. Forwarding is {}",
isEnabled ? "enabled" : "disabled");
}
}
}
/**
* Packet processor responsible for forwarding packets along their paths.
......@@ -86,7 +110,7 @@ public class ReactiveForwarding {
public void process(PacketContext context) {
// Stop processing if the packet has been handled, since we
// can't do any more to it.
if (context.isHandled()) {
if (!isEnabled || context.isHandled()) {
return;
}
......@@ -114,8 +138,8 @@ public class ReactiveForwarding {
// Otherwise, get a set of paths that lead from here to the
// destination edge switch.
Set<Path> paths = topologyService.getPaths(topologyService.currentTopology(),
pkt.receivedFrom().deviceId(),
dst.location().deviceId());
pkt.receivedFrom().deviceId(),
dst.location().deviceId());
if (paths.isEmpty()) {
// If there are no paths, flood and bail.
flood(context);
......@@ -127,8 +151,8 @@ public class ReactiveForwarding {
Path path = pickForwardPath(paths, pkt.receivedFrom().port());
if (path == null) {
log.warn("Doh... don't know where to go... {} -> {} received on {}",
ethPkt.getSourceMAC(), ethPkt.getDestinationMAC(),
pkt.receivedFrom());
ethPkt.getSourceMAC(), ethPkt.getDestinationMAC(),
pkt.receivedFrom());
flood(context);
return;
}
......@@ -152,7 +176,7 @@ public class ReactiveForwarding {
// Floods the specified packet if permissible.
private void flood(PacketContext context) {
if (topologyService.isBroadcastPoint(topologyService.currentTopology(),
context.inPacket().receivedFrom())) {
context.inPacket().receivedFrom())) {
packetOut(context, PortNumber.FLOOD);
} else {
context.block();
......@@ -174,18 +198,17 @@ public class ReactiveForwarding {
Ethernet inPkt = context.inPacket().parsed();
TrafficSelector.Builder builder = DefaultTrafficSelector.builder();
builder.matchEthType(inPkt.getEtherType())
.matchEthSrc(inPkt.getSourceMAC())
.matchEthDst(inPkt.getDestinationMAC())
.matchInport(context.inPacket().receivedFrom().port());
.matchEthSrc(inPkt.getSourceMAC())
.matchEthDst(inPkt.getDestinationMAC())
.matchInport(context.inPacket().receivedFrom().port());
TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
treat.setOutput(portNumber);
FlowRule f = new DefaultFlowRule(context.inPacket().receivedFrom().deviceId(),
builder.build(), treat.build(), PRIORITY, appId, TIMEOUT);
builder.build(), treat.build(), PRIORITY, appId, TIMEOUT);
flowRuleService.applyFlowRules(f);
}
}
......
......@@ -25,6 +25,7 @@
<module>proxyarp</module>
<module>config</module>
<module>sdnip</module>
<module>calendar</module>
</modules>
<properties>
......
......@@ -36,6 +36,36 @@
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-thirdparty</artifactId>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-misc</artifactId>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onos-cli</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.karaf.shell</groupId>
<artifactId>org.apache.karaf.shell.console</artifactId>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
......
......@@ -126,8 +126,8 @@ public class PeerConnectivity {
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPProtocol(IPv4.PROTOCOL_TCP)
.matchIPSrc(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH))
.matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH))
.matchIPSrc(IpPrefix.valueOf(bgpdAddress.toInt(), IPV4_BIT_LENGTH))
.matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toInt(), IPV4_BIT_LENGTH))
.matchTcpDst(BGP_PORT)
.build();
......@@ -147,8 +147,8 @@ public class PeerConnectivity {
selector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPProtocol(IPv4.PROTOCOL_TCP)
.matchIPSrc(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH))
.matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH))
.matchIPSrc(IpPrefix.valueOf(bgpdAddress.toInt(), IPV4_BIT_LENGTH))
.matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toInt(), IPV4_BIT_LENGTH))
.matchTcpSrc(BGP_PORT)
.build();
......@@ -165,8 +165,8 @@ public class PeerConnectivity {
selector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPProtocol(IPv4.PROTOCOL_TCP)
.matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH))
.matchIPDst(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH))
.matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toInt(), IPV4_BIT_LENGTH))
.matchIPDst(IpPrefix.valueOf(bgpdAddress.toInt(), IPV4_BIT_LENGTH))
.matchTcpDst(BGP_PORT)
.build();
......@@ -183,8 +183,8 @@ public class PeerConnectivity {
selector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPProtocol(IPv4.PROTOCOL_TCP)
.matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH))
.matchIPDst(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH))
.matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toInt(), IPV4_BIT_LENGTH))
.matchIPDst(IpPrefix.valueOf(bgpdAddress.toInt(), IPV4_BIT_LENGTH))
.matchTcpSrc(BGP_PORT)
.build();
......@@ -251,8 +251,8 @@ public class PeerConnectivity {
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPProtocol(IPv4.PROTOCOL_ICMP)
.matchIPSrc(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH))
.matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH))
.matchIPSrc(IpPrefix.valueOf(bgpdAddress.toInt(), IPV4_BIT_LENGTH))
.matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toInt(), IPV4_BIT_LENGTH))
.build();
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
......@@ -269,8 +269,8 @@ public class PeerConnectivity {
selector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPProtocol(IPv4.PROTOCOL_ICMP)
.matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH))
.matchIPDst(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH))
.matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toInt(), IPV4_BIT_LENGTH))
.matchIPDst(IpPrefix.valueOf(bgpdAddress.toInt(), IPV4_BIT_LENGTH))
.build();
PointToPointIntent reversedIntent = new PointToPointIntent(
......
package org.onlab.onos.sdnip;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Objects;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import com.google.common.base.MoreObjects;
/**
* Represents a route entry for an IP prefix.
*/
public class RouteEntry {
private final IpPrefix prefix; // The IP prefix
private final IpAddress nextHop; // Next-hop IP address
/**
* Class constructor.
*
* @param prefix the IP prefix of the route
* @param nextHop the next hop IP address for the route
*/
public RouteEntry(IpPrefix prefix, IpAddress nextHop) {
this.prefix = checkNotNull(prefix);
this.nextHop = checkNotNull(nextHop);
}
/**
* Returns the IP prefix of the route.
*
* @return the IP prefix of the route
*/
public IpPrefix prefix() {
return prefix;
}
/**
* Returns the next hop IP address for the route.
*
* @return the next hop IP address for the route
*/
public IpAddress nextHop() {
return nextHop;
}
/**
* Creates the binary string representation of an IPv4 prefix.
* The string length is equal to the prefix length.
*
* @param ip4Prefix the IPv4 prefix to use
* @return the binary string representation
*/
static String createBinaryString(IpPrefix ip4Prefix) {
if (ip4Prefix.prefixLength() == 0) {
return "";
}
StringBuilder result = new StringBuilder(ip4Prefix.prefixLength());
long value = ip4Prefix.toInt();
for (int i = 0; i < ip4Prefix.prefixLength(); i++) {
long mask = 1 << (IpAddress.MAX_INET_MASK - 1 - i);
result.append(((value & mask) == 0) ? "0" : "1");
}
return result.toString();
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
//
// NOTE: Subclasses are considered as change of identity, hence
// equals() will return false if the class type doesn't match.
//
if (other == null || getClass() != other.getClass()) {
return false;
}
RouteEntry otherRoute = (RouteEntry) other;
return Objects.equals(this.prefix, otherRoute.prefix) &&
Objects.equals(this.nextHop, otherRoute.nextHop);
}
@Override
public int hashCode() {
return Objects.hash(prefix, nextHop);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("prefix", prefix)
.add("nextHop", nextHop)
.toString();
}
}
package org.onlab.onos.sdnip;
/**
* An interface to receive route updates from route providers.
*/
public interface RouteListener {
/**
* Receives a route update from a route provider.
*
* @param routeUpdate the updated route information
*/
public void update(RouteUpdate routeUpdate);
}
package org.onlab.onos.sdnip;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Objects;
import com.google.common.base.MoreObjects;
/**
* Represents a change in routing information.
*/
public class RouteUpdate {
private final Type type; // The route update type
private final RouteEntry routeEntry; // The updated route entry
/**
* Specifies the type of a route update.
* <p/>
* Route updates can either provide updated information for a route, or
* withdraw a previously updated route.
*/
public enum Type {
/**
* The update contains updated route information for a route.
*/
UPDATE,
/**
* The update withdraws the route, meaning any previous information is
* no longer valid.
*/
DELETE
}
/**
* Class constructor.
*
* @param type the type of the route update
* @param routeEntry the route entry with the update
*/
public RouteUpdate(Type type, RouteEntry routeEntry) {
this.type = type;
this.routeEntry = checkNotNull(routeEntry);
}
/**
* Returns the type of the route update.
*
* @return the type of the update
*/
public Type type() {
return type;
}
/**
* Returns the route entry the route update is for.
*
* @return the route entry the route update is for
*/
public RouteEntry routeEntry() {
return routeEntry;
}
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (!(other instanceof RouteUpdate)) {
return false;
}
RouteUpdate otherUpdate = (RouteUpdate) other;
return Objects.equals(this.type, otherUpdate.type) &&
Objects.equals(this.routeEntry, otherUpdate.routeEntry);
}
@Override
public int hashCode() {
return Objects.hash(type, routeEntry);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("type", type)
.add("routeEntry", routeEntry)
.toString();
}
}
This diff is collapsed. Click to expand it.
......@@ -2,21 +2,30 @@ package org.onlab.onos.sdnip;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.Collection;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.onos.net.host.HostService;
import org.onlab.onos.net.intent.IntentService;
import org.onlab.onos.sdnip.RouteUpdate.Type;
import org.onlab.onos.sdnip.bgp.BgpRouteEntry;
import org.onlab.onos.sdnip.bgp.BgpSessionManager;
import org.onlab.onos.sdnip.config.SdnIpConfigReader;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.slf4j.Logger;
/**
* Placeholder SDN-IP component.
* Component for the SDN-IP peering application.
*/
@Component(immediate = true)
public class SdnIp {
@Service
public class SdnIp implements SdnIpService {
private final Logger log = getLogger(getClass());
......@@ -28,6 +37,8 @@ public class SdnIp {
private SdnIpConfigReader config;
private PeerConnectivity peerConnectivity;
private Router router;
private BgpSessionManager bgpSessionManager;
@Activate
protected void activate() {
......@@ -41,10 +52,31 @@ public class SdnIp {
peerConnectivity = new PeerConnectivity(config, interfaceService, intentService);
peerConnectivity.start();
router = new Router(intentService, hostService, config, interfaceService);
router.start();
bgpSessionManager = new BgpSessionManager(router);
bgpSessionManager.startUp(2000); // TODO
// TODO need to disable link discovery on external ports
router.update(new RouteUpdate(Type.UPDATE, new RouteEntry(
IpPrefix.valueOf("172.16.20.0/24"),
IpAddress.valueOf("192.168.10.1"))));
}
@Deactivate
protected void deactivate() {
log.info("Stopped");
}
@Override
public Collection<BgpRouteEntry> getBgpRoutes() {
return bgpSessionManager.getBgpRoutes();
}
@Override
public Collection<RouteEntry> getRoutes() {
return router.getRoutes();
}
}
......
package org.onlab.onos.sdnip;
import java.util.Collection;
import org.onlab.onos.sdnip.bgp.BgpRouteEntry;
/**
* Service interface exported by SDN-IP.
*/
public interface SdnIpService {
/**
* Gets the BGP routes.
*
* @return the BGP routes
*/
public Collection<BgpRouteEntry> getBgpRoutes();
/**
* Gets all the routes known to SDN-IP.
*
* @return the SDN-IP routes
*/
public Collection<RouteEntry> getRoutes();
}
package org.onlab.onos.sdnip.bgp;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.frame.FrameDecoder;
import org.onlab.onos.sdnip.bgp.BgpConstants.Notifications.MessageHeaderError;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class for handling the decoding of the BGP messages.
*/
class BgpFrameDecoder extends FrameDecoder {
private static final Logger log =
LoggerFactory.getLogger(BgpFrameDecoder.class);
private final BgpSession bgpSession;
/**
* Constructor for a given BGP Session.
*
* @param bgpSession the BGP session state to use.
*/
BgpFrameDecoder(BgpSession bgpSession) {
this.bgpSession = bgpSession;
}
@Override
protected Object decode(ChannelHandlerContext ctx,
Channel channel,
ChannelBuffer buf) throws Exception {
//
// NOTE: If we close the channel during the decoding, we might still
// see some incoming messages while the channel closing is completed.
//
if (bgpSession.isClosed()) {
return null;
}
log.trace("BGP Peer: decode(): remoteAddr = {} localAddr = {} " +
"messageSize = {}",
ctx.getChannel().getRemoteAddress(),
ctx.getChannel().getLocalAddress(),
buf.readableBytes());
// Test for minimum length of the BGP message
if (buf.readableBytes() < BgpConstants.BGP_HEADER_LENGTH) {
// No enough data received
return null;
}
//
// Mark the current buffer position in case we haven't received
// the whole message.
//
buf.markReaderIndex();
//
// Read and check the BGP message Marker field: it must be all ones
// (See RFC 4271, Section 4.1)
//
byte[] marker = new byte[BgpConstants.BGP_HEADER_MARKER_LENGTH];
buf.readBytes(marker);
for (int i = 0; i < marker.length; i++) {
if (marker[i] != (byte) 0xff) {
log.debug("BGP RX Error: invalid marker {} at position {}",
marker[i], i);
//
// ERROR: Connection Not Synchronized
//
// Send NOTIFICATION and close the connection
int errorCode = MessageHeaderError.ERROR_CODE;
int errorSubcode =
MessageHeaderError.CONNECTION_NOT_SYNCHRONIZED;
ChannelBuffer txMessage =
bgpSession.prepareBgpNotification(errorCode, errorSubcode,
null);
ctx.getChannel().write(txMessage);
bgpSession.closeChannel(ctx);
return null;
}
}
//
// Read and check the BGP message Length field
//
int length = buf.readUnsignedShort();
if ((length < BgpConstants.BGP_HEADER_LENGTH) ||
(length > BgpConstants.BGP_MESSAGE_MAX_LENGTH)) {
log.debug("BGP RX Error: invalid Length field {}. " +
"Must be between {} and {}",
length,
BgpConstants.BGP_HEADER_LENGTH,
BgpConstants.BGP_MESSAGE_MAX_LENGTH);
//
// ERROR: Bad Message Length
//
// Send NOTIFICATION and close the connection
ChannelBuffer txMessage =
bgpSession.prepareBgpNotificationBadMessageLength(length);
ctx.getChannel().write(txMessage);
bgpSession.closeChannel(ctx);
return null;
}
//
// Test whether the rest of the message is received:
// So far we have read the Marker (16 octets) and the
// Length (2 octets) fields.
//
int remainingMessageLen =
length - BgpConstants.BGP_HEADER_MARKER_LENGTH - 2;
if (buf.readableBytes() < remainingMessageLen) {
// No enough data received
buf.resetReaderIndex();
return null;
}
//
// Read the BGP message Type field, and process based on that type
//
int type = buf.readUnsignedByte();
remainingMessageLen--; // Adjust after reading the type
ChannelBuffer message = buf.readBytes(remainingMessageLen);
//
// Process the remaining of the message based on the message type
//
switch (type) {
case BgpConstants.BGP_TYPE_OPEN:
bgpSession.processBgpOpen(ctx, message);
break;
case BgpConstants.BGP_TYPE_UPDATE:
bgpSession.processBgpUpdate(ctx, message);
break;
case BgpConstants.BGP_TYPE_NOTIFICATION:
bgpSession.processBgpNotification(ctx, message);
break;
case BgpConstants.BGP_TYPE_KEEPALIVE:
bgpSession.processBgpKeepalive(ctx, message);
break;
default:
//
// ERROR: Bad Message Type
//
// Send NOTIFICATION and close the connection
int errorCode = MessageHeaderError.ERROR_CODE;
int errorSubcode = MessageHeaderError.BAD_MESSAGE_TYPE;
ChannelBuffer data = ChannelBuffers.buffer(1);
data.writeByte(type);
ChannelBuffer txMessage =
bgpSession.prepareBgpNotification(errorCode, errorSubcode,
data);
ctx.getChannel().write(txMessage);
bgpSession.closeChannel(ctx);
return null;
}
return null;
}
}
/**
* Implementation of the BGP protocol.
*/
package org.onlab.onos.sdnip.bgp;
\ No newline at end of file
package org.onlab.onos.sdnip.cli;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.cli.AbstractShellCommand;
import org.onlab.onos.sdnip.SdnIpService;
import org.onlab.onos.sdnip.bgp.BgpConstants;
import org.onlab.onos.sdnip.bgp.BgpRouteEntry;
/**
* Command to show the routes learned through BGP.
*/
@Command(scope = "onos", name = "bgp-routes",
description = "Lists all routes received from BGP")
public class BgpRoutesListCommand extends AbstractShellCommand {
private static final String FORMAT =
"prefix=%s, nexthop=%s, origin=%s, localpref=%s, med=%s, aspath=%s, bgpid=%s";
@Override
protected void execute() {
SdnIpService service = get(SdnIpService.class);
for (BgpRouteEntry route : service.getBgpRoutes()) {
printRoute(route);
}
}
private void printRoute(BgpRouteEntry route) {
if (route != null) {
print(FORMAT, route.prefix(), route.nextHop(),
originToString(route.getOrigin()), route.getLocalPref(),
route.getMultiExitDisc(), route.getAsPath(),
route.getBgpSession().getRemoteBgpId());
}
}
private static String originToString(int origin) {
String originString = "UNKNOWN";
switch (origin) {
case BgpConstants.Update.Origin.IGP:
originString = "IGP";
break;
case BgpConstants.Update.Origin.EGP:
originString = "EGP";
break;
case BgpConstants.Update.Origin.INCOMPLETE:
originString = "INCOMPLETE";
break;
default:
break;
}
return originString;
}
}
package org.onlab.onos.sdnip.cli;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.cli.AbstractShellCommand;
import org.onlab.onos.sdnip.RouteEntry;
import org.onlab.onos.sdnip.SdnIpService;
/**
* Command to show the list of routes in SDN-IP's routing table.
*/
@Command(scope = "onos", name = "routes",
description = "Lists all routes known to SDN-IP")
public class RoutesListCommand extends AbstractShellCommand {
private static final String FORMAT =
"prefix=%s, nexthop=%s";
@Override
protected void execute() {
SdnIpService service = get(SdnIpService.class);
for (RouteEntry route : service.getRoutes()) {
printRoute(route);
}
}
private void printRoute(RouteEntry route) {
if (route != null) {
print(FORMAT, route.prefix(), route.nextHop());
}
}
}
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
<command>
<action class="org.onlab.onos.sdnip.cli.BgpRoutesListCommand"/>
</command>
<command>
<action class="org.onlab.onos.sdnip.cli.RoutesListCommand"/>
</command>
</command-bundle>
</blueprint>
package org.onlab.onos.sdnip;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
/**
* Unit tests for the RouteEntry class.
*/
public class RouteEntryTest {
/**
* Tests valid class constructor.
*/
@Test
public void testConstructor() {
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
RouteEntry routeEntry = new RouteEntry(prefix, nextHop);
assertThat(routeEntry.toString(),
is("RouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8}"));
}
/**
* Tests invalid class constructor for null IPv4 prefix.
*/
@Test(expected = NullPointerException.class)
public void testInvalidConstructorNullPrefix() {
IpPrefix prefix = null;
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
new RouteEntry(prefix, nextHop);
}
/**
* Tests invalid class constructor for null IPv4 next-hop.
*/
@Test(expected = NullPointerException.class)
public void testInvalidConstructorNullNextHop() {
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = null;
new RouteEntry(prefix, nextHop);
}
/**
* Tests getting the fields of a route entry.
*/
@Test
public void testGetFields() {
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
RouteEntry routeEntry = new RouteEntry(prefix, nextHop);
assertThat(routeEntry.prefix(), is(prefix));
assertThat(routeEntry.nextHop(), is(nextHop));
}
/**
* Tests creating a binary string from IPv4 prefix.
*/
@Test
public void testCreateBinaryString() {
IpPrefix prefix;
prefix = IpPrefix.valueOf("0.0.0.0/0");
assertThat(RouteEntry.createBinaryString(prefix), is(""));
prefix = IpPrefix.valueOf("192.168.166.0/22");
assertThat(RouteEntry.createBinaryString(prefix),
is("1100000010101000101001"));
prefix = IpPrefix.valueOf("192.168.166.0/23");
assertThat(RouteEntry.createBinaryString(prefix),
is("11000000101010001010011"));
prefix = IpPrefix.valueOf("192.168.166.0/24");
assertThat(RouteEntry.createBinaryString(prefix),
is("110000001010100010100110"));
prefix = IpPrefix.valueOf("130.162.10.1/25");
assertThat(RouteEntry.createBinaryString(prefix),
is("1000001010100010000010100"));
prefix = IpPrefix.valueOf("255.255.255.255/32");
assertThat(RouteEntry.createBinaryString(prefix),
is("11111111111111111111111111111111"));
}
/**
* Tests equality of {@link RouteEntry}.
*/
@Test
public void testEquality() {
IpPrefix prefix1 = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop1 = IpAddress.valueOf("5.6.7.8");
RouteEntry routeEntry1 = new RouteEntry(prefix1, nextHop1);
IpPrefix prefix2 = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop2 = IpAddress.valueOf("5.6.7.8");
RouteEntry routeEntry2 = new RouteEntry(prefix2, nextHop2);
assertThat(routeEntry1, is(routeEntry2));
}
/**
* Tests non-equality of {@link RouteEntry}.
*/
@Test
public void testNonEquality() {
IpPrefix prefix1 = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop1 = IpAddress.valueOf("5.6.7.8");
RouteEntry routeEntry1 = new RouteEntry(prefix1, nextHop1);
IpPrefix prefix2 = IpPrefix.valueOf("1.2.3.0/25"); // Different
IpAddress nextHop2 = IpAddress.valueOf("5.6.7.8");
RouteEntry routeEntry2 = new RouteEntry(prefix2, nextHop2);
IpPrefix prefix3 = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop3 = IpAddress.valueOf("5.6.7.9"); // Different
RouteEntry routeEntry3 = new RouteEntry(prefix3, nextHop3);
assertThat(routeEntry1, is(not(routeEntry2)));
assertThat(routeEntry1, is(not(routeEntry3)));
}
/**
* Tests object string representation.
*/
@Test
public void testToString() {
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
RouteEntry routeEntry = new RouteEntry(prefix, nextHop);
assertThat(routeEntry.toString(),
is("RouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8}"));
}
}
package org.onlab.onos.sdnip.bgp;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import org.junit.Test;
/**
* Unit tests for the BgpRouteEntry.AsPath class.
*/
public class AsPathTest {
/**
* Generates an AS Path.
*
* @return a generated AS Path
*/
private BgpRouteEntry.AsPath generateAsPath() {
ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
segmentAsNumbers1.add((long) 1);
segmentAsNumbers1.add((long) 2);
segmentAsNumbers1.add((long) 3);
BgpRouteEntry.PathSegment pathSegment1 =
new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
pathSegments.add(pathSegment1);
//
byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
segmentAsNumbers2.add((long) 4);
segmentAsNumbers2.add((long) 5);
segmentAsNumbers2.add((long) 6);
BgpRouteEntry.PathSegment pathSegment2 =
new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
pathSegments.add(pathSegment2);
//
BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
return asPath;
}
/**
* Tests valid class constructor.
*/
@Test
public void testConstructor() {
BgpRouteEntry.AsPath asPath = generateAsPath();
String expectedString =
"AsPath{pathSegments=" +
"[PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}, " +
"PathSegment{type=1, segmentAsNumbers=[4, 5, 6]}]}";
assertThat(asPath.toString(), is(expectedString));
}
/**
* Tests invalid class constructor for null Path Segments.
*/
@Test(expected = NullPointerException.class)
public void testInvalidConstructorNullPathSegments() {
ArrayList<BgpRouteEntry.PathSegment> pathSegments = null;
new BgpRouteEntry.AsPath(pathSegments);
}
/**
* Tests getting the fields of an AS Path.
*/
@Test
public void testGetFields() {
// Create the fields to compare against
ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
segmentAsNumbers1.add((long) 1);
segmentAsNumbers1.add((long) 2);
segmentAsNumbers1.add((long) 3);
BgpRouteEntry.PathSegment pathSegment1 =
new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
pathSegments.add(pathSegment1);
//
byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
segmentAsNumbers2.add((long) 4);
segmentAsNumbers2.add((long) 5);
segmentAsNumbers2.add((long) 6);
BgpRouteEntry.PathSegment pathSegment2 =
new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
pathSegments.add(pathSegment2);
// Generate the entry to test
BgpRouteEntry.AsPath asPath = generateAsPath();
assertThat(asPath.getPathSegments(), is(pathSegments));
}
/**
* Tests getting the AS Path Length.
*/
@Test
public void testGetAsPathLength() {
BgpRouteEntry.AsPath asPath = generateAsPath();
assertThat(asPath.getAsPathLength(), is(4));
// Create an empty AS Path
ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
asPath = new BgpRouteEntry.AsPath(pathSegments);
assertThat(asPath.getAsPathLength(), is(0));
}
/**
* Tests equality of {@link BgpRouteEntry.AsPath}.
*/
@Test
public void testEquality() {
BgpRouteEntry.AsPath asPath1 = generateAsPath();
BgpRouteEntry.AsPath asPath2 = generateAsPath();
assertThat(asPath1, is(asPath2));
}
/**
* Tests non-equality of {@link BgpRouteEntry.AsPath}.
*/
@Test
public void testNonEquality() {
BgpRouteEntry.AsPath asPath1 = generateAsPath();
// Setup AS Path 2
ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
segmentAsNumbers1.add((long) 1);
segmentAsNumbers1.add((long) 2);
segmentAsNumbers1.add((long) 3);
BgpRouteEntry.PathSegment pathSegment1 =
new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
pathSegments.add(pathSegment1);
//
byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
segmentAsNumbers2.add((long) 4);
segmentAsNumbers2.add((long) 55); // Different
segmentAsNumbers2.add((long) 6);
BgpRouteEntry.PathSegment pathSegment2 =
new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
pathSegments.add(pathSegment2);
//
BgpRouteEntry.AsPath asPath2 = new BgpRouteEntry.AsPath(pathSegments);
assertThat(asPath1, is(not(asPath2)));
}
/**
* Tests object string representation.
*/
@Test
public void testToString() {
BgpRouteEntry.AsPath asPath = generateAsPath();
String expectedString =
"AsPath{pathSegments=" +
"[PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}, " +
"PathSegment{type=1, segmentAsNumbers=[4, 5, 6]}]}";
assertThat(asPath.toString(), is(expectedString));
}
}
package org.onlab.onos.sdnip.bgp;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import org.junit.Test;
/**
* Unit tests for the BgpRouteEntry.PathSegment class.
*/
public class PathSegmentTest {
/**
* Generates a Path Segment.
*
* @return a generated PathSegment
*/
private BgpRouteEntry.PathSegment generatePathSegment() {
byte pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
ArrayList<Long> segmentAsNumbers = new ArrayList<>();
segmentAsNumbers.add((long) 1);
segmentAsNumbers.add((long) 2);
segmentAsNumbers.add((long) 3);
BgpRouteEntry.PathSegment pathSegment =
new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
return pathSegment;
}
/**
* Tests valid class constructor.
*/
@Test
public void testConstructor() {
BgpRouteEntry.PathSegment pathSegment = generatePathSegment();
String expectedString =
"PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}";
assertThat(pathSegment.toString(), is(expectedString));
}
/**
* Tests invalid class constructor for null Segment AS Numbers.
*/
@Test(expected = NullPointerException.class)
public void testInvalidConstructorNullSegmentAsNumbers() {
byte pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
ArrayList<Long> segmentAsNumbers = null;
new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
}
/**
* Tests getting the fields of a Path Segment.
*/
@Test
public void testGetFields() {
// Create the fields to compare against
byte pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
ArrayList<Long> segmentAsNumbers = new ArrayList<>();
segmentAsNumbers.add((long) 1);
segmentAsNumbers.add((long) 2);
segmentAsNumbers.add((long) 3);
// Generate the entry to test
BgpRouteEntry.PathSegment pathSegment = generatePathSegment();
assertThat(pathSegment.getType(), is(pathSegmentType));
assertThat(pathSegment.getSegmentAsNumbers(), is(segmentAsNumbers));
}
/**
* Tests equality of {@link BgpRouteEntry.PathSegment}.
*/
@Test
public void testEquality() {
BgpRouteEntry.PathSegment pathSegment1 = generatePathSegment();
BgpRouteEntry.PathSegment pathSegment2 = generatePathSegment();
assertThat(pathSegment1, is(pathSegment2));
}
/**
* Tests non-equality of {@link BgpRouteEntry.PathSegment}.
*/
@Test
public void testNonEquality() {
BgpRouteEntry.PathSegment pathSegment1 = generatePathSegment();
// Setup Path Segment 2
byte pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
ArrayList<Long> segmentAsNumbers = new ArrayList<>();
segmentAsNumbers.add((long) 1);
segmentAsNumbers.add((long) 22); // Different
segmentAsNumbers.add((long) 3);
//
BgpRouteEntry.PathSegment pathSegment2 =
new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
assertThat(pathSegment1, is(not(pathSegment2)));
}
/**
* Tests object string representation.
*/
@Test
public void testToString() {
BgpRouteEntry.PathSegment pathSegment = generatePathSegment();
String expectedString =
"PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}";
assertThat(pathSegment.toString(), is(expectedString));
}
}
package org.onlab.onos.sdnip.bgp;
import java.util.Collection;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
/**
* Class for handling the remote BGP Peer session.
*/
class TestBgpPeerChannelHandler extends SimpleChannelHandler {
static final long PEER_AS = 65001;
static final int PEER_HOLDTIME = 120; // 120 seconds
final IpAddress bgpId; // The BGP ID
final long localPref; // Local preference for routes
final long multiExitDisc = 20; // MED value
ChannelHandlerContext savedCtx;
/**
* Constructor for given BGP ID.
*
* @param bgpId the BGP ID to use
* @param localPref the local preference for the routes to use
*/
TestBgpPeerChannelHandler(IpAddress bgpId,
long localPref) {
this.bgpId = bgpId;
this.localPref = localPref;
}
/**
* Closes the channel.
*/
void closeChannel() {
savedCtx.getChannel().close();
}
@Override
public void channelConnected(ChannelHandlerContext ctx,
ChannelStateEvent channelEvent) {
this.savedCtx = ctx;
// Prepare and transmit BGP OPEN message
ChannelBuffer message = prepareBgpOpen();
ctx.getChannel().write(message);
// Prepare and transmit BGP KEEPALIVE message
message = prepareBgpKeepalive();
ctx.getChannel().write(message);
}
@Override
public void channelDisconnected(ChannelHandlerContext ctx,
ChannelStateEvent channelEvent) {
// Nothing to do
}
/**
* Prepares BGP OPEN message.
*
* @return the message to transmit (BGP header included)
*/
ChannelBuffer prepareBgpOpen() {
ChannelBuffer message =
ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
message.writeByte(BgpConstants.BGP_VERSION);
message.writeShort((int) PEER_AS);
message.writeShort(PEER_HOLDTIME);
message.writeInt(bgpId.toInt());
message.writeByte(0); // No Optional Parameters
return prepareBgpMessage(BgpConstants.BGP_TYPE_OPEN, message);
}
/**
* Prepares BGP UPDATE message.
*
* @param nextHopRouter the next-hop router address for the routes to add
* @param addedRoutes the routes to add
* @param withdrawnRoutes the routes to withdraw
* @return the message to transmit (BGP header included)
*/
ChannelBuffer prepareBgpUpdate(IpAddress nextHopRouter,
Collection<IpPrefix> addedRoutes,
Collection<IpPrefix> withdrawnRoutes) {
int attrFlags;
ChannelBuffer message =
ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
ChannelBuffer pathAttributes =
ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
// Encode the Withdrawn Routes
ChannelBuffer encodedPrefixes = encodePackedPrefixes(withdrawnRoutes);
message.writeShort(encodedPrefixes.readableBytes());
message.writeBytes(encodedPrefixes);
// Encode the Path Attributes
// ORIGIN: IGP
attrFlags = 0x40; // Transitive flag
pathAttributes.writeByte(attrFlags);
pathAttributes.writeByte(BgpConstants.Update.Origin.TYPE);
pathAttributes.writeByte(1); // Data length
pathAttributes.writeByte(BgpConstants.Update.Origin.IGP);
// AS_PATH: Two Path Segments of 3 ASes each
attrFlags = 0x40; // Transitive flag
pathAttributes.writeByte(attrFlags);
pathAttributes.writeByte(BgpConstants.Update.AsPath.TYPE);
pathAttributes.writeByte(16); // Data length
byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
pathAttributes.writeByte(pathSegmentType1);
pathAttributes.writeByte(3); // Three ASes
pathAttributes.writeShort(65010); // AS=65010
pathAttributes.writeShort(65020); // AS=65020
pathAttributes.writeShort(65030); // AS=65030
byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
pathAttributes.writeByte(pathSegmentType2);
pathAttributes.writeByte(3); // Three ASes
pathAttributes.writeShort(65041); // AS=65041
pathAttributes.writeShort(65042); // AS=65042
pathAttributes.writeShort(65043); // AS=65043
// NEXT_HOP: nextHopRouter
attrFlags = 0x40; // Transitive flag
pathAttributes.writeByte(attrFlags);
pathAttributes.writeByte(BgpConstants.Update.NextHop.TYPE);
pathAttributes.writeByte(4); // Data length
pathAttributes.writeInt(nextHopRouter.toInt()); // Next-hop router
// LOCAL_PREF: localPref
attrFlags = 0x40; // Transitive flag
pathAttributes.writeByte(attrFlags);
pathAttributes.writeByte(BgpConstants.Update.LocalPref.TYPE);
pathAttributes.writeByte(4); // Data length
pathAttributes.writeInt((int) localPref); // Preference value
// MULTI_EXIT_DISC: multiExitDisc
attrFlags = 0x80; // Optional
// Non-Transitive flag
pathAttributes.writeByte(attrFlags);
pathAttributes.writeByte(BgpConstants.Update.MultiExitDisc.TYPE);
pathAttributes.writeByte(4); // Data length
pathAttributes.writeInt((int) multiExitDisc); // Preference value
// The NLRI prefixes
encodedPrefixes = encodePackedPrefixes(addedRoutes);
// Write the Path Attributes, beginning with its length
message.writeShort(pathAttributes.readableBytes());
message.writeBytes(pathAttributes);
message.writeBytes(encodedPrefixes);
return prepareBgpMessage(BgpConstants.BGP_TYPE_UPDATE, message);
}
/**
* Encodes a collection of IPv4 network prefixes in a packed format.
* <p>
* The IPv4 prefixes are encoded in the form:
* <Length, Prefix> where Length is the length in bits of the IPv4 prefix,
* and Prefix is the IPv4 prefix (padded with trailing bits to the end
* of an octet).
*
* @param prefixes the prefixes to encode
* @return the buffer with the encoded prefixes
*/
private ChannelBuffer encodePackedPrefixes(Collection<IpPrefix> prefixes) {
ChannelBuffer message =
ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
// Write each of the prefixes
for (IpPrefix prefix : prefixes) {
int prefixBitlen = prefix.prefixLength();
int prefixBytelen = (prefixBitlen + 7) / 8; // Round-up
message.writeByte(prefixBitlen);
IpAddress address = prefix.toIpAddress();
long value = address.toInt() & 0xffffffffL;
for (int i = 0; i < IpAddress.INET_LEN; i++) {
if (prefixBytelen-- == 0) {
break;
}
long nextByte =
(value >> ((IpAddress.INET_LEN - i - 1) * 8)) & 0xff;
message.writeByte((int) nextByte);
}
}
return message;
}
/**
* Prepares BGP KEEPALIVE message.
*
* @return the message to transmit (BGP header included)
*/
ChannelBuffer prepareBgpKeepalive() {
ChannelBuffer message =
ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
return prepareBgpMessage(BgpConstants.BGP_TYPE_KEEPALIVE, message);
}
/**
* Prepares BGP NOTIFICATION message.
*
* @param errorCode the BGP NOTIFICATION Error Code
* @param errorSubcode the BGP NOTIFICATION Error Subcode if applicable,
* otherwise BgpConstants.Notifications.ERROR_SUBCODE_UNSPECIFIC
* @param payload the BGP NOTIFICATION Data if applicable, otherwise null
* @return the message to transmit (BGP header included)
*/
ChannelBuffer prepareBgpNotification(int errorCode, int errorSubcode,
ChannelBuffer data) {
ChannelBuffer message =
ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
// Prepare the NOTIFICATION message payload
message.writeByte(errorCode);
message.writeByte(errorSubcode);
if (data != null) {
message.writeBytes(data);
}
return prepareBgpMessage(BgpConstants.BGP_TYPE_NOTIFICATION, message);
}
/**
* Prepares BGP message.
*
* @param type the BGP message type
* @param payload the message payload to transmit (BGP header excluded)
* @return the message to transmit (BGP header included)
*/
private ChannelBuffer prepareBgpMessage(int type, ChannelBuffer payload) {
ChannelBuffer message =
ChannelBuffers.buffer(BgpConstants.BGP_HEADER_LENGTH +
payload.readableBytes());
// Write the marker
for (int i = 0; i < BgpConstants.BGP_HEADER_MARKER_LENGTH; i++) {
message.writeByte(0xff);
}
// Write the rest of the BGP header
message.writeShort(BgpConstants.BGP_HEADER_LENGTH +
payload.readableBytes());
message.writeByte(type);
// Write the payload
message.writeBytes(payload);
return message;
}
}
package org.onlab.onos.sdnip.bgp;
import java.util.concurrent.CountDownLatch;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.frame.FrameDecoder;
import org.onlab.packet.IpAddress;
/**
* Class for handling the decoding of the BGP messages at the remote
* BGP peer session.
*/
class TestBgpPeerFrameDecoder extends FrameDecoder {
int remoteBgpVersion; // 1 octet
long remoteAs; // 2 octets
long remoteHoldtime; // 2 octets
IpAddress remoteBgpIdentifier; // 4 octets -> IPv4 address
final CountDownLatch receivedOpenMessageLatch = new CountDownLatch(1);
final CountDownLatch receivedKeepaliveMessageLatch = new CountDownLatch(1);
@Override
protected Object decode(ChannelHandlerContext ctx,
Channel channel,
ChannelBuffer buf) throws Exception {
// Test for minimum length of the BGP message
if (buf.readableBytes() < BgpConstants.BGP_HEADER_LENGTH) {
// No enough data received
return null;
}
//
// Mark the current buffer position in case we haven't received
// the whole message.
//
buf.markReaderIndex();
//
// Read and check the BGP message Marker field: it must be all ones
//
byte[] marker = new byte[BgpConstants.BGP_HEADER_MARKER_LENGTH];
buf.readBytes(marker);
for (int i = 0; i < marker.length; i++) {
if (marker[i] != (byte) 0xff) {
// ERROR: Connection Not Synchronized. Close the channel.
ctx.getChannel().close();
return null;
}
}
//
// Read and check the BGP message Length field
//
int length = buf.readUnsignedShort();
if ((length < BgpConstants.BGP_HEADER_LENGTH) ||
(length > BgpConstants.BGP_MESSAGE_MAX_LENGTH)) {
// ERROR: Bad Message Length. Close the channel.
ctx.getChannel().close();
return null;
}
//
// Test whether the rest of the message is received:
// So far we have read the Marker (16 octets) and the
// Length (2 octets) fields.
//
int remainingMessageLen =
length - BgpConstants.BGP_HEADER_MARKER_LENGTH - 2;
if (buf.readableBytes() < remainingMessageLen) {
// No enough data received
buf.resetReaderIndex();
return null;
}
//
// Read the BGP message Type field, and process based on that type
//
int type = buf.readUnsignedByte();
remainingMessageLen--; // Adjust after reading the type
ChannelBuffer message = buf.readBytes(remainingMessageLen);
//
// Process the remaining of the message based on the message type
//
switch (type) {
case BgpConstants.BGP_TYPE_OPEN:
processBgpOpen(ctx, message);
break;
case BgpConstants.BGP_TYPE_UPDATE:
// NOTE: Not used as part of the test, because ONOS does not
// originate UPDATE messages.
break;
case BgpConstants.BGP_TYPE_NOTIFICATION:
// NOTE: Not used as part of the testing (yet)
break;
case BgpConstants.BGP_TYPE_KEEPALIVE:
processBgpKeepalive(ctx, message);
break;
default:
// ERROR: Bad Message Type. Close the channel.
ctx.getChannel().close();
return null;
}
return null;
}
/**
* Processes BGP OPEN message.
*
* @param ctx the Channel Handler Context.
* @param message the message to process.
*/
private void processBgpOpen(ChannelHandlerContext ctx,
ChannelBuffer message) {
int minLength =
BgpConstants.BGP_OPEN_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH;
if (message.readableBytes() < minLength) {
// ERROR: Bad Message Length. Close the channel.
ctx.getChannel().close();
return;
}
//
// Parse the OPEN message
//
remoteBgpVersion = message.readUnsignedByte();
remoteAs = message.readUnsignedShort();
remoteHoldtime = message.readUnsignedShort();
remoteBgpIdentifier = IpAddress.valueOf((int) message.readUnsignedInt());
// Optional Parameters
int optParamLen = message.readUnsignedByte();
if (message.readableBytes() < optParamLen) {
// ERROR: Bad Message Length. Close the channel.
ctx.getChannel().close();
return;
}
message.readBytes(optParamLen); // NOTE: data ignored
// BGP OPEN message successfully received
receivedOpenMessageLatch.countDown();
}
/**
* Processes BGP KEEPALIVE message.
*
* @param ctx the Channel Handler Context.
* @param message the message to process.
*/
private void processBgpKeepalive(ChannelHandlerContext ctx,
ChannelBuffer message) {
if (message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH !=
BgpConstants.BGP_KEEPALIVE_EXPECTED_LENGTH) {
// ERROR: Bad Message Length. Close the channel.
ctx.getChannel().close();
return;
}
// BGP KEEPALIVE message successfully received
receivedKeepaliveMessageLatch.countDown();
}
}
......@@ -26,6 +26,16 @@
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-osgi</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
......
package org.onlab.onos.cli;
import org.apache.karaf.shell.commands.Option;
import org.apache.karaf.shell.console.OsgiCommandSupport;
import org.onlab.osgi.DefaultServiceDirectory;
import org.onlab.osgi.ServiceNotFoundException;
......@@ -9,6 +10,10 @@ import org.onlab.osgi.ServiceNotFoundException;
*/
public abstract class AbstractShellCommand extends OsgiCommandSupport {
@Option(name = "-j", aliases = "--json", description = "Output JSON",
required = false, multiValued = false)
private boolean json = false;
/**
* Returns the reference to the implementation of the specified service.
*
......@@ -46,6 +51,15 @@ public abstract class AbstractShellCommand extends OsgiCommandSupport {
*/
protected abstract void execute();
/**
* Indicates whether JSON format should be output.
*
* @return true if JSON is requested
*/
protected boolean outputJson() {
return json;
}
@Override
protected Object doExecute() throws Exception {
try {
......
package org.onlab.onos.cli;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.google.common.collect.Lists;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.cluster.ClusterService;
import org.onlab.onos.cluster.ControllerNode;
......@@ -26,15 +28,50 @@ public class MastersListCommand extends AbstractShellCommand {
MastershipService mastershipService = get(MastershipService.class);
List<ControllerNode> nodes = newArrayList(service.getNodes());
Collections.sort(nodes, Comparators.NODE_COMPARATOR);
if (outputJson()) {
print("%s", json(service, mastershipService, nodes));
} else {
for (ControllerNode node : nodes) {
List<DeviceId> ids = Lists.newArrayList(mastershipService.getDevicesOf(node.id()));
Collections.sort(ids, Comparators.ELEMENT_ID_COMPARATOR);
print("%s: %d devices", node.id(), ids.size());
for (DeviceId deviceId : ids) {
print(" %s", deviceId);
}
}
}
}
// Produces JSON structure.
private JsonNode json(ClusterService service, MastershipService mastershipService,
List<ControllerNode> nodes) {
ObjectMapper mapper = new ObjectMapper();
ArrayNode result = mapper.createArrayNode();
ControllerNode self = service.getLocalNode();
for (ControllerNode node : nodes) {
List<DeviceId> ids = Lists.newArrayList(mastershipService.getDevicesOf(node.id()));
Collections.sort(ids, Comparators.ELEMENT_ID_COMPARATOR);
print("%s: %d devices", node.id(), ids.size());
for (DeviceId deviceId : ids) {
print(" %s", deviceId);
}
result.add(mapper.createObjectNode()
.put("id", node.id().toString())
.put("size", ids.size())
.set("devices", json(mapper, ids)));
}
return result;
}
/**
* Produces a JSON array containing the specified device identifiers.
*
* @param mapper object mapper
* @param ids collection of device identifiers
* @return JSON array
*/
public static JsonNode json(ObjectMapper mapper, Iterable<DeviceId> ids) {
ArrayNode result = mapper.createArrayNode();
for (DeviceId deviceId : ids) {
result.add(deviceId.toString());
}
return result;
}
}
......
package org.onlab.onos.cli;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.cluster.ClusterService;
import org.onlab.onos.cluster.ControllerNode;
......@@ -24,12 +27,32 @@ public class NodesListCommand extends AbstractShellCommand {
ClusterService service = get(ClusterService.class);
List<ControllerNode> nodes = newArrayList(service.getNodes());
Collections.sort(nodes, Comparators.NODE_COMPARATOR);
if (outputJson()) {
print("%s", json(service, nodes));
} else {
ControllerNode self = service.getLocalNode();
for (ControllerNode node : nodes) {
print(FMT, node.id(), node.ip(), node.tcpPort(),
service.getState(node.id()),
node.equals(self) ? "*" : "");
}
}
}
// Produces JSON structure.
private JsonNode json(ClusterService service, List<ControllerNode> nodes) {
ObjectMapper mapper = new ObjectMapper();
ArrayNode result = mapper.createArrayNode();
ControllerNode self = service.getLocalNode();
for (ControllerNode node : nodes) {
print(FMT, node.id(), node.ip(), node.tcpPort(),
service.getState(node.id()),
node.equals(self) ? "*" : "");
result.add(mapper.createObjectNode()
.put("id", node.id().toString())
.put("ip", node.ip().toString())
.put("tcpPort", node.tcpPort())
.put("state", service.getState(node.id()).toString())
.put("self", node.equals(self)));
}
return result;
}
}
......
package org.onlab.onos.cli;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.CoreService;
import org.onlab.onos.cluster.ClusterService;
......@@ -22,18 +23,32 @@ public class SummaryCommand extends AbstractShellCommand {
protected void execute() {
TopologyService topologyService = get(TopologyService.class);
Topology topology = topologyService.currentTopology();
print("node=%s, version=%s",
get(ClusterService.class).getLocalNode().ip(),
get(CoreService.class).version().toString());
print("nodes=%d, devices=%d, links=%d, hosts=%d, clusters=%s, paths=%d, flows=%d, intents=%d",
get(ClusterService.class).getNodes().size(),
get(DeviceService.class).getDeviceCount(),
get(LinkService.class).getLinkCount(),
get(HostService.class).getHostCount(),
topologyService.getClusters(topology).size(),
topology.pathCount(),
get(FlowRuleService.class).getFlowRuleCount(),
get(IntentService.class).getIntentCount());
if (outputJson()) {
print("%s", new ObjectMapper().createObjectNode()
.put("node", get(ClusterService.class).getLocalNode().ip().toString())
.put("version", get(CoreService.class).version().toString())
.put("nodes", get(ClusterService.class).getNodes().size())
.put("devices", get(DeviceService.class).getDeviceCount())
.put("links", get(LinkService.class).getLinkCount())
.put("hosts", get(HostService.class).getHostCount())
.put("clusters", topologyService.getClusters(topology).size())
.put("paths", topology.pathCount())
.put("flows", get(FlowRuleService.class).getFlowRuleCount())
.put("intents", get(IntentService.class).getIntentCount()));
} else {
print("node=%s, version=%s",
get(ClusterService.class).getLocalNode().ip(),
get(CoreService.class).version().toString());
print("nodes=%d, devices=%d, links=%d, hosts=%d, clusters=%s, paths=%d, flows=%d, intents=%d",
get(ClusterService.class).getNodes().size(),
get(DeviceService.class).getDeviceCount(),
get(LinkService.class).getLinkCount(),
get(HostService.class).getHostCount(),
topologyService.getClusters(topology).size(),
topology.pathCount(),
get(FlowRuleService.class).getFlowRuleCount(),
get(IntentService.class).getIntentCount());
}
}
}
......
package org.onlab.onos.cli.net;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
......@@ -10,6 +11,7 @@ import org.onlab.onos.net.topology.TopologyCluster;
import java.util.Collections;
import java.util.List;
import static org.onlab.onos.cli.MastersListCommand.json;
import static org.onlab.onos.net.topology.ClusterId.clusterId;
/**
......@@ -33,11 +35,14 @@ public class ClusterDevicesCommand extends ClustersListCommand {
} else {
List<DeviceId> ids = Lists.newArrayList(service.getClusterDevices(topology, cluster));
Collections.sort(ids, Comparators.ELEMENT_ID_COMPARATOR);
for (DeviceId deviceId : ids) {
print("%s", deviceId);
if (outputJson()) {
print("%s", json(new ObjectMapper(), ids));
} else {
for (DeviceId deviceId : ids) {
print("%s", deviceId);
}
}
}
}
}
......
......@@ -5,6 +5,7 @@ import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.topology.TopologyCluster;
import static org.onlab.onos.cli.net.LinksListCommand.json;
import static org.onlab.onos.cli.net.LinksListCommand.linkString;
import static org.onlab.onos.net.topology.ClusterId.clusterId;
......@@ -26,6 +27,8 @@ public class ClusterLinksCommand extends ClustersListCommand {
TopologyCluster cluster = service.getCluster(topology, clusterId(cid));
if (cluster == null) {
error("No such cluster %s", cid);
} else if (outputJson()) {
print("%s", json(service.getClusterLinks(topology, cluster)));
} else {
for (Link link : service.getClusterLinks(topology, cluster)) {
print(linkString(link));
......
package org.onlab.onos.cli.net;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.google.common.collect.Lists;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.cli.Comparators;
......@@ -24,9 +27,26 @@ public class ClustersListCommand extends TopologyCommand {
List<TopologyCluster> clusters = Lists.newArrayList(service.getClusters(topology));
Collections.sort(clusters, Comparators.CLUSTER_COMPARATOR);
if (outputJson()) {
print("%s", json(clusters));
} else {
for (TopologyCluster cluster : clusters) {
print(FMT, cluster.id().index(), cluster.deviceCount(), cluster.linkCount());
}
}
}
// Produces a JSON result.
private JsonNode json(Iterable<TopologyCluster> clusters) {
ObjectMapper mapper = new ObjectMapper();
ArrayNode result = mapper.createArrayNode();
for (TopologyCluster cluster : clusters) {
print(FMT, cluster.id().index(), cluster.deviceCount(), cluster.linkCount());
result.add(mapper.createObjectNode()
.put("id", cluster.id().index())
.put("deviceCount", cluster.deviceCount())
.put("linkCount", cluster.linkCount()));
}
return result;
}
}
......
package org.onlab.onos.cli.net;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.apache.karaf.shell.commands.Option;
import org.onlab.onos.cli.Comparators;
import org.onlab.onos.net.Device;
import org.onlab.onos.net.Port;
......@@ -22,6 +27,14 @@ public class DevicePortsListCommand extends DevicesListCommand {
private static final String FMT = " port=%s, state=%s";
@Option(name = "-e", aliases = "--enabled", description = "Show only enabled ports",
required = false, multiValued = false)
private boolean enabled = false;
@Option(name = "-d", aliases = "--disabled", description = "Show only disabled ports",
required = false, multiValued = false)
private boolean disabled = false;
@Argument(index = 0, name = "uri", description = "Device ID",
required = false, multiValued = false)
String uri = null;
......@@ -30,26 +43,78 @@ public class DevicePortsListCommand extends DevicesListCommand {
protected void execute() {
DeviceService service = get(DeviceService.class);
if (uri == null) {
for (Device device : getSortedDevices(service)) {
printDevice(service, device);
if (outputJson()) {
print("%s", jsonPorts(service, getSortedDevices(service)));
} else {
for (Device device : getSortedDevices(service)) {
printDevice(service, device);
}
}
} else {
Device device = service.getDevice(deviceId(uri));
if (device == null) {
error("No such device %s", uri);
} else if (outputJson()) {
print("%s", jsonPorts(service, new ObjectMapper(), device));
} else {
printDevice(service, device);
}
}
}
/**
* Produces JSON array containing ports of the specified devices.
*
* @param service device service
* @param devices collection of devices
* @return JSON array
*/
public JsonNode jsonPorts(DeviceService service, Iterable<Device> devices) {
ObjectMapper mapper = new ObjectMapper();
ArrayNode result = mapper.createArrayNode();
for (Device device : devices) {
result.add(jsonPorts(service, mapper, device));
}
return result;
}
/**
* Produces JSON array containing ports of the specified device.
*
* @param service device service
* @param mapper object mapper
* @param device infrastructure devices
* @return JSON array
*/
public JsonNode jsonPorts(DeviceService service, ObjectMapper mapper, Device device) {
ObjectNode result = mapper.createObjectNode();
ArrayNode ports = mapper.createArrayNode();
for (Port port : service.getPorts(device.id())) {
if (isIncluded(port)) {
ports.add(mapper.createObjectNode()
.put("port", port.number().toString())
.put("isEnabled", port.isEnabled()));
}
}
return result.put("device", device.id().toString()).set("ports", ports);
}
// Determines if a port should be included in output.
private boolean isIncluded(Port port) {
return enabled && port.isEnabled() || disabled && !port.isEnabled() ||
!enabled && !disabled;
}
@Override
protected void printDevice(DeviceService service, Device device) {
super.printDevice(service, device);
List<Port> ports = new ArrayList<>(service.getPorts(device.id()));
Collections.sort(ports, Comparators.PORT_COMPARATOR);
for (Port port : ports) {
print(FMT, port.number(), port.isEnabled() ? "enabled" : "disabled");
if (isIncluded(port)) {
print(FMT, port.number(), port.isEnabled() ? "enabled" : "disabled");
}
}
}
......
package org.onlab.onos.cli.net;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.cli.AbstractShellCommand;
import org.onlab.onos.cli.Comparators;
......@@ -24,12 +28,55 @@ public class DevicesListCommand extends AbstractShellCommand {
@Override
protected void execute() {
DeviceService service = get(DeviceService.class);
for (Device device : getSortedDevices(service)) {
printDevice(service, device);
if (outputJson()) {
print("%s", json(service, getSortedDevices(service)));
} else {
for (Device device : getSortedDevices(service)) {
printDevice(service, device);
}
}
}
/**
* Returns JSON node representing the specified devices.
*
* @param service device service
* @param devices collection of devices
* @return JSON node
*/
public static JsonNode json(DeviceService service, Iterable<Device> devices) {
ObjectMapper mapper = new ObjectMapper();
ArrayNode result = mapper.createArrayNode();
for (Device device : devices) {
result.add(json(service, mapper, device));
}
return result;
}
/**
* Returns JSON node representing the specified device.
*
* @param service device service
* @param mapper object mapper
* @param device infrastructure device
* @return JSON node
*/
public static ObjectNode json(DeviceService service, ObjectMapper mapper,
Device device) {
ObjectNode result = mapper.createObjectNode();
if (device != null) {
result.put("id", device.id().toString())
.put("available", service.isAvailable(device.id()))
.put("role", service.getRole(device.id()).toString())
.put("mfr", device.manufacturer())
.put("hw", device.hwVersion())
.put("sw", device.swVersion())
.put("serial", device.serialNumber());
}
return result;
}
/**
* Returns the list of devices sorted using the device ID URIs.
*
* @param service device service
......
package org.onlab.onos.cli.net;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Maps;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
......@@ -12,6 +16,8 @@ import org.onlab.onos.net.device.DeviceService;
import org.onlab.onos.net.flow.FlowEntry;
import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
import org.onlab.onos.net.flow.FlowRuleService;
import org.onlab.onos.net.flow.criteria.Criterion;
import org.onlab.onos.net.flow.instructions.Instruction;
import java.util.Collections;
import java.util.List;
......@@ -48,9 +54,73 @@ public class FlowsListCommand extends AbstractShellCommand {
DeviceService deviceService = get(DeviceService.class);
FlowRuleService service = get(FlowRuleService.class);
Map<Device, List<FlowEntry>> flows = getSortedFlows(deviceService, service);
for (Device d : getSortedDevices(deviceService)) {
printFlows(d, flows.get(d), coreService);
if (outputJson()) {
print("%s", json(coreService, getSortedDevices(deviceService), flows));
} else {
for (Device d : getSortedDevices(deviceService)) {
printFlows(d, flows.get(d), coreService);
}
}
}
/**
* Produces a JSON array of flows grouped by the each device.
*
* @param coreService core service
* @param devices collection of devices to group flow by
* @param flows collection of flows per each device
* @return JSON array
*/
private JsonNode json(CoreService coreService, Iterable<Device> devices,
Map<Device, List<FlowEntry>> flows) {
ObjectMapper mapper = new ObjectMapper();
ArrayNode result = mapper.createArrayNode();
for (Device device : devices) {
result.add(json(coreService, mapper, device, flows.get(device)));
}
return result;
}
// Produces JSON object with the flows of the given device.
private ObjectNode json(CoreService coreService, ObjectMapper mapper,
Device device, List<FlowEntry> flows) {
ObjectNode result = mapper.createObjectNode();
ArrayNode array = mapper.createArrayNode();
for (FlowEntry flow : flows) {
array.add(json(coreService, mapper, flow));
}
result.put("device", device.id().toString())
.put("flowCount", flows.size())
.set("flows", array);
return result;
}
// Produces JSON structure with the specified flow data.
private ObjectNode json(CoreService coreService, ObjectMapper mapper,
FlowEntry flow) {
ObjectNode result = mapper.createObjectNode();
ArrayNode crit = mapper.createArrayNode();
for (Criterion c : flow.selector().criteria()) {
crit.add(c.toString());
}
ArrayNode instr = mapper.createArrayNode();
for (Instruction i : flow.treatment().instructions()) {
instr.add(i.toString());
}
result.put("flowId", Long.toHexString(flow.id().value()))
.put("state", flow.state().toString())
.put("bytes", flow.bytes())
.put("packets", flow.packets())
.put("life", flow.life())
.put("appId", coreService.getAppId(flow.appId()).name());
result.set("selector", crit);
result.set("treatment", instr);
return result;
}
/**
......
package org.onlab.onos.cli.net;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.cli.AbstractShellCommand;
import org.onlab.onos.cli.Comparators;
import org.onlab.onos.net.Host;
import org.onlab.onos.net.host.HostService;
import org.onlab.packet.IpPrefix;
import java.util.Collections;
import java.util.List;
......@@ -15,7 +20,7 @@ import static com.google.common.collect.Lists.newArrayList;
* Lists all currently-known hosts.
*/
@Command(scope = "onos", name = "hosts",
description = "Lists all currently-known hosts.")
description = "Lists all currently-known hosts.")
public class HostsListCommand extends AbstractShellCommand {
private static final String FMT =
......@@ -24,11 +29,42 @@ public class HostsListCommand extends AbstractShellCommand {
@Override
protected void execute() {
HostService service = get(HostService.class);
for (Host host : getSortedHosts(service)) {
printHost(host);
if (outputJson()) {
print("%s", json(getSortedHosts(service)));
} else {
for (Host host : getSortedHosts(service)) {
printHost(host);
}
}
}
// Produces JSON structure.
private static JsonNode json(Iterable<Host> hosts) {
ObjectMapper mapper = new ObjectMapper();
ArrayNode result = mapper.createArrayNode();
for (Host host : hosts) {
result.add(json(mapper, host));
}
return result;
}
// Produces JSON structure.
private static JsonNode json(ObjectMapper mapper, Host host) {
ObjectNode loc = LinksListCommand.json(mapper, host.location())
.put("time", host.location().time());
ArrayNode ips = mapper.createArrayNode();
for (IpPrefix ip : host.ipAddresses()) {
ips.add(ip.toString());
}
ObjectNode result = mapper.createObjectNode()
.put("id", host.id().toString())
.put("mac", host.mac().toString())
.put("vlan", host.vlan().toString());
result.set("location", loc);
result.set("ips", ips);
return result;
}
/**
* Returns the list of devices sorted using the device ID URIs.
*
......@@ -44,14 +80,14 @@ public class HostsListCommand extends AbstractShellCommand {
/**
* Prints information about a host.
*
* @param host
* @param host end-station host
*/
protected void printHost(Host host) {
if (host != null) {
print(FMT, host.id(), host.mac(),
host.location().deviceId(),
host.location().port(),
host.vlan(), host.ipAddresses());
host.location().deviceId(),
host.location().port(),
host.vlan(), host.ipAddresses());
}
}
}
}
......
package org.onlab.onos.cli.net;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.cli.AbstractShellCommand;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.link.LinkService;
......@@ -27,9 +32,55 @@ public class LinksListCommand extends AbstractShellCommand {
LinkService service = get(LinkService.class);
Iterable<Link> links = uri != null ?
service.getDeviceLinks(deviceId(uri)) : service.getLinks();
if (outputJson()) {
print("%s", json(links));
} else {
for (Link link : links) {
print(linkString(link));
}
}
}
/**
* Produces a JSON array containing the specified links.
*
* @param links collection of links
* @return JSON array
*/
public static JsonNode json(Iterable<Link> links) {
ObjectMapper mapper = new ObjectMapper();
ArrayNode result = mapper.createArrayNode();
for (Link link : links) {
print(linkString(link));
result.add(json(mapper, link));
}
return result;
}
/**
* Produces a JSON object for the specified link.
*
* @param mapper object mapper
* @param link link to encode
* @return JSON object
*/
public static ObjectNode json(ObjectMapper mapper, Link link) {
ObjectNode result = mapper.createObjectNode();
result.set("src", json(mapper, link.src()));
result.set("dst", json(mapper, link.dst()));
return result;
}
/**
* Produces a JSON object for the specified connect point.
*
* @param mapper object mapper
* @param connectPoint connection point to encode
* @return JSON object
*/
public static ObjectNode json(ObjectMapper mapper, ConnectPoint connectPoint) {
return mapper.createObjectNode()
.put("device", connectPoint.deviceId().toString())
.put("port", connectPoint.port().toString());
}
/**
......
package org.onlab.onos.cli.net;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.net.Link;
......@@ -32,9 +35,30 @@ public class PathListCommand extends TopologyCommand {
protected void execute() {
init();
Set<Path> paths = service.getPaths(topology, deviceId(src), deviceId(dst));
if (outputJson()) {
print("%s", json(paths));
} else {
for (Path path : paths) {
print(pathString(path));
}
}
}
/**
* Produces a JSON array containing the specified paths.
*
* @param paths collection of paths
* @return JSON array
*/
public static JsonNode json(Iterable<Path> paths) {
ObjectMapper mapper = new ObjectMapper();
ArrayNode result = mapper.createArrayNode();
for (Path path : paths) {
print(pathString(path));
result.add(LinksListCommand.json(mapper, path)
.put("cost", path.cost())
.set("links", LinksListCommand.json(path.links())));
}
return result;
}
/**
......
package org.onlab.onos.cli.net;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.cli.AbstractShellCommand;
import org.onlab.onos.net.topology.Topology;
......@@ -30,8 +31,17 @@ public class TopologyCommand extends AbstractShellCommand {
@Override
protected void execute() {
init();
print(FMT, topology.time(), topology.deviceCount(), topology.linkCount(),
topology.clusterCount(), topology.pathCount());
if (outputJson()) {
print("%s", new ObjectMapper().createObjectNode()
.put("time", topology.time())
.put("deviceCount", topology.deviceCount())
.put("linkCount", topology.linkCount())
.put("clusterCount", topology.clusterCount())
.put("pathCount", topology.pathCount()));
} else {
print(FMT, topology.time(), topology.deviceCount(), topology.linkCount(),
topology.clusterCount(), topology.pathCount());
}
}
}
......
......@@ -12,7 +12,11 @@ public final class ControllerNodeToNodeId
@Override
public NodeId apply(ControllerNode input) {
return input.id();
if (input == null) {
return null;
} else {
return input.id();
}
}
/**
......
package org.onlab.onos.net.host;
import java.util.Collections;
import java.util.Set;
import org.onlab.onos.net.AbstractDescription;
import org.onlab.onos.net.HostLocation;
import org.onlab.onos.net.SparseAnnotations;
......@@ -7,6 +10,8 @@ import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import com.google.common.collect.ImmutableSet;
import static com.google.common.base.MoreObjects.toStringHelper;
/**
......@@ -18,7 +23,7 @@ public class DefaultHostDescription extends AbstractDescription
private final MacAddress mac;
private final VlanId vlan;
private final HostLocation location;
private final IpPrefix ip;
private final Set<IpPrefix> ip;
/**
* Creates a host description using the supplied information.
......@@ -31,7 +36,7 @@ public class DefaultHostDescription extends AbstractDescription
public DefaultHostDescription(MacAddress mac, VlanId vlan,
HostLocation location,
SparseAnnotations... annotations) {
this(mac, vlan, location, null, annotations);
this(mac, vlan, location, Collections.<IpPrefix>emptySet(), annotations);
}
/**
......@@ -46,11 +51,26 @@ public class DefaultHostDescription extends AbstractDescription
public DefaultHostDescription(MacAddress mac, VlanId vlan,
HostLocation location, IpPrefix ip,
SparseAnnotations... annotations) {
this(mac, vlan, location, ImmutableSet.of(ip), annotations);
}
/**
* Creates a host description using the supplied information.
*
* @param mac host MAC address
* @param vlan host VLAN identifier
* @param location host location
* @param ip host IP addresses
* @param annotations optional key/value annotations map
*/
public DefaultHostDescription(MacAddress mac, VlanId vlan,
HostLocation location, Set<IpPrefix> ip,
SparseAnnotations... annotations) {
super(annotations);
this.mac = mac;
this.vlan = vlan;
this.location = location;
this.ip = ip;
this.ip = ImmutableSet.copyOf(ip);
}
@Override
......@@ -69,7 +89,7 @@ public class DefaultHostDescription extends AbstractDescription
}
@Override
public IpPrefix ipAddress() {
public Set<IpPrefix> ipAddress() {
return ip;
}
......
package org.onlab.onos.net.host;
import java.util.Set;
import org.onlab.onos.net.Description;
import org.onlab.onos.net.HostLocation;
import org.onlab.packet.IpPrefix;
......@@ -38,6 +40,6 @@ public interface HostDescription extends Description {
* @return host IP address
*/
// FIXME: Switch to IpAddress
IpPrefix ipAddress();
Set<IpPrefix> ipAddress();
}
......
......@@ -12,7 +12,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
/**
* Abstraction of end-station to end-station bidirectional connectivity.
*/
public class HostToHostIntent extends ConnectivityIntent {
public final class HostToHostIntent extends ConnectivityIntent {
private final HostId one;
private final HostId two;
......
......@@ -14,7 +14,7 @@ import com.google.common.base.MoreObjects;
* Abstraction of a connectivity intent that is implemented by a set of path
* segments.
*/
public class LinkCollectionIntent extends ConnectivityIntent implements InstallableIntent {
public final class LinkCollectionIntent extends ConnectivityIntent implements InstallableIntent {
private final Set<Link> links;
......@@ -46,6 +46,12 @@ public class LinkCollectionIntent extends ConnectivityIntent implements Installa
return links;
}
/**
* Returns the set of links that represent the network connections needed
* by this intent.
*
* @return Set of links for the network hops needed by this intent
*/
public Set<Link> links() {
return links;
}
......
......@@ -15,7 +15,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
/**
* Abstraction of multiple source to single destination connectivity intent.
*/
public class MultiPointToSinglePointIntent extends ConnectivityIntent {
public final class MultiPointToSinglePointIntent extends ConnectivityIntent {
private final Set<ConnectPoint> ingressPoints;
private final ConnectPoint egressPoint;
......
package org.onlab.onos.cluster;
import static com.google.common.base.Predicates.notNull;
import static org.junit.Assert.*;
import static org.onlab.onos.cluster.ControllerNodeToNodeId.toNodeId;
......@@ -30,12 +31,13 @@ public class ControllerNodeToNodeIdTest {
@Test
public final void testToNodeId() {
final Iterable<ControllerNode> nodes = Arrays.asList(CN1, CN2, CN3);
final Iterable<ControllerNode> nodes = Arrays.asList(CN1, CN2, CN3, null);
final List<NodeId> nodeIds = Arrays.asList(NID1, NID2, NID3);
assertEquals(nodeIds,
FluentIterable.from(nodes)
.transform(toNodeId())
.filter(notNull())
.toList());
}
......
......@@ -48,10 +48,16 @@ public final class NetTestTools {
new HashSet<IpPrefix>());
}
// Short-hand for creating a connection point.
public static ConnectPoint connectPoint(String id, int port) {
return new ConnectPoint(did(id), portNumber(port));
}
// Short-hand for creating a link.
public static Link link(String src, int sp, String dst, int dp) {
return new DefaultLink(PID, new ConnectPoint(did(src), portNumber(sp)),
new ConnectPoint(did(dst), portNumber(dp)),
return new DefaultLink(PID,
connectPoint(src, sp),
connectPoint(dst, dp),
Link.Type.DIRECT);
}
......
......@@ -8,6 +8,8 @@ import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import com.google.common.collect.ImmutableSet;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
......@@ -33,7 +35,7 @@ public class DefualtHostDecriptionTest {
assertEquals("incorrect mac", MAC, host.hwAddress());
assertEquals("incorrect vlan", VLAN, host.vlan());
assertEquals("incorrect location", LOC, host.location());
assertEquals("incorrect ip's", IP, host.ipAddress());
assertEquals("incorrect ip's", ImmutableSet.of(IP), host.ipAddress());
assertTrue("incorrect toString", host.toString().contains("vlan=10"));
}
......
......@@ -6,15 +6,15 @@
<parent>
<groupId>org.onlab.onos</groupId>
<artifactId>onos-core-hz</artifactId>
<artifactId>onos-core</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-core-hz-net</artifactId>
<artifactId>onos-json</artifactId>
<packaging>bundle</packaging>
<description>ONOS Hazelcast based distributed store subsystems</description>
<description>ONOS JSON encode/decode facilities</description>
<dependencies>
<dependency>
......@@ -23,24 +23,22 @@
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onos-core-hz-common</artifactId>
<version>${project.version}</version>
<artifactId>onos-api</artifactId>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onos-core-hz-common</artifactId>
<classifier>tests</classifier>
<scope>test</scope>
<artifactId>onos-core-trivial</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
</dependency>
</dependencies>
<build>
......
package org.onlab.onos.json.impl;
/**
* Created by tom on 10/16/14.
*/
public class DeleteMe {
}
/**
* Implementation of JSON codec factory and of the builtin codecs.
*/
package org.onlab.onos.json.impl;
\ No newline at end of file
......@@ -36,26 +36,9 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<scope>test</scope>
</dependency>
<!-- TODO Consider removing store dependency.
Currently required for DistributedDeviceManagerTest. -->
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onos-core-hz-net</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<!-- FIXME: should be somewhere else -->
<artifactId>onos-core-hz-common</artifactId>
<version>${project.version}</version>
<classifier>tests</classifier>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<scope>test</scope>
</dependency>
......
/**
*
* Miscellaneous core system implementations.
*/
package org.onlab.onos.impl;
\ No newline at end of file
......
......@@ -41,7 +41,7 @@ public class HostToHostIntentCompiler
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostService hostService;
private IdGenerator<IntentId> intentIdGenerator;
protected IdGenerator<IntentId> intentIdGenerator;
@Activate
public void activate() {
......
......@@ -37,7 +37,7 @@ public class MultiPointToSinglePointIntentCompiler
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PathService pathService;
private IdGenerator<IntentId> intentIdGenerator;
protected IdGenerator<IntentId> intentIdGenerator;
@Activate
public void activate() {
......
......@@ -355,7 +355,7 @@ public class ProxyArpManager implements ProxyArpService {
arp.setTargetProtocolAddress(((ARP) request.getPayload())
.getSenderProtocolAddress());
arp.setSenderProtocolAddress(srcIp.toRealInt());
arp.setSenderProtocolAddress(srcIp.toInt());
eth.setPayload(arp);
return eth;
}
......
package org.onlab.onos.net.intent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.onlab.onos.net.ElementId;
import org.onlab.onos.net.Path;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
import org.onlab.onos.net.flow.criteria.Criterion;
import org.onlab.onos.net.flow.instructions.Instruction;
import org.onlab.onos.net.topology.LinkWeight;
import org.onlab.onos.net.topology.PathService;
import static org.onlab.onos.net.NetTestTools.createPath;
/**
* Common mocks used by the intent framework tests.
*/
public class IntentTestsMocks {
/**
* Mock traffic selector class used for satisfying API requirements.
*/
public static class MockSelector implements TrafficSelector {
@Override
public Set<Criterion> criteria() {
return new HashSet<>();
}
}
/**
* Mock traffic treatment class used for satisfying API requirements.
*/
public static class MockTreatment implements TrafficTreatment {
@Override
public List<Instruction> instructions() {
return new ArrayList<>();
}
}
/**
* Mock path service for creating paths within the test.
*/
public static class MockPathService implements PathService {
final String[] pathHops;
final String[] reversePathHops;
/**
* Constructor that provides a set of hops to mock.
*
* @param pathHops path hops to mock
*/
public MockPathService(String[] pathHops) {
this.pathHops = pathHops;
String[] reversed = pathHops.clone();
Collections.reverse(Arrays.asList(reversed));
reversePathHops = reversed;
}
@Override
public Set<Path> getPaths(ElementId src, ElementId dst) {
Set<Path> result = new HashSet<>();
String[] allHops = new String[pathHops.length];
if (src.toString().endsWith(pathHops[0])) {
System.arraycopy(pathHops, 0, allHops, 0, pathHops.length);
} else {
System.arraycopy(reversePathHops, 0, allHops, 0, pathHops.length);
}
result.add(createPath(allHops));
return result;
}
@Override
public Set<Path> getPaths(ElementId src, ElementId dst, LinkWeight weight) {
return getPaths(src, dst);
}
}
}
package org.onlab.onos.net.intent;
import java.util.Collection;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.onlab.onos.net.Link;
/**
* Matcher to determine if a Collection of Links contains a path between a source
* and a destination.
*/
public class LinksHaveEntryWithSourceDestinationPairMatcher extends
TypeSafeMatcher<Collection<Link>> {
private final String source;
private final String destination;
/**
* Creates a matcher for a given path represented by a source and
* a destination.
*
* @param source string identifier for the source of the path
* @param destination string identifier for the destination of the path
*/
LinksHaveEntryWithSourceDestinationPairMatcher(String source,
String destination) {
this.source = source;
this.destination = destination;
}
@Override
public boolean matchesSafely(Collection<Link> links) {
for (Link link : links) {
if (link.src().elementId().toString().endsWith(source) &&
link.dst().elementId().toString().endsWith(destination)) {
return true;
}
}
return false;
}
@Override
public void describeTo(Description description) {
description.appendText("link lookup for source \"");
description.appendText(source);
description.appendText(" and destination ");
description.appendText(destination);
description.appendText("\"");
}
@Override
public void describeMismatchSafely(Collection<Link> links,
Description mismatchDescription) {
mismatchDescription.appendText("was ").
appendText(links.toString());
}
/**
* Creates a link has path matcher.
*
* @param source string identifier for the source of the path
* @param destination string identifier for the destination of the path
* @return matcher to match the path
*/
public static LinksHaveEntryWithSourceDestinationPairMatcher linksHasPath(
String source,
String destination) {
return new LinksHaveEntryWithSourceDestinationPairMatcher(source,
destination);
}
}
package org.onlab.onos.net.intent;
import org.junit.Test;
import org.onlab.onos.net.HostId;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.onlab.onos.net.NetTestTools.hid;
/**
* Unit tests for the HostToHostIntent class.
*/
public class TestHostToHostIntent {
private TrafficSelector selector = new IntentTestsMocks.MockSelector();
private TrafficTreatment treatment = new IntentTestsMocks.MockTreatment();
private HostToHostIntent makeHostToHost(long id, HostId one, HostId two) {
return new HostToHostIntent(new IntentId(id),
one,
two,
selector,
treatment);
}
/**
* Tests the equals() method where two HostToHostIntents have references
* to the same hosts. These should compare equal.
*/
@Test
public void testSameEquals() {
HostId one = hid("00:00:00:00:00:01/-1");
HostId two = hid("00:00:00:00:00:02/-1");
HostToHostIntent i1 = makeHostToHost(12, one, two);
HostToHostIntent i2 = makeHostToHost(12, one, two);
assertThat(i1, is(equalTo(i2)));
}
/**
* Tests the equals() method where two HostToHostIntents have references
* to different Hosts. These should compare not equal.
*/
@Test
public void testLinksDifferentEquals() {
HostId one = hid("00:00:00:00:00:01/-1");
HostId two = hid("00:00:00:00:00:02/-1");
HostToHostIntent i1 = makeHostToHost(12, one, two);
HostToHostIntent i2 = makeHostToHost(12, two, one);
assertThat(i1, is(not(equalTo(i2))));
}
/**
* Tests the equals() method where two HostToHostIntents have different
* ids. These should compare not equal.
*/
@Test
public void testBaseDifferentEquals() {
HostId one = hid("00:00:00:00:00:01/-1");
HostId two = hid("00:00:00:00:00:02/-1");
HostToHostIntent i1 = makeHostToHost(12, one, two);
HostToHostIntent i2 = makeHostToHost(11, one, two);
assertThat(i1, is(not(equalTo(i2))));
}
/**
* Tests that the hashCode() values for two equivalent HostToHostIntent
* objects are the same.
*/
@Test
public void testHashCodeEquals() {
HostId one = hid("00:00:00:00:00:01/-1");
HostId two = hid("00:00:00:00:00:02/-1");
HostToHostIntent i1 = makeHostToHost(12, one, two);
HostToHostIntent i2 = makeHostToHost(12, one, two);
assertThat(i1.hashCode(), is(equalTo(i2.hashCode())));
}
/**
* Tests that the hashCode() values for two distinct LinkCollectionIntent
* objects are different.
*/
@Test
public void testHashCodeDifferent() {
HostId one = hid("00:00:00:00:00:01/-1");
HostId two = hid("00:00:00:00:00:02/-1");
HostToHostIntent i1 = makeHostToHost(12, one, two);
HostToHostIntent i2 = makeHostToHost(112, one, two);
assertThat(i1.hashCode(), is(not(equalTo(i2.hashCode()))));
}
/**
* Checks that the HostToHostIntent class is immutable.
*/
@Test
public void checkImmutability() {
ImmutableClassChecker.assertThatClassIsImmutable(HostToHostIntent.class);
}
}
package org.onlab.onos.net.intent;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
import org.onlab.onos.net.flow.criteria.Criterion;
import org.onlab.onos.net.flow.instructions.Instruction;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.onlab.onos.net.NetTestTools.link;
/**
* Unit tests for the LinkCollectionIntent class.
*/
public class TestLinkCollectionIntent {
private static class MockSelector implements TrafficSelector {
@Override
public Set<Criterion> criteria() {
return new HashSet<Criterion>();
}
private Link link1 = link("dev1", 1, "dev2", 2);
private Link link2 = link("dev1", 1, "dev3", 2);
private Link link3 = link("dev2", 1, "dev3", 2);
private Set<Link> links1;
private Set<Link> links2;
private TrafficSelector selector = new IntentTestsMocks.MockSelector();
private TrafficTreatment treatment = new IntentTestsMocks.MockTreatment();
private LinkCollectionIntent makeLinkCollection(long id, Set<Link> links) {
return new LinkCollectionIntent(new IntentId(id),
selector, treatment, links);
}
@Before
public void setup() {
links1 = new HashSet<>();
links2 = new HashSet<>();
}
private static class MockTreatment implements TrafficTreatment {
@Override
public List<Instruction> instructions() {
return new ArrayList<>();
}
/**
* Tests the equals() method where two LinkCollectionIntents have references
* to the same Links in different orders. These should compare equal.
*/
@Test
public void testSameEquals() {
links1.add(link1);
links1.add(link2);
links1.add(link3);
links2.add(link3);
links2.add(link2);
links2.add(link1);
LinkCollectionIntent i1 = makeLinkCollection(12, links1);
LinkCollectionIntent i2 = makeLinkCollection(12, links2);
assertThat(i1, is(equalTo(i2)));
}
/**
* Tests the equals() method where two LinkCollectionIntents have references
* to different Links. These should compare not equal.
*/
@Test
public void testComparison() {
TrafficSelector selector = new MockSelector();
TrafficTreatment treatment = new MockTreatment();
Set<Link> links = new HashSet<>();
LinkCollectionIntent i1 = new LinkCollectionIntent(new IntentId(12),
selector, treatment, links);
LinkCollectionIntent i2 = new LinkCollectionIntent(new IntentId(12),
selector, treatment, links);
public void testLinksDifferentEquals() {
links1.add(link1);
links1.add(link2);
links2.add(link3);
links2.add(link1);
assertThat(i1.equals(i2), is(true));
LinkCollectionIntent i1 = makeLinkCollection(12, links1);
LinkCollectionIntent i2 = makeLinkCollection(12, links2);
assertThat(i1, is(not(equalTo(i2))));
}
/**
* Tests the equals() method where two LinkCollectionIntents have different
* ids. These should compare not equal.
*/
@Test
public void testBaseDifferentEquals() {
links1.add(link1);
links1.add(link2);
links2.add(link2);
links2.add(link1);
LinkCollectionIntent i1 = makeLinkCollection(1, links1);
LinkCollectionIntent i2 = makeLinkCollection(2, links2);
assertThat(i1, is(not(equalTo(i2))));
}
/**
* Tests that the hashCode() values for two equivalent LinkCollectionIntent
* objects are the same.
*/
@Test
public void testHashCodeEquals() {
links1.add(link1);
links1.add(link2);
links1.add(link3);
links2.add(link3);
links2.add(link2);
links2.add(link1);
LinkCollectionIntent i1 = makeLinkCollection(1, links1);
LinkCollectionIntent i2 = makeLinkCollection(1, links2);
assertThat(i1.hashCode(), is(equalTo(i2.hashCode())));
}
/**
* Tests that the hashCode() values for two distinct LinkCollectionIntent
* objects are different.
*/
@Test
public void testHashCodeDifferent() {
links1.add(link1);
links1.add(link2);
links2.add(link1);
links2.add(link3);
LinkCollectionIntent i1 = makeLinkCollection(1, links1);
LinkCollectionIntent i2 = makeLinkCollection(1, links2);
assertThat(i1.hashCode(), is(not(equalTo(i2.hashCode()))));
}
/**
* Checks that the HostToHostIntent class is immutable.
*/
@Test
public void checkImmutability() {
ImmutableClassChecker.assertThatClassIsImmutable(LinkCollectionIntent.class);
}
}
......
package org.onlab.onos.net.intent;
import java.util.HashSet;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.onlab.onos.net.NetTestTools.connectPoint;
/**
* Unit tests for the MultiPointToSinglePointIntent class.
*/
public class TestMultiPointToSinglePointIntent {
private ConnectPoint point1 = connectPoint("dev1", 1);
private ConnectPoint point2 = connectPoint("dev2", 1);
private ConnectPoint point3 = connectPoint("dev3", 1);
private TrafficSelector selector = new IntentTestsMocks.MockSelector();
private TrafficTreatment treatment = new IntentTestsMocks.MockTreatment();
Set<ConnectPoint> ingress1;
Set<ConnectPoint> ingress2;
/**
* Creates a MultiPointToSinglePointIntent object.
*
* @param id identifier to use for the new intent
* @param ingress set of ingress points
* @param egress egress point
* @return MultiPointToSinglePoint intent
*/
private MultiPointToSinglePointIntent makeIntent(long id,
Set<ConnectPoint> ingress,
ConnectPoint egress) {
return new MultiPointToSinglePointIntent(new IntentId(id),
selector,
treatment,
ingress,
egress);
}
/**
* Initializes the ingress sets.
*/
@Before
public void setup() {
ingress1 = new HashSet<>();
ingress2 = new HashSet<>();
}
/**
* Tests the equals() method where two MultiPointToSinglePoint have references
* to the same Links in different orders. These should compare equal.
*/
@Test
public void testSameEquals() {
Set<ConnectPoint> ingress1 = new HashSet<>();
ingress1.add(point2);
ingress1.add(point3);
Set<ConnectPoint> ingress2 = new HashSet<>();
ingress2.add(point3);
ingress2.add(point2);
Intent i1 = makeIntent(12, ingress1, point1);
Intent i2 = makeIntent(12, ingress2, point1);
assertThat(i1, is(equalTo(i2)));
}
/**
* Tests the equals() method where two MultiPointToSinglePoint have references
* to different Links. These should compare not equal.
*/
@Test
public void testLinksDifferentEquals() {
ingress1.add(point3);
ingress2.add(point3);
ingress2.add(point2);
Intent i1 = makeIntent(12, ingress1, point1);
Intent i2 = makeIntent(12, ingress2, point1);
assertThat(i1, is(not(equalTo(i2))));
}
/**
* Tests the equals() method where two MultiPointToSinglePoint have different
* ids. These should compare not equal.
*/
@Test
public void testBaseDifferentEquals() {
ingress1.add(point3);
ingress2.add(point3);
Intent i1 = makeIntent(12, ingress1, point1);
Intent i2 = makeIntent(11, ingress2, point1);
assertThat(i1, is(not(equalTo(i2))));
}
/**
* Tests that the hashCode() values for two equivalent MultiPointToSinglePoint
* objects are the same.
*/
@Test
public void testHashCodeEquals() {
ingress1.add(point2);
ingress1.add(point3);
ingress2.add(point3);
ingress2.add(point2);
Intent i1 = makeIntent(12, ingress1, point1);
Intent i2 = makeIntent(12, ingress2, point1);
assertThat(i1.hashCode(), is(equalTo(i2.hashCode())));
}
/**
* Tests that the hashCode() values for two distinct MultiPointToSinglePoint
* objects are different.
*/
@Test
public void testHashCodeDifferent() {
ingress1.add(point2);
ingress2.add(point3);
ingress2.add(point2);
Intent i1 = makeIntent(12, ingress1, point1);
Intent i2 = makeIntent(12, ingress2, point1);
assertThat(i1.hashCode(), is(not(equalTo(i2.hashCode()))));
}
/**
* Checks that the MultiPointToSinglePointIntent class is immutable.
*/
@Test
public void checkImmutability() {
ImmutableClassChecker.
assertThatClassIsImmutable(MultiPointToSinglePointIntent.class);
}
}
package org.onlab.onos.net.intent.impl;
import java.util.List;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.onlab.onos.net.Host;
import org.onlab.onos.net.HostId;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
import org.onlab.onos.net.host.HostService;
import org.onlab.onos.net.intent.HostToHostIntent;
import org.onlab.onos.net.intent.Intent;
import org.onlab.onos.net.intent.IntentId;
import org.onlab.onos.net.intent.IntentTestsMocks;
import org.onlab.onos.net.intent.PathIntent;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.onlab.onos.net.NetTestTools.hid;
import static org.onlab.onos.net.intent.LinksHaveEntryWithSourceDestinationPairMatcher.linksHasPath;
/**
* Unit tests for the HostToHost intent compiler.
*/
public class TestHostToHostIntentCompiler {
private static final String HOST_ONE_MAC = "00:00:00:00:00:01";
private static final String HOST_TWO_MAC = "00:00:00:00:00:02";
private static final String HOST_ONE_VLAN = "-1";
private static final String HOST_TWO_VLAN = "-1";
private static final String HOST_ONE = HOST_ONE_MAC + "/" + HOST_ONE_VLAN;
private static final String HOST_TWO = HOST_TWO_MAC + "/" + HOST_TWO_VLAN;
private TrafficSelector selector = new IntentTestsMocks.MockSelector();
private TrafficTreatment treatment = new IntentTestsMocks.MockTreatment();
private HostId hostOneId = HostId.hostId(HOST_ONE);
private HostId hostTwoId = HostId.hostId(HOST_TWO);
private HostService mockHostService;
@Before
public void setup() {
Host hostOne = createMock(Host.class);
expect(hostOne.mac()).andReturn(new MacAddress(HOST_ONE_MAC.getBytes())).anyTimes();
expect(hostOne.vlan()).andReturn(VlanId.vlanId()).anyTimes();
replay(hostOne);
Host hostTwo = createMock(Host.class);
expect(hostTwo.mac()).andReturn(new MacAddress(HOST_TWO_MAC.getBytes())).anyTimes();
expect(hostTwo.vlan()).andReturn(VlanId.vlanId()).anyTimes();
replay(hostTwo);
mockHostService = createMock(HostService.class);
expect(mockHostService.getHost(eq(hostOneId))).andReturn(hostOne).anyTimes();
expect(mockHostService.getHost(eq(hostTwoId))).andReturn(hostTwo).anyTimes();
replay(mockHostService);
}
/**
* Creates a HostToHost intent based on two host Ids.
*
* @param oneIdString string for host one id
* @param twoIdString string for host two id
* @return HostToHostIntent for the two hosts
*/
private HostToHostIntent makeIntent(String oneIdString, String twoIdString) {
return new HostToHostIntent(new IntentId(12),
hid(oneIdString),
hid(twoIdString),
selector,
treatment);
}
/**
* Creates a compiler for HostToHost intents.
*
* @param hops string array describing the path hops to use when compiling
* @return HostToHost intent compiler
*/
private HostToHostIntentCompiler makeCompiler(String[] hops) {
HostToHostIntentCompiler compiler =
new HostToHostIntentCompiler();
compiler.pathService = new IntentTestsMocks.MockPathService(hops);
compiler.hostService = mockHostService;
IdBlockAllocator idBlockAllocator = new DummyIdBlockAllocator();
compiler.intentIdGenerator =
new IdBlockAllocatorBasedIntentIdGenerator(idBlockAllocator);
return compiler;
}
/**
* Tests a pair of hosts with 8 hops between them.
*/
@Test
public void testSingleLongPathCompilation() {
HostToHostIntent intent = makeIntent(HOST_ONE,
HOST_TWO);
assertThat(intent, is(notNullValue()));
String[] hops = {HOST_ONE, "h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8", HOST_TWO};
HostToHostIntentCompiler compiler = makeCompiler(hops);
assertThat(compiler, is(notNullValue()));
List<Intent> result = compiler.compile(intent);
assertThat(result, is(Matchers.notNullValue()));
assertThat(result, hasSize(2));
Intent forwardResultIntent = result.get(0);
assertThat(forwardResultIntent instanceof PathIntent, is(true));
Intent reverseResultIntent = result.get(1);
assertThat(reverseResultIntent instanceof PathIntent, is(true));
if (forwardResultIntent instanceof PathIntent) {
PathIntent forwardPathIntent = (PathIntent) forwardResultIntent;
assertThat(forwardPathIntent.path().links(), hasSize(9));
assertThat(forwardPathIntent.path().links(), linksHasPath(HOST_ONE, "h1"));
assertThat(forwardPathIntent.path().links(), linksHasPath("h1", "h2"));
assertThat(forwardPathIntent.path().links(), linksHasPath("h2", "h3"));
assertThat(forwardPathIntent.path().links(), linksHasPath("h3", "h4"));
assertThat(forwardPathIntent.path().links(), linksHasPath("h4", "h5"));
assertThat(forwardPathIntent.path().links(), linksHasPath("h5", "h6"));
assertThat(forwardPathIntent.path().links(), linksHasPath("h6", "h7"));
assertThat(forwardPathIntent.path().links(), linksHasPath("h7", "h8"));
assertThat(forwardPathIntent.path().links(), linksHasPath("h8", HOST_TWO));
}
if (reverseResultIntent instanceof PathIntent) {
PathIntent reversePathIntent = (PathIntent) reverseResultIntent;
assertThat(reversePathIntent.path().links(), hasSize(9));
assertThat(reversePathIntent.path().links(), linksHasPath("h1", HOST_ONE));
assertThat(reversePathIntent.path().links(), linksHasPath("h2", "h1"));
assertThat(reversePathIntent.path().links(), linksHasPath("h3", "h2"));
assertThat(reversePathIntent.path().links(), linksHasPath("h4", "h3"));
assertThat(reversePathIntent.path().links(), linksHasPath("h5", "h4"));
assertThat(reversePathIntent.path().links(), linksHasPath("h6", "h5"));
assertThat(reversePathIntent.path().links(), linksHasPath("h7", "h6"));
assertThat(reversePathIntent.path().links(), linksHasPath("h8", "h7"));
assertThat(reversePathIntent.path().links(), linksHasPath(HOST_TWO, "h8"));
}
}
}
package org.onlab.onos.net.intent.impl;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.ElementId;
import org.onlab.onos.net.Path;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
import org.onlab.onos.net.intent.Intent;
import org.onlab.onos.net.intent.IntentId;
import org.onlab.onos.net.intent.IntentTestsMocks;
import org.onlab.onos.net.intent.LinkCollectionIntent;
import org.onlab.onos.net.intent.MultiPointToSinglePointIntent;
import org.onlab.onos.net.topology.LinkWeight;
import org.onlab.onos.net.topology.PathService;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.onlab.onos.net.NetTestTools.connectPoint;
import static org.onlab.onos.net.NetTestTools.createPath;
import static org.onlab.onos.net.intent.LinksHaveEntryWithSourceDestinationPairMatcher.linksHasPath;
/**
* Unit tests for the MultiPointToSinglePoint intent compiler.
*/
public class TestMultiPointToSinglePointIntentCompiler {
private TrafficSelector selector = new IntentTestsMocks.MockSelector();
private TrafficTreatment treatment = new IntentTestsMocks.MockTreatment();
/**
* Mock path service for creating paths within the test.
*/
private static class MockPathService implements PathService {
final String[] pathHops;
/**
* Constructor that provides a set of hops to mock.
*
* @param pathHops path hops to mock
*/
MockPathService(String[] pathHops) {
this.pathHops = pathHops;
}
@Override
public Set<Path> getPaths(ElementId src, ElementId dst) {
Set<Path> result = new HashSet<>();
String[] allHops = new String[pathHops.length + 1];
allHops[0] = src.toString();
System.arraycopy(pathHops, 0, allHops, 1, pathHops.length);
result.add(createPath(allHops));
return result;
}
@Override
public Set<Path> getPaths(ElementId src, ElementId dst, LinkWeight weight) {
return null;
}
}
/**
* Creates a MultiPointToSinglePoint intent for a group of ingress points
* and an egress point.
*
* @param ingressIds array of ingress device ids
* @param egressId device id of the egress point
* @return MultiPointToSinglePoint intent
*/
private MultiPointToSinglePointIntent makeIntent(String[] ingressIds, String egressId) {
Set<ConnectPoint> ingressPoints = new HashSet<>();
ConnectPoint egressPoint = connectPoint(egressId, 1);
for (String ingressId : ingressIds) {
ingressPoints.add(connectPoint(ingressId, 1));
}
return new MultiPointToSinglePointIntent(
new IntentId(12),
selector,
treatment,
ingressPoints,
egressPoint);
}
/**
* Creates a compiler for MultiPointToSinglePoint intents.
*
* @param hops hops to use while computing paths for this intent
* @return MultiPointToSinglePoint intent
*/
private MultiPointToSinglePointIntentCompiler makeCompiler(String[] hops) {
MultiPointToSinglePointIntentCompiler compiler =
new MultiPointToSinglePointIntentCompiler();
compiler.pathService = new MockPathService(hops);
IdBlockAllocator idBlockAllocator = new DummyIdBlockAllocator();
compiler.intentIdGenerator =
new IdBlockAllocatorBasedIntentIdGenerator(idBlockAllocator);
return compiler;
}
/**
* Tests a single ingress point with 8 hops to its egress point.
*/
@Test
public void testSingleLongPathCompilation() {
String[] ingress = {"ingress"};
String egress = "egress";
MultiPointToSinglePointIntent intent = makeIntent(ingress, egress);
assertThat(intent, is(notNullValue()));
String[] hops = {"h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8",
egress};
MultiPointToSinglePointIntentCompiler compiler = makeCompiler(hops);
assertThat(compiler, is(notNullValue()));
List<Intent> result = compiler.compile(intent);
assertThat(result, is(Matchers.notNullValue()));
assertThat(result, hasSize(1));
Intent resultIntent = result.get(0);
assertThat(resultIntent instanceof LinkCollectionIntent, is(true));
if (resultIntent instanceof LinkCollectionIntent) {
LinkCollectionIntent linkIntent = (LinkCollectionIntent) resultIntent;
assertThat(linkIntent.links(), hasSize(9));
assertThat(linkIntent.links(), linksHasPath("ingress", "h1"));
assertThat(linkIntent.links(), linksHasPath("h1", "h2"));
assertThat(linkIntent.links(), linksHasPath("h2", "h3"));
assertThat(linkIntent.links(), linksHasPath("h4", "h5"));
assertThat(linkIntent.links(), linksHasPath("h5", "h6"));
assertThat(linkIntent.links(), linksHasPath("h7", "h8"));
assertThat(linkIntent.links(), linksHasPath("h8", "egress"));
}
}
/**
* Tests a simple topology where two ingress points share some path segments
* and some path segments are not shared.
*/
@Test
public void testTwoIngressCompilation() {
String[] ingress = {"ingress1", "ingress2"};
String egress = "egress";
MultiPointToSinglePointIntent intent = makeIntent(ingress, egress);
assertThat(intent, is(notNullValue()));
final String[] hops = {"inner1", "inner2", egress};
MultiPointToSinglePointIntentCompiler compiler = makeCompiler(hops);
assertThat(compiler, is(notNullValue()));
List<Intent> result = compiler.compile(intent);
assertThat(result, is(notNullValue()));
assertThat(result, hasSize(1));
Intent resultIntent = result.get(0);
assertThat(resultIntent instanceof LinkCollectionIntent, is(true));
if (resultIntent instanceof LinkCollectionIntent) {
LinkCollectionIntent linkIntent = (LinkCollectionIntent) resultIntent;
assertThat(linkIntent.links(), hasSize(4));
assertThat(linkIntent.links(), linksHasPath("ingress1", "inner1"));
assertThat(linkIntent.links(), linksHasPath("ingress2", "inner1"));
assertThat(linkIntent.links(), linksHasPath("inner1", "inner2"));
assertThat(linkIntent.links(), linksHasPath("inner2", "egress"));
}
}
/**
* Tests a large number of ingress points that share a common path to the
* egress point.
*/
@Test
public void testMultiIngressCompilation() {
String[] ingress = {"i1", "i2", "i3", "i4", "i5",
"i6", "i7", "i8", "i9", "i10"};
String egress = "e";
MultiPointToSinglePointIntent intent = makeIntent(ingress, egress);
assertThat(intent, is(notNullValue()));
final String[] hops = {"n1", egress};
MultiPointToSinglePointIntentCompiler compiler = makeCompiler(hops);
assertThat(compiler, is(notNullValue()));
List<Intent> result = compiler.compile(intent);
assertThat(result, is(notNullValue()));
assertThat(result, hasSize(1));
Intent resultIntent = result.get(0);
assertThat(resultIntent instanceof LinkCollectionIntent, is(true));
if (resultIntent instanceof LinkCollectionIntent) {
LinkCollectionIntent linkIntent = (LinkCollectionIntent) resultIntent;
assertThat(linkIntent.links(), hasSize(ingress.length + 1));
for (String ingressToCheck : ingress) {
assertThat(linkIntent.links(),
linksHasPath(ingressToCheck,
"n1"));
}
assertThat(linkIntent.links(), linksHasPath("n1", egress));
}
}
}
......@@ -20,6 +20,7 @@
<module>api</module>
<module>net</module>
<module>store</module>
<module>json</module>
</modules>
<dependencies>
......
......@@ -19,21 +19,10 @@
<dependencies>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onos-api</artifactId>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onos-core-serializers</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-nio</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-netty</artifactId>
......@@ -50,10 +39,6 @@
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava-testlib</artifactId>
<scope>test</scope>
......@@ -69,13 +54,4 @@
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
......
......@@ -114,7 +114,7 @@ public class ClusterCommunicationManager
message.subject().value(), SERIALIZER.encode(message));
return true;
} catch (IOException e) {
log.error("Failed to send cluster message to nodeId: " + toNodeId, e);
log.trace("Failed to send cluster message to nodeId: " + toNodeId, e);
throw e;
}
}
......
/**
* Common abstractions and facilities for implementing distributed store
* using gossip protocol.
*/
package org.onlab.onos.store.common.impl;
......@@ -15,7 +15,7 @@ import org.onlab.onos.net.device.DefaultPortDescription;
import org.onlab.onos.net.device.DeviceDescription;
import org.onlab.onos.net.device.PortDescription;
import org.onlab.onos.store.Timestamp;
import org.onlab.onos.store.common.impl.Timestamped;
import org.onlab.onos.store.impl.Timestamped;
/*
* Collection of Description of a Device and Ports, given from a Provider.
......
......@@ -38,7 +38,7 @@ import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
import org.onlab.onos.store.cluster.messaging.ClusterMessage;
import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
import org.onlab.onos.store.cluster.messaging.MessageSubject;
import org.onlab.onos.store.common.impl.Timestamped;
import org.onlab.onos.store.impl.Timestamped;
import org.onlab.onos.store.serializers.KryoSerializer;
import org.onlab.onos.store.serializers.DistributedStoreSerializers;
import org.onlab.packet.ChassisId;
......@@ -516,12 +516,12 @@ public class GossipDeviceStore
Map<PortNumber, Port> ports,
Set<PortNumber> processed) {
List<DeviceEvent> events = new ArrayList<>();
Iterator<PortNumber> iterator = ports.keySet().iterator();
Iterator<Entry<PortNumber, Port>> iterator = ports.entrySet().iterator();
while (iterator.hasNext()) {
PortNumber portNumber = iterator.next();
Entry<PortNumber, Port> e = iterator.next();
PortNumber portNumber = e.getKey();
if (!processed.contains(portNumber)) {
events.add(new DeviceEvent(PORT_REMOVED, device,
ports.get(portNumber)));
events.add(new DeviceEvent(PORT_REMOVED, device, e.getValue()));
iterator.remove();
}
}
......@@ -1139,7 +1139,7 @@ public class GossipDeviceStore
try {
unicastMessage(peer, DEVICE_ADVERTISE, ad);
} catch (IOException e) {
log.error("Failed to send anti-entropy advertisement", e);
log.debug("Failed to send anti-entropy advertisement to {}", peer);
return;
}
} catch (Exception e) {
......
package org.onlab.onos.store.device.impl;
import static com.google.common.base.Preconditions.checkNotNull;
import org.apache.commons.lang3.concurrent.ConcurrentException;
import org.apache.commons.lang3.concurrent.ConcurrentInitializer;
import org.onlab.onos.net.device.DeviceDescription;
import org.onlab.onos.store.common.impl.Timestamped;
// FIXME: consider removing this class
public final class InitDeviceDescs
implements ConcurrentInitializer<DeviceDescriptions> {
private final Timestamped<DeviceDescription> deviceDesc;
public InitDeviceDescs(Timestamped<DeviceDescription> deviceDesc) {
this.deviceDesc = checkNotNull(deviceDesc);
}
@Override
public DeviceDescriptions get() throws ConcurrentException {
return new DeviceDescriptions(deviceDesc);
}
}
......@@ -3,7 +3,7 @@ package org.onlab.onos.store.device.impl;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.device.DeviceDescription;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.common.impl.Timestamped;
import org.onlab.onos.store.impl.Timestamped;
import com.google.common.base.MoreObjects;
......
......@@ -3,7 +3,7 @@ package org.onlab.onos.store.device.impl;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.device.DeviceDescription;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.common.impl.Timestamped;
import org.onlab.onos.store.impl.Timestamped;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Serializer;
......
......@@ -5,7 +5,7 @@ import java.util.List;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.device.PortDescription;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.common.impl.Timestamped;
import org.onlab.onos.store.impl.Timestamped;
import com.google.common.base.MoreObjects;
......
......@@ -5,7 +5,7 @@ import java.util.List;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.device.PortDescription;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.common.impl.Timestamped;
import org.onlab.onos.store.impl.Timestamped;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Serializer;
......
......@@ -3,7 +3,7 @@ package org.onlab.onos.store.device.impl;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.device.PortDescription;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.common.impl.Timestamped;
import org.onlab.onos.store.impl.Timestamped;
import com.google.common.base.MoreObjects;
......
......@@ -3,7 +3,7 @@ package org.onlab.onos.store.device.impl;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.device.PortDescription;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.common.impl.Timestamped;
import org.onlab.onos.store.impl.Timestamped;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Serializer;
......
/**
* Implementation of device store using distributed distributed p2p synchronization protocol.
* Implementation of distributed device store using p2p synchronization protocol.
*/
package org.onlab.onos.store.device.impl;
......
package org.onlab.onos.store.flow;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Collection;
import java.util.Collections;
import org.onlab.onos.cluster.NodeId;
import com.google.common.base.Optional;
/**
* Class to represent placement information about Master/Backup copy.
*/
public final class ReplicaInfo {
private final Optional<NodeId> master;
private final Collection<NodeId> backups;
/**
* Creates a ReplicaInfo instance.
*
* @param master NodeId of the node where the master copy should be
* @param backups collection of NodeId, where backup copies should be placed
*/
public ReplicaInfo(NodeId master, Collection<NodeId> backups) {
this.master = Optional.fromNullable(master);
this.backups = checkNotNull(backups);
}
/**
* Returns the NodeId, if there is a Node where the master copy should be.
*
* @return NodeId, where the master copy should be placed
*/
public Optional<NodeId> master() {
return master;
}
/**
* Returns the collection of NodeId, where backup copies should be placed.
*
* @return collection of NodeId, where backup copies should be placed
*/
public Collection<NodeId> backups() {
return backups;
}
// for Serializer
private ReplicaInfo() {
this.master = Optional.absent();
this.backups = Collections.emptyList();
}
}
package org.onlab.onos.store.flow;
import static com.google.common.base.Preconditions.checkNotNull;
import org.onlab.onos.event.AbstractEvent;
import org.onlab.onos.net.DeviceId;
/**
* Describes a device replicainfo event.
*/
public class ReplicaInfoEvent extends AbstractEvent<ReplicaInfoEvent.Type, DeviceId> {
private final ReplicaInfo replicaInfo;
/**
* Types of Replica info event.
*/
public enum Type {
/**
* Event to notify that master placement should be changed.
*/
MASTER_CHANGED,
//
// BACKUPS_CHANGED?
}
/**
* Creates an event of a given type and for the specified device,
* and replica info.
*
* @param type replicainfo event type
* @param device event device subject
* @param replicaInfo replicainfo
*/
public ReplicaInfoEvent(Type type, DeviceId device, ReplicaInfo replicaInfo) {
super(type, device);
this.replicaInfo = checkNotNull(replicaInfo);
}
/**
* Returns the current replica information for the subject.
*
* @return replica information for the subject
*/
public ReplicaInfo replicaInfo() {
return replicaInfo;
};
}
package org.onlab.onos.store.flow;
import org.onlab.onos.event.EventListener;
/**
* Entity capable of receiving Replica placement information-related events.
*/
public interface ReplicaInfoEventListener extends EventListener<ReplicaInfoEvent> {
}
package org.onlab.onos.store.flow;
import org.onlab.onos.net.DeviceId;
/**
* Service to return where the Replica should be placed.
*/
public interface ReplicaInfoService {
// returns where it should be.
/**
* Returns the placement information for given Device.
*
* @param deviceId identifier of the device
* @return placement information
*/
ReplicaInfo getReplicaInfoFor(DeviceId deviceId);
}
package org.onlab.onos.store.flow.impl;
import static org.slf4j.LoggerFactory.getLogger;
import static org.onlab.onos.store.flow.ReplicaInfoEvent.Type.MASTER_CHANGED;
import java.util.Collections;
import java.util.List;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.onos.cluster.NodeId;
import org.onlab.onos.event.AbstractListenerRegistry;
import org.onlab.onos.event.EventDeliveryService;
import org.onlab.onos.mastership.MastershipEvent;
import org.onlab.onos.mastership.MastershipListener;
import org.onlab.onos.mastership.MastershipService;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.store.flow.ReplicaInfo;
import org.onlab.onos.store.flow.ReplicaInfoEvent;
import org.onlab.onos.store.flow.ReplicaInfoEventListener;
import org.onlab.onos.store.flow.ReplicaInfoService;
import org.slf4j.Logger;
/**
* Manages replica placement information.
*/
@Component(immediate = true)
@Service
public class ReplicaInfoManager implements ReplicaInfoService {
private final Logger log = getLogger(getClass());
private final MastershipListener mastershipListener = new InternalMastershipListener();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected EventDeliveryService eventDispatcher;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected MastershipService mastershipService;
protected final AbstractListenerRegistry<ReplicaInfoEvent, ReplicaInfoEventListener>
listenerRegistry = new AbstractListenerRegistry<>();
@Activate
public void activate() {
eventDispatcher.addSink(ReplicaInfoEvent.class, listenerRegistry);
mastershipService.addListener(mastershipListener);
log.info("Started");
}
@Deactivate
public void deactivate() {
eventDispatcher.removeSink(ReplicaInfoEvent.class);
mastershipService.removeListener(mastershipListener);
log.info("Stopped");
}
@Override
public ReplicaInfo getReplicaInfoFor(DeviceId deviceId) {
// TODO: populate backup List when we reach the point we need them.
return new ReplicaInfo(mastershipService.getMasterFor(deviceId),
Collections.<NodeId>emptyList());
}
final class InternalMastershipListener implements MastershipListener {
@Override
public void event(MastershipEvent event) {
// TODO: distinguish stby list update, when MastershipService,
// start publishing them
final List<NodeId> standbyList = Collections.<NodeId>emptyList();
eventDispatcher.post(new ReplicaInfoEvent(MASTER_CHANGED,
event.subject(),
new ReplicaInfo(event.master(), standbyList)));
}
}
}
/**
* Implementation of the distributed flow rule store using p2p synchronization
* protocol.
*/
package org.onlab.onos.store.flow.impl;
......@@ -4,6 +4,11 @@ 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");
public static final MessageSubject HOST_UPDATED
= new MessageSubject("peer-host-updated");
public static final MessageSubject HOST_REMOVED
= new MessageSubject("peer-host-removed");
public static final MessageSubject HOST_ANTI_ENTROPY_ADVERTISEMENT
= new MessageSubject("host-enti-entropy-advertisement");;
}
......
package org.onlab.onos.store.host.impl;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Map;
import org.onlab.onos.cluster.NodeId;
import org.onlab.onos.net.HostId;
import org.onlab.onos.store.Timestamp;
/**
* Host AE Advertisement message.
*/
public final class HostAntiEntropyAdvertisement {
private final NodeId sender;
private final Map<HostFragmentId, Timestamp> timestamps;
private final Map<HostId, Timestamp> tombstones;
public HostAntiEntropyAdvertisement(NodeId sender,
Map<HostFragmentId, Timestamp> timestamps,
Map<HostId, Timestamp> tombstones) {
this.sender = checkNotNull(sender);
this.timestamps = checkNotNull(timestamps);
this.tombstones = checkNotNull(tombstones);
}
public NodeId sender() {
return sender;
}
public Map<HostFragmentId, Timestamp> timestamps() {
return timestamps;
}
public Map<HostId, Timestamp> tombstones() {
return tombstones;
}
// For serializer
@SuppressWarnings("unused")
private HostAntiEntropyAdvertisement() {
this.sender = null;
this.timestamps = null;
this.tombstones = null;
}
}
package org.onlab.onos.store.host.impl;
import java.util.Objects;
import org.onlab.onos.net.HostId;
import org.onlab.onos.net.provider.ProviderId;
import com.google.common.base.MoreObjects;
/**
* Identifier for HostDescription from a Provider.
*/
public final class HostFragmentId {
public final ProviderId providerId;
public final HostId hostId;
public HostFragmentId(HostId hostId, ProviderId providerId) {
this.providerId = providerId;
this.hostId = hostId;
}
public HostId hostId() {
return hostId;
}
public ProviderId providerId() {
return providerId;
}
@Override
public int hashCode() {
return Objects.hash(providerId, hostId);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof HostFragmentId)) {
return false;
}
HostFragmentId that = (HostFragmentId) obj;
return Objects.equals(this.hostId, that.hostId) &&
Objects.equals(this.providerId, that.providerId);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("providerId", providerId)
.add("hostId", hostId)
.toString();
}
// for serializer
@SuppressWarnings("unused")
private HostFragmentId() {
this.providerId = null;
this.hostId = null;
}
}
/**
* Implementation of the distributed host store using p2p synchronization protocol.
*/
package org.onlab.onos.store.host.impl;
package org.onlab.onos.store.common.impl;
package org.onlab.onos.store.impl;
import static com.google.common.base.Preconditions.checkNotNull;
......@@ -58,12 +58,12 @@ public final class Timestamped<T> {
}
/**
* Tests if this timestamp is newer thatn the specified timestamp.
* @param timestamp to compare agains
* Tests if this timestamp is newer than the specified timestamp.
* @param other timestamp to compare against
* @return true if this instance is newer
*/
public boolean isNewer(Timestamp timestamp) {
return this.timestamp.compareTo(checkNotNull(timestamp)) > 0;
public boolean isNewer(Timestamp other) {
return this.timestamp.compareTo(checkNotNull(other)) > 0;
}
@Override
......
......@@ -4,7 +4,7 @@ import com.google.common.base.MoreObjects;
import org.onlab.onos.net.link.LinkDescription;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.common.impl.Timestamped;
import org.onlab.onos.store.impl.Timestamped;
/**
* Information published by GossipDeviceStore to notify peers of a device
......
/**
* Implementation of link store using distributed p2p synchronization protocol.
* Implementation of distributed link store using p2p synchronization protocol.
*/
package org.onlab.onos.store.link.impl;
......
package org.onlab.onos.store.serializers;
import org.onlab.onos.store.common.impl.Timestamped;
import org.onlab.onos.store.impl.MastershipBasedTimestamp;
import org.onlab.onos.store.impl.Timestamped;
import org.onlab.onos.store.impl.WallClockTimestamp;
import org.onlab.util.KryoPool;
......
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.