Ayaka Koshibe

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

Showing 162 changed files with 8291 additions and 523 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
<?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-optical</artifactId>
<packaging>bundle</packaging>
<description>ONOS application for packet/optical deployments</description>
<dependencies>
<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.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
package org.onlab.onos.optical.cfg;
import static org.onlab.onos.net.DeviceId.deviceId;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
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.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DefaultAnnotations;
import org.onlab.onos.net.Device;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.MastershipRole;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.device.DefaultDeviceDescription;
import org.onlab.onos.net.device.DeviceDescription;
import org.onlab.onos.net.device.DeviceProvider;
import org.onlab.onos.net.device.DeviceProviderRegistry;
import org.onlab.onos.net.device.DeviceProviderService;
import org.onlab.onos.net.link.DefaultLinkDescription;
import org.onlab.onos.net.link.LinkProvider;
import org.onlab.onos.net.link.LinkProviderRegistry;
import org.onlab.onos.net.link.LinkProviderService;
import org.onlab.onos.net.provider.AbstractProvider;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.packet.ChassisId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* OpticalConfigProvider emulates the SB network provider for optical switches,
* optical links and any other state that needs to be configured for correct network
* operations.
*
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@Component(immediate = true)
public class OpticalConfigProvider extends AbstractProvider implements DeviceProvider, LinkProvider {
protected static final Logger log = LoggerFactory
.getLogger(OpticalConfigProvider.class);
// TODO: fix hard coded file path later.
private static final String DEFAULT_CONFIG_FILE =
"/opt/onos/config/demo-3-roadm-2-ps.json";
private String configFileName = DEFAULT_CONFIG_FILE;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected LinkProviderRegistry linkProviderRegistry;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceProviderRegistry deviceProviderRegistry;
private static final String OPTICAL_ANNOTATION = "optical.";
private LinkProviderService linkProviderService;
private DeviceProviderService deviceProviderService;
private static final List<Roadm> RAW_ROADMS = new ArrayList<>();
private static final List<WdmLink> RAW_WDMLINKS = new ArrayList<>();
private static final List<PktOptLink> RAW_PKTOPTLINKS = new ArrayList<>();
private static final String ROADM = "Roadm";
private static final String WDM_LINK = "wdmLink";
private static final String PKT_OPT_LINK = "pktOptLink";
protected OpticalNetworkConfig opticalNetworkConfig;
public OpticalConfigProvider() {
super(new ProviderId("of", "org.onlab.onos.provider.opticalConfig", true));
}
@Activate
protected void activate() {
linkProviderService = linkProviderRegistry.register(this);
deviceProviderService = deviceProviderRegistry.register(this);
log.info("Starting optical network configuration process...");
log.info("Optical config file set to {}", configFileName);
loadOpticalConfig();
parseOpticalConfig();
publishOpticalConfig();
}
@Deactivate
protected void deactivate() {
linkProviderRegistry.unregister(this);
linkProviderService = null;
deviceProviderRegistry.unregister(this);
deviceProviderService = null;
RAW_ROADMS.clear();
RAW_WDMLINKS.clear();
RAW_PKTOPTLINKS.clear();
log.info("Stopped");
}
private void loadOpticalConfig() {
ObjectMapper mapper = new ObjectMapper();
opticalNetworkConfig = new OpticalNetworkConfig();
try {
opticalNetworkConfig = mapper.readValue(new File(configFileName), OpticalNetworkConfig.class);
} catch (JsonParseException e) {
String err = String.format("JsonParseException while loading network "
+ "config from file: %s: %s", configFileName, e.getMessage());
log.error(err, e);
} catch (JsonMappingException e) {
String err = String.format(
"JsonMappingException while loading network config "
+ "from file: %s: %s", configFileName, e.getMessage());
log.error(err, e);
} catch (IOException e) {
String err = String.format("IOException while loading network config "
+ "from file: %s %s", configFileName, e.getMessage());
log.error(err, e);
}
}
private void parseOpticalConfig() {
List<OpticalSwitchDescription> swList = opticalNetworkConfig.getOpticalSwitches();
List<OpticalLinkDescription> lkList = opticalNetworkConfig.getOpticalLinks();
for (OpticalSwitchDescription sw : swList) {
String swtype = sw.getType();
boolean allow = sw.isAllowed();
if (swtype.equals(ROADM) && allow) {
int regNum = 0;
Set<Map.Entry<String, JsonNode>> m = sw.params.entrySet();
for (Map.Entry<String, JsonNode> e : m) {
String key = e.getKey();
JsonNode j = e.getValue();
if (key.equals("numRegen")) {
regNum = j.asInt();
}
}
Roadm newRoadm = new Roadm();
newRoadm.setName(sw.name);
newRoadm.setNodeId(sw.nodeDpid);
newRoadm.setLongtitude(sw.longitude);
newRoadm.setLatitude(sw.latitude);
newRoadm.setRegenNum(regNum);
RAW_ROADMS.add(newRoadm);
log.info(newRoadm.toString());
}
}
for (OpticalLinkDescription lk : lkList) {
String lktype = lk.getType();
switch (lktype) {
case WDM_LINK:
WdmLink newWdmLink = new WdmLink();
newWdmLink.setSrcNodeId(lk.getNodeDpid1());
newWdmLink.setSnkNodeId(lk.getNodeDpid2());
newWdmLink.setAdminWeight(1000); // default weight for each WDM link.
Set<Map.Entry<String, JsonNode>> m = lk.params.entrySet();
for (Map.Entry<String, JsonNode> e : m) {
String key = e.getKey();
JsonNode j = e.getValue();
if (key.equals("nodeName1")) {
newWdmLink.setSrcNodeName(j.asText());
} else if (key.equals("nodeName2")) {
newWdmLink.setSnkNodeName(j.asText());
} else if (key.equals("port1")) {
newWdmLink.setSrcPort(j.asInt());
} else if (key.equals("port2")) {
newWdmLink.setSnkPort(j.asInt());
} else if (key.equals("distKms")) {
newWdmLink.setDistance(j.asDouble());
} else if (key.equals("numWaves")) {
newWdmLink.setWavelengthNumber(j.asInt());
} else {
log.error("error found");
// TODO add exception processing;
}
}
RAW_WDMLINKS.add(newWdmLink);
log.info(newWdmLink.toString());
break;
case PKT_OPT_LINK:
PktOptLink newPktOptLink = new PktOptLink();
newPktOptLink.setSrcNodeId(lk.getNodeDpid1());
newPktOptLink.setSnkNodeId(lk.getNodeDpid2());
newPktOptLink.setAdminWeight(10); // default weight for each packet-optical link.
Set<Map.Entry<String, JsonNode>> ptm = lk.params.entrySet();
for (Map.Entry<String, JsonNode> e : ptm) {
String key = e.getKey();
JsonNode j = e.getValue();
if (key.equals("nodeName1")) {
newPktOptLink.setSrcNodeName(j.asText());
} else if (key.equals("nodeName2")) {
newPktOptLink.setSnkNodeName(j.asText());
} else if (key.equals("port1")) {
newPktOptLink.setSrcPort(j.asInt());
} else if (key.equals("port2")) {
newPktOptLink.setSnkPort(j.asInt());
} else if (key.equals("bandWidth")) {
newPktOptLink.setBandwdith(j.asDouble());
} else {
log.error("error found");
// TODO add exception processing;
}
}
RAW_PKTOPTLINKS.add(newPktOptLink);
log.info(newPktOptLink.toString());
break;
default:
}
}
}
private void publishOpticalConfig() {
if (deviceProviderService == null || linkProviderService == null) {
return;
}
// Discover the optical ROADM objects
Iterator<Roadm> iterWdmNode = RAW_ROADMS.iterator();
while (iterWdmNode.hasNext()) {
Roadm value = iterWdmNode.next();
DeviceId did = deviceId("of:" + value.getNodeId().replace(":", ""));
ChassisId cid = new ChassisId(value.getNodeId());
DefaultAnnotations extendedAttributes = DefaultAnnotations.builder()
.set(OPTICAL_ANNOTATION + "switchType", "ROADM")
.set(OPTICAL_ANNOTATION + "switchName", value.getName())
.set(OPTICAL_ANNOTATION + "latitude", Double.toString(value.getLatitude()))
.set(OPTICAL_ANNOTATION + "longtitude", Double.toString(value.getLongtitude()))
.set(OPTICAL_ANNOTATION + "regNum", Integer.toString(value.getRegenNum()))
.build();
DeviceDescription description =
new DefaultDeviceDescription(did.uri(),
Device.Type.SWITCH,
"",
"",
"",
"",
cid,
extendedAttributes);
deviceProviderService.deviceConnected(did, description);
}
// Discover the optical WDM link objects
Iterator<WdmLink> iterWdmlink = RAW_WDMLINKS.iterator();
while (iterWdmlink.hasNext()) {
WdmLink value = iterWdmlink.next();
DeviceId srcNodeId = deviceId("of:" + value.getSrcNodeId().replace(":", ""));
DeviceId snkNodeId = deviceId("of:" + value.getSnkNodeId().replace(":", ""));
PortNumber srcPort = PortNumber.portNumber(value.getSrcPort());
PortNumber snkPort = PortNumber.portNumber(value.getSnkPort());
ConnectPoint srcPoint = new ConnectPoint(srcNodeId, srcPort);
ConnectPoint snkPoint = new ConnectPoint(snkNodeId, snkPort);
DefaultAnnotations extendedAttributes = DefaultAnnotations.builder()
.set(OPTICAL_ANNOTATION + "linkType", "WDM")
.set(OPTICAL_ANNOTATION + "distance", Double.toString(value.getDistance()))
.set(OPTICAL_ANNOTATION + "cost", Double.toString(value.getDistance()))
.set(OPTICAL_ANNOTATION + "adminWeight", Double.toString(value.getAdminWeight()))
.set(OPTICAL_ANNOTATION + "wavelengthNum", Integer.toString(value.getWavelengthNumber()))
.build();
DefaultLinkDescription linkDescription =
new DefaultLinkDescription(srcPoint,
snkPoint,
Link.Type.DIRECT,
extendedAttributes);
linkProviderService.linkDetected(linkDescription);
log.info(String.format("WDM link: %s : %s",
linkDescription.src().toString(), linkDescription.dst().toString()));
}
// Discover the packet optical link objects
Iterator<PktOptLink> iterPktOptlink = RAW_PKTOPTLINKS.iterator();
while (iterPktOptlink.hasNext()) {
PktOptLink value = iterPktOptlink.next();
DeviceId srcNodeId = deviceId("of:" + value.getSrcNodeId().replace(":", ""));
DeviceId snkNodeId = deviceId("of:" + value.getSnkNodeId().replace(":", ""));
PortNumber srcPort = PortNumber.portNumber(value.getSrcPort());
PortNumber snkPort = PortNumber.portNumber(value.getSnkPort());
ConnectPoint srcPoint = new ConnectPoint(srcNodeId, srcPort);
ConnectPoint snkPoint = new ConnectPoint(snkNodeId, snkPort);
DefaultAnnotations extendedAttributes = DefaultAnnotations.builder()
.set(OPTICAL_ANNOTATION + "linkType", "PktOptLink")
.set(OPTICAL_ANNOTATION + "bandwidth", Double.toString(value.getBandwidth()))
.set(OPTICAL_ANNOTATION + "cost", Double.toString(value.getBandwidth()))
.set(OPTICAL_ANNOTATION + "adminWeight", Double.toString(value.getAdminWeight()))
.build();
DefaultLinkDescription linkDescription =
new DefaultLinkDescription(srcPoint,
snkPoint,
Link.Type.DIRECT,
extendedAttributes);
linkProviderService.linkDetected(linkDescription);
log.info(String.format("Packet-optical link: %s : %s",
linkDescription.src().toString(), linkDescription.dst().toString()));
}
}
@Override
public void triggerProbe(Device device) {
// TODO We may want to consider re-reading config files and publishing them based on this event.
}
@Override
public void roleChanged(Device device, MastershipRole newRole) {
// TODO Auto-generated method stub.
}
}
package org.onlab.onos.optical.cfg;
import java.util.Map;
import org.codehaus.jackson.JsonNode;
import org.onlab.util.HexString;
/**
* Public class corresponding to JSON described data model.
*/
public class OpticalLinkDescription {
protected String type;
protected Boolean allowed;
protected long dpid1;
protected long dpid2;
protected String nodeDpid1;
protected String nodeDpid2;
protected Map<String, JsonNode> params;
protected Map<String, String> publishAttributes;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Boolean isAllowed() {
return allowed;
}
public void setAllowed(Boolean allowed) {
this.allowed = allowed;
}
public String getNodeDpid1() {
return nodeDpid1;
}
public void setNodeDpid1(String nodeDpid1) {
this.nodeDpid1 = nodeDpid1;
this.dpid1 = HexString.toLong(nodeDpid1);
}
public String getNodeDpid2() {
return nodeDpid2;
}
public void setNodeDpid2(String nodeDpid2) {
this.nodeDpid2 = nodeDpid2;
this.dpid2 = HexString.toLong(nodeDpid2);
}
public long getDpid1() {
return dpid1;
}
public void setDpid1(long dpid1) {
this.dpid1 = dpid1;
this.nodeDpid1 = HexString.toHexString(dpid1);
}
public long getDpid2() {
return dpid2;
}
public void setDpid2(long dpid2) {
this.dpid2 = dpid2;
this.nodeDpid2 = HexString.toHexString(dpid2);
}
public Map<String, JsonNode> getParams() {
return params;
}
public void setParams(Map<String, JsonNode> params) {
this.params = params;
}
public Map<String, String> getPublishAttributes() {
return publishAttributes;
}
public void setPublishAttributes(Map<String, String> publishAttributes) {
this.publishAttributes = publishAttributes;
}
}
package org.onlab.onos.optical.cfg;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Public class corresponding to JSON described data model.
*/
public class OpticalNetworkConfig {
protected static final Logger log = LoggerFactory.getLogger(OpticalNetworkConfig.class);
private List<OpticalSwitchDescription> opticalSwitches;
private List<OpticalLinkDescription> opticalLinks;
public OpticalNetworkConfig() {
opticalSwitches = new ArrayList<OpticalSwitchDescription>();
opticalLinks = new ArrayList<OpticalLinkDescription>();
}
public List<OpticalSwitchDescription> getOpticalSwitches() {
return opticalSwitches;
}
public void setOpticalSwitches(List<OpticalSwitchDescription> switches) {
this.opticalSwitches = switches;
}
public List<OpticalLinkDescription> getOpticalLinks() {
return opticalLinks;
}
public void setOpticalLinks(List<OpticalLinkDescription> links) {
this.opticalLinks = links;
}
}
package org.onlab.onos.optical.cfg;
import java.util.Map;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.annotate.JsonProperty;
import org.onlab.util.HexString;
/**
* Public class corresponding to JSON described data model.
*/
public class OpticalSwitchDescription {
protected String name;
protected long dpid;
protected String nodeDpid;
protected String type;
protected double latitude;
protected double longitude;
protected boolean allowed;
protected Map<String, JsonNode> params;
protected Map<String, String> publishAttributes;
public String getName() {
return name;
}
@JsonProperty("name")
public void setName(String name) {
this.name = name;
}
public long getDpid() {
return dpid;
}
@JsonProperty("dpid")
public void setDpid(long dpid) {
this.dpid = dpid;
this.nodeDpid = HexString.toHexString(dpid);
}
public String getNodeDpid() {
return nodeDpid;
}
public String getHexDpid() {
return nodeDpid;
}
public void setNodeDpid(String nodeDpid) {
this.nodeDpid = nodeDpid;
this.dpid = HexString.toLong(nodeDpid);
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public double getLatitude() {
return latitude;
}
public void setLatitude(double latitude) {
this.latitude = latitude;
}
public double getLongitude() {
return longitude;
}
public void setLongitude(double longitude) {
this.longitude = longitude;
}
public boolean isAllowed() {
return allowed;
}
public void setAllowed(boolean allowed) {
this.allowed = allowed;
}
public Map<String, JsonNode> getParams() {
return params;
}
public void setParams(Map<String, JsonNode> params) {
this.params = params;
}
public Map<String, String> getPublishAttributes() {
return publishAttributes;
}
public void setPublishAttributes(Map<String, String> publishAttributes) {
this.publishAttributes = publishAttributes;
}
}
package org.onlab.onos.optical.cfg;
/**
* Packet-optical link Java data object.
*/
class PktOptLink {
private String srcNodeName;
private String snkNodeName;
private String srcNodeId;
private String snkNodeId;
private int srcPort;
private int snkPort;
private double bandwidth;
private double cost;
private long adminWeight;
public PktOptLink(String srcName, String snkName) {
this.srcNodeName = srcName;
this.snkNodeName = snkName;
}
public PktOptLink() {
// TODO Auto-generated constructor stub
}
public void setSrcNodeName(String name) {
this.srcNodeName = name;
}
public String getSrcNodeName() {
return this.srcNodeName;
}
public void setSnkNodeName(String name) {
this.snkNodeName = name;
}
public String getSnkNodeName() {
return this.snkNodeName;
}
public void setSrcNodeId(String nodeId) {
this.srcNodeId = nodeId;
}
public String getSrcNodeId() {
return this.srcNodeId;
}
public void setSnkNodeId(String nodeId) {
this.snkNodeId = nodeId;
}
public String getSnkNodeId() {
return this.snkNodeId;
}
public void setSrcPort(int port) {
this.srcPort = port;
}
public int getSrcPort() {
return this.srcPort;
}
public void setSnkPort(int port) {
this.snkPort = port;
}
public int getSnkPort() {
return this.snkPort;
}
public void setBandwdith(double x) {
this.bandwidth = x;
}
public double getBandwidth() {
return this.bandwidth;
}
public void setCost(double x) {
this.cost = x;
}
public double getCost() {
return this.cost;
}
public void setAdminWeight(long x) {
this.adminWeight = x;
}
public long getAdminWeight() {
return this.adminWeight;
}
@Override
public String toString() {
return new StringBuilder(" srcNodeName: ").append(this.srcNodeName)
.append(" snkNodeName: ").append(this.snkNodeName)
.append(" srcNodeId: ").append(this.srcNodeId)
.append(" snkNodeId: ").append(this.snkNodeId)
.append(" srcPort: ").append(this.srcPort)
.append(" snkPort: ").append(this.snkPort)
.append(" bandwidth: ").append(this.bandwidth)
.append(" cost: ").append(this.cost)
.append(" adminWeight: ").append(this.adminWeight).toString();
}
}
package org.onlab.onos.optical.cfg;
/**
* ROADM java data object converted from a JSON file.
*/
class Roadm {
private String name;
private String nodeID;
private double longtitude;
private double latitude;
private int regenNum;
//TODO use the following attributes when needed for configurations
private int tPort10G;
private int tPort40G;
private int tPort100G;
private int wPort;
public Roadm() {
}
public Roadm(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setNodeId(String nameId) {
this.nodeID = nameId;
}
public String getNodeId() {
return this.nodeID;
}
public void setLongtitude(double x) {
this.longtitude = x;
}
public double getLongtitude() {
return this.longtitude;
}
public void setLatitude(double y) {
this.latitude = y;
}
public double getLatitude() {
return this.latitude;
}
public void setRegenNum(int num) {
this.regenNum = num;
}
public int getRegenNum() {
return this.regenNum;
}
public void setTport10GNum(int num) {
this.tPort10G = num;
}
public int getTport10GNum() {
return this.tPort10G;
}
public void setTport40GNum(int num) {
this.tPort40G = num;
}
public int getTport40GNum() {
return this.tPort40G;
}
public void setTport100GNum(int num) {
this.tPort100G = num;
}
public int getTport100GNum() {
return this.tPort100G;
}
public void setWportNum(int num) {
this.wPort = num;
}
public int getWportNum() {
return this.wPort;
}
@Override
public String toString() {
return new StringBuilder(" ROADM Name: ").append(this.name)
.append(" nodeID: ").append(this.nodeID)
.append(" longtitude: ").append(this.longtitude)
.append(" latitude: ").append(this.latitude)
.append(" regenNum: ").append(this.regenNum)
.append(" 10GTportNum: ").append(this.tPort10G)
.append(" 40GTportNum: ").append(this.tPort40G)
.append(" 100GTportNum: ").append(this.tPort100G)
.append(" WportNum: ").append(this.wPort).toString();
}
}
package org.onlab.onos.optical.cfg;
/**
* WDM Link Java data object converted from a JSON file.
*/
class WdmLink {
private String srcNodeName;
private String snkNodeName;
private String srcNodeId;
private String snkNodeId;
private int srcPort;
private int snkPort;
private double distance;
private double cost;
private int wavelengthNumber;
private long adminWeight;
public WdmLink(String name1, String name2) {
this.srcNodeName = name1;
this.snkNodeName = name2;
}
public WdmLink() {
// TODO Auto-generated constructor stub
}
public void setSrcNodeName(String name) {
this.srcNodeName = name;
}
public String getSrcNodeName() {
return this.srcNodeName;
}
public void setSnkNodeName(String name) {
this.snkNodeName = name;
}
public String getSnkNodeName() {
return this.snkNodeName;
}
public void setSrcNodeId(String nodeId) {
this.srcNodeId = nodeId;
}
public String getSrcNodeId() {
return this.srcNodeId;
}
public void setSnkNodeId(String nodeId) {
this.snkNodeId = nodeId;
}
public String getSnkNodeId() {
return this.snkNodeId;
}
public void setSrcPort(int port) {
this.srcPort = port;
}
public int getSrcPort() {
return this.srcPort;
}
public void setSnkPort(int port) {
this.snkPort = port;
}
public int getSnkPort() {
return this.snkPort;
}
public void setDistance(double x) {
this.distance = x;
}
public double getDistance() {
return this.distance;
}
public void setCost(double x) {
this.cost = x;
}
public double getCost() {
return this.cost;
}
public void setWavelengthNumber(int x) {
this.wavelengthNumber = x;
}
public int getWavelengthNumber() {
return this.wavelengthNumber;
}
public void setAdminWeight(long x) {
this.adminWeight = x;
}
public long getAdminWeight() {
return this.adminWeight;
}
@Override
public String toString() {
return new StringBuilder(" srcNodeName: ").append(this.srcNodeName)
.append(" snkNodeName: ").append(this.snkNodeName)
.append(" srcNodeId: ").append(this.srcNodeId)
.append(" snkNodeId: ").append(this.snkNodeId)
.append(" srcPort: ").append(this.srcPort)
.append(" snkPort: ").append(this.snkPort)
.append(" distance: ").append(this.distance)
.append(" cost: ").append(this.cost)
.append(" wavelengthNumber: ").append(this.wavelengthNumber)
.append(" adminWeight: ").append(this.adminWeight).toString();
}
}
{
"opticalSwitches": [
{
"allowed": true,
"latitude": 37.6,
"longitude": 122.3,
"name": "SFO-W10",
"nodeDpid": "00:00:ff:ff:ff:ff:ff:01",
"params": {
"numRegen": 0
},
"type": "Roadm"
},
{
"allowed": true,
"latitude": 37.3,
"longitude": 121.9,
"name": "SJC-W10",
"nodeDpid": "00:00:ff:ff:ff:ff:ff:02",
"params": {
"numRegen": 0
},
"type": "Roadm"
},
{
"allowed": true,
"latitude": 33.9,
"longitude": 118.4
"name": "LAX-W10",
"nodeDpid": "00:00:ff:ff:ff:ff:ff:03",
"params": {
"numRegen": 0
},
"type": "Roadm"
},
{
"allowed": true,
"latitude": 32.8,
"longitude": 117.1,
"name": "SDG-W10",
"nodeDpid": "00:00:ff:ff:ff:ff:ff:04",
"params": {
"numRegen": 3
},
"type": "Roadm"
},
{
"allowed": true,
"latitude": 44.8,
"longitude": 93.1,
"name": "MSP-M10",
"nodeDpid": "00:00:ff:ff:ff:ff:ff:05",
"params": {
"numRegen": 3
},
"type": "Roadm"
},
{
"allowed": true,
"latitude": 32.8,
"longitude": 97.1,
"name": "DFW-M10",
"nodeDpid": "00:00:ff:ff:ff:ff:ff:06",
"params": {
"numRegen": 3
},
"type": "Roadm"
},
{
"allowed": true,
"latitude": 41.8,
"longitude": 120.1,
"name": "CHG-N10",
"nodeDpid": "00:00:ff:ff:ff:ff:ff:07",
"params": {
"numRegen": 3
},
"type": "Roadm"
},
{
"allowed": true,
"latitude": 38.8,
"longitude": 77.1,
"name": "IAD-M10",
"nodeDpid": "00:00:ff:ff:ff:ff:ff:08",
"params": {
"numRegen": 3
},
"type": "Roadm"
},
{
"allowed": true,
"latitude": 40.8,
"longitude": 73.1,
"name": "JFK-E10",
"nodeDpid": "00:00:ff:ff:ff:ff:ff:09",
"params": {
"numRegen": 0
},
"type": "Roadm"
},
{
"allowed": true,
"latitude": 33.8,
"longitude": 84.1,
"name": "ATL-S10",
"nodeDpid": "00:00:ff:ff:ff:ff:ff:0A",
"params": {
"numRegen": 0
},
"type": "Roadm"
}
],
"opticalLinks": [
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:ff:01",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:02",
"params": {
"distKms": 1000,
"nodeName1": "SFO-W10",
"nodeName2": "SJC-W10",
"numWaves": 80,
"port1": 10,
"port2": 10
},
"type": "wdmLink"
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:ff:02",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:03",
"params": {
"distKms": 1000,
"nodeName1": "SJC-W10",
"nodeName2": "LAX-W10",
"numWaves": 80,
"port1": 20,
"port2": 10
},
"type": "wdmLink"
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:ff:03",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:04",
"params": {
"distKms": 1000,
"nodeName1": "LAX-W10",
"nodeName2": "SDG-W10",
"numWaves": 80,
"port1": 30,
"port2": 10
},
"type": "wdmLink"
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:ff:02",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:05",
"params": {
"distKms": 4000,
"nodeName1": "SJC-W10",
"nodeName2": "MSP-M10",
"numWaves": 80,
"port1": 20,
"port2": 10
},
"type": "wdmLink"
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:ff:03",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:06",
"params": {
"distKms": 5000,
"nodeName1": "LAX-W10",
"nodeName2": "DFW-M10",
"numWaves": 80,
"port1": 20,
"port2": 10
},
"type": "wdmLink"
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:ff:05",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:06",
"params": {
"distKms": 3000,
"nodeName1": "MSP-M10",
"nodeName2": "DFW-M10",
"numWaves": 80,
"port1": 30,
"port2": 20
},
"type": "wdmLink"
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:ff:05",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:07",
"params": {
"distKms": 3000,
"nodeName1": "MSP-M10",
"nodeName2": "CHG-N10",
"numWaves": 80,
"port1": 20,
"port2": 21
},
"type": "wdmLink"
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:ff:06",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:08",
"params": {
"distKms": 4000,
"nodeName1": "DFW-M10",
"nodeName2": "IAD-M10",
"numWaves": 80,
"port1": 30,
"port2": 10
},
"type": "wdmLink"
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:ff:07",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:08",
"params": {
"distKms": 4000,
"nodeName1": "CHG-M10",
"nodeName2": "IAD-M10",
"numWaves": 80,
"port1": 30,
"port2": 20
},
"type": "wdmLink"
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:ff:07",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:09",
"params": {
"distKms": 5000,
"nodeName1": "CHG-M10",
"nodeName2": "JFK-E10",
"numWaves": 80,
"port1": 20,
"port2": 10
},
"type": "wdmLink"
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:ff:08",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:0A",
"params": {
"distKms": 3000,
"nodeName1": "IAD-M10",
"nodeName2": "ATL-S10",
"numWaves": 80,
"port1": 30,
"port2": 10
},
"type": "wdmLink"
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:ff:09",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:0A",
"params": {
"distKms": 4000,
"nodeName1": "JFK-E10",
"nodeName2": "ATL-S10",
"numWaves": 80,
"port1": 20,
"port2": 20
},
"type": "wdmLink"
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:00:01",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:01",
"params": {
"nodeName1": "SFO-R10",
"nodeName2": "SFO-W10",
"port1": 10,
"port2": 1
},
"type": "pktOptLink"
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:00:03",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:03",
"params": {
"nodeName1": "LAX-R10",
"nodeName2": "LAX-W10",
"port1": 10,
"port2": 1
},
"type": "pktOptLink"
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:00:04",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:04",
"params": {
"nodeName1": "SDG-R10",
"nodeName2": "SDG-W10",
"port1": 10,
"port2": 1
},
"type": "pktOptLink"
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:00:07",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:07",
"params": {
"nodeName1": "CHG-R10",
"nodeName2": "CHG-W10",
"port1": 10,
"port2": 1
},
"type": "pktOptLink"
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:00:09",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:09",
"params": {
"nodeName1": "JFK-R10",
"nodeName2": "JFK-W10",
"port1": 10,
"port2": 1
},
"type": "pktOptLink"
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:00:0A",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:0A",
"params": {
"nodeName1": "ATL-R10",
"nodeName2": "ATL-W10",
"port1": 10,
"port2": 1
},
"type": "pktOptLink"
},
]
}
{
"opticalSwitches": [
{
"allowed": true,
"latitude": 37.6,
"longitude": 122.3,
"name": "ROADM1",
"nodeDpid": "00:00:ff:ff:ff:ff:ff:01",
"params": {
"numRegen": 0
},
"type": "Roadm"
},
{
"allowed": true,
"latitude": 37.3,
"longitude": 121.9,
"name": "ROADM2",
"nodeDpid": "00:00:ff:ff:ff:ff:ff:02",
"params": {
"numRegen": 0
},
"type": "Roadm"
},
{
"allowed": true,
"latitude": 33.9,
"longitude": 118.4,
"name": "ROADM3",
"nodeDpid": "00:00:ff:ff:ff:ff:ff:03",
"params": {
"numRegen": 2
},
"type": "Roadm"
}
],
"opticalLinks": [
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:ff:01",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:03",
"params": {
"distKms": 1000,
"nodeName1": "ROADM1",
"nodeName2": "ROADM3",
"numWaves": 80,
"port1": 10,
"port2": 30
},
"type": "wdmLink"
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:ff:03",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:02",
"params": {
"distKms": 2000,
"nodeName1": "ROADM3",
"nodeName2": "ROADM2",
"numWaves": 80,
"port1": 31,
"port2": 20
},
"type": "wdmLink"
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:00:01",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:01",
"params": {
"nodeName1": "ROUTER1",
"nodeName2": "ROADM1",
"bandWidth": 100000,
"port1": 10,
"port2": 11
},
"type": "pktOptLink"
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:00:02",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:02",
"params": {
"nodeName1": "ROUTER2",
"nodeName2": "ROADM2",
"bandWidth": 100000,
"port1": 10,
"port2": 21
},
"type": "pktOptLink"
}
]
}
......@@ -25,6 +25,8 @@
<module>proxyarp</module>
<module>config</module>
<module>sdnip</module>
<module>calendar</module>
<module>optical</module>
</modules>
<properties>
......
......@@ -42,6 +42,30 @@
<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>
......
......@@ -25,10 +25,10 @@ import org.slf4j.LoggerFactory;
/**
* Manages the connectivity requirements between peers.
*/
public class PeerConnectivity {
public class PeerConnectivityManager {
private static final Logger log = LoggerFactory.getLogger(
PeerConnectivity.class);
PeerConnectivityManager.class);
// TODO these shouldn't be defined here
private static final short BGP_PORT = 179;
......@@ -41,7 +41,7 @@ public class PeerConnectivity {
// TODO this sucks.
private int intentId = 0;
public PeerConnectivity(SdnIpConfigService configInfoService,
public PeerConnectivityManager(SdnIpConfigService configInfoService,
InterfaceService interfaceService, IntentService intentService) {
this.configInfoService = configInfoService;
this.interfaceService = interfaceService;
......
......@@ -62,10 +62,8 @@ public class Router implements RouteListener {
private static final Logger log = LoggerFactory.getLogger(Router.class);
// Store all route updates in a InvertedRadixTree.
// The key in this Tree is the binary sting of prefix of route.
// The Ip4Address is the next hop address of route, and is also the value
// of each entry.
// Store all route updates in a radix tree.
// The key in this tree is the binary string of prefix of the route.
private InvertedRadixTree<RouteEntry> bgpRoutes;
// Stores all incoming route updates in a queue.
......@@ -102,7 +100,7 @@ public class Router implements RouteListener {
* Class constructor.
*
* @param intentService the intent service
* @param proxyArp the proxy ARP service
* @param hostService the host service
* @param configInfoService the configuration service
* @param interfaceService the interface service
*/
......@@ -135,6 +133,10 @@ public class Router implements RouteListener {
*/
public void start() {
// TODO hack to enable SDN-IP now for testing
isElectedLeader = true;
isActivatedLeader = true;
bgpUpdatesExecutor.execute(new Runnable() {
@Override
public void run() {
......@@ -441,8 +443,8 @@ public class Router implements RouteListener {
/**
* Processes adding a route entry.
* <p/>
* Put new route entry into InvertedRadixTree. If there was an existing
* nexthop for this prefix, but the next hop was different, then execute
* Put new route entry into the radix tree. If there was an existing
* next hop for this prefix, but the next hop was different, then execute
* deleting old route entry. If the next hop is the SDN domain, we do not
* handle it at the moment. Otherwise, execute adding a route.
*
......@@ -623,8 +625,8 @@ public class Router implements RouteListener {
/**
* Executes deleting a route entry.
* <p/>
* Removes prefix from InvertedRadixTree, if success, then try to delete
* the relative intent.
* Removes prefix from radix tree, and if successful, then try to delete
* the related intent.
*
* @param routeEntry the route entry to delete
*/
......@@ -690,9 +692,9 @@ public class Router implements RouteListener {
public void arpResponse(IpAddress ipAddress, MacAddress macAddress) {
log.debug("Received ARP response: {} => {}", ipAddress, macAddress);
// We synchronize on this to prevent changes to the InvertedRadixTree
// while we're pushing intent. If the InvertedRadixTree changes, the
// InvertedRadixTree and intent could get out of sync.
// We synchronize on this to prevent changes to the radix tree
// while we're pushing intents. If the tree changes, the
// tree and intents could get out of sync.
synchronized (this) {
Set<RouteEntry> routesToPush =
......@@ -709,14 +711,14 @@ public class Router implements RouteListener {
log.debug("Pushing prefix {} next hop {}",
routeEntry.prefix(), routeEntry.nextHop());
// We only push prefix flows if the prefix is still in the
// InvertedRadixTree and the next hop is the same as our
// radix tree and the next hop is the same as our
// update.
// The prefix could have been removed while we were waiting
// for the ARP, or the next hop could have changed.
addRouteIntentToNextHop(prefix, ipAddress, macAddress);
} else {
log.debug("Received ARP response, but {}/{} is no longer in"
+ " InvertedRadixTree", routeEntry.prefix(),
+ " the radix tree", routeEntry.prefix(),
routeEntry.nextHop());
}
}
......
......@@ -2,14 +2,18 @@ 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;
......@@ -17,10 +21,11 @@ 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());
......@@ -31,7 +36,7 @@ public class SdnIp {
protected HostService hostService;
private SdnIpConfigReader config;
private PeerConnectivity peerConnectivity;
private PeerConnectivityManager peerConnectivity;
private Router router;
private BgpSessionManager bgpSessionManager;
......@@ -44,7 +49,7 @@ public class SdnIp {
InterfaceService interfaceService = new HostServiceBasedInterfaceService(hostService);
peerConnectivity = new PeerConnectivity(config, interfaceService, intentService);
peerConnectivity = new PeerConnectivityManager(config, interfaceService, intentService);
peerConnectivity.start();
router = new Router(intentService, hostService, config, interfaceService);
......@@ -64,4 +69,18 @@ public class SdnIp {
protected void deactivate() {
log.info("Stopped");
}
@Override
public Collection<BgpRouteEntry> getBgpRoutes() {
return bgpSessionManager.getBgpRoutes();
}
@Override
public Collection<RouteEntry> getRoutes() {
return router.getRoutes();
}
static String dpidToUri(String dpid) {
return "of:" + dpid.replace(":", "");
}
}
......
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.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.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.reportMatcher;
import static org.easymock.EasyMock.reset;
import static org.easymock.EasyMock.verify;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.easymock.IArgumentMatcher;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.flow.DefaultTrafficSelector;
import org.onlab.onos.net.flow.DefaultTrafficTreatment;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
import org.onlab.onos.net.intent.IntentId;
import org.onlab.onos.net.intent.IntentService;
import org.onlab.onos.net.intent.PointToPointIntent;
import org.onlab.onos.sdnip.bgp.BgpConstants;
import org.onlab.onos.sdnip.config.BgpPeer;
import org.onlab.onos.sdnip.config.BgpSpeaker;
import org.onlab.onos.sdnip.config.Interface;
import org.onlab.onos.sdnip.config.InterfaceAddress;
import org.onlab.onos.sdnip.config.SdnIpConfigService;
import org.onlab.packet.Ethernet;
import org.onlab.packet.IPv4;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import com.google.common.collect.Sets;
/**
* Unit tests for PeerConnectivityManager interface.
*/
public class PeerConnectivityManagerTest {
private PeerConnectivityManager peerConnectivityManager;
private IntentService intentService;
private SdnIpConfigService configInfoService;
private InterfaceService interfaceService;
private Map<String, BgpSpeaker> bgpSpeakers;
private Map<String, Interface> interfaces;
private Map<IpAddress, BgpPeer> peers;
private Map<String, BgpSpeaker> configuredBgpSpeakers;
private Map<String, Interface> configuredInterfaces;
private Map<IpAddress, BgpPeer> configuredPeers;
private List<PointToPointIntent> intentList;
private final String dpid1 = "00:00:00:00:00:00:00:01";
private final String dpid2 = "00:00:00:00:00:00:00:02";
private final DeviceId deviceId1 =
DeviceId.deviceId(SdnIp.dpidToUri(dpid1));
private final DeviceId deviceId2 =
DeviceId.deviceId(SdnIp.dpidToUri(dpid2));
// Interfaces connected to BGP speakers
private final ConnectPoint s1Eth100 =
new ConnectPoint(deviceId1, PortNumber.portNumber(100));
private final ConnectPoint s2Eth100 =
new ConnectPoint(deviceId2, PortNumber.portNumber(100));
// Interfaces connected to BGP peers
private final ConnectPoint s1Eth1 =
new ConnectPoint(deviceId1, PortNumber.portNumber(1));
private final ConnectPoint s2Eth1 =
new ConnectPoint(deviceId2, PortNumber.portNumber(1));
// We don't compare the intent ID so all expected intents can use the same ID
private final IntentId testIntentId = new IntentId(0);
private final TrafficTreatment noTreatment =
DefaultTrafficTreatment.builder().build();
@Before
public void setUp() throws Exception {
bgpSpeakers = Collections.unmodifiableMap(setUpBgpSpeakers());
interfaces = Collections.unmodifiableMap(setUpInterfaces());
peers = Collections.unmodifiableMap(setUpPeers());
initPeerConnectivity();
intentList = setUpIntentList();
}
/**
* Sets up BGP speakers.
*
* @return configured BGP speakers as a map from speaker name to speaker
*/
private Map<String, BgpSpeaker> setUpBgpSpeakers() {
configuredBgpSpeakers = new HashMap<>();
BgpSpeaker bgpSpeaker1 = new BgpSpeaker(
"bgpSpeaker1",
"00:00:00:00:00:00:00:01", 100,
"00:00:00:00:00:01");
List<InterfaceAddress> interfaceAddresses1 =
new LinkedList<InterfaceAddress>();
interfaceAddresses1.add(new InterfaceAddress(dpid1, 1, "192.168.10.101"));
interfaceAddresses1.add(new InterfaceAddress(dpid2, 1, "192.168.20.101"));
bgpSpeaker1.setInterfaceAddresses(interfaceAddresses1);
configuredBgpSpeakers.put(bgpSpeaker1.name(), bgpSpeaker1);
// BGP speaker2 is attached to the same switch port with speaker1
BgpSpeaker bgpSpeaker2 = new BgpSpeaker(
"bgpSpeaker2",
"00:00:00:00:00:00:00:01", 100,
"00:00:00:00:00:02");
List<InterfaceAddress> interfaceAddresses2 =
new LinkedList<InterfaceAddress>();
interfaceAddresses2.add(new InterfaceAddress(dpid1, 1, "192.168.10.102"));
interfaceAddresses2.add(new InterfaceAddress(dpid2, 1, "192.168.20.102"));
bgpSpeaker2.setInterfaceAddresses(interfaceAddresses2);
configuredBgpSpeakers.put(bgpSpeaker2.name(), bgpSpeaker2);
BgpSpeaker bgpSpeaker3 = new BgpSpeaker(
"bgpSpeaker3",
"00:00:00:00:00:00:00:02", 100,
"00:00:00:00:00:03");
List<InterfaceAddress> interfaceAddresses3 =
new LinkedList<InterfaceAddress>();
interfaceAddresses3.add(new InterfaceAddress(dpid1, 1, "192.168.10.103"));
interfaceAddresses3.add(new InterfaceAddress(dpid2, 1, "192.168.20.103"));
bgpSpeaker3.setInterfaceAddresses(interfaceAddresses3);
configuredBgpSpeakers.put(bgpSpeaker3.name(), bgpSpeaker3);
return configuredBgpSpeakers;
}
/**
* Sets up logical interfaces, which emulate the configured interfaces
* in SDN-IP application.
*
* @return configured interfaces as a MAP from Interface name to Interface
*/
private Map<String, Interface> setUpInterfaces() {
configuredInterfaces = new HashMap<>();
String interfaceSw1Eth1 = "s1-eth1";
Interface intfsw1eth1 = new Interface(s1Eth1,
Collections.singleton(IpPrefix.valueOf("192.168.10.0/24")),
MacAddress.valueOf("00:00:00:00:00:01"));
configuredInterfaces.put(interfaceSw1Eth1, intfsw1eth1);
String interfaceSw2Eth1 = "s2-eth1";
Interface intfsw2eth1 = new Interface(s2Eth1,
Collections.singleton(IpPrefix.valueOf("192.168.20.0/24")),
MacAddress.valueOf("00:00:00:00:00:02"));
configuredInterfaces.put(interfaceSw2Eth1, intfsw2eth1);
interfaceService = createMock(InterfaceService.class);
expect(interfaceService.getInterface(s1Eth1))
.andReturn(intfsw1eth1).anyTimes();
expect(interfaceService.getInterface(s2Eth1))
.andReturn(intfsw2eth1).anyTimes();
// Non-existent interface used during one of the tests
expect(interfaceService.getInterface(new ConnectPoint(
DeviceId.deviceId(SdnIp.dpidToUri("00:00:00:00:00:00:01:00")),
PortNumber.portNumber(1))))
.andReturn(null).anyTimes();
expect(interfaceService.getInterfaces()).andReturn(
Sets.newHashSet(configuredInterfaces.values())).anyTimes();
replay(interfaceService);
return configuredInterfaces;
}
/**
* Sets up BGP daemon peers.
*
* @return configured BGP peers as a MAP from peer IP address to BgpPeer
*/
private Map<IpAddress, BgpPeer> setUpPeers() {
configuredPeers = new HashMap<>();
String peerSw1Eth1 = "192.168.10.1";
configuredPeers.put(IpAddress.valueOf(peerSw1Eth1),
new BgpPeer(dpid1, 1, peerSw1Eth1));
// Two BGP peers are connected to switch 2 port 1.
String peer1Sw2Eth1 = "192.168.20.1";
configuredPeers.put(IpAddress.valueOf(peer1Sw2Eth1),
new BgpPeer(dpid2, 1, peer1Sw2Eth1));
String peer2Sw2Eth1 = "192.168.20.2";
configuredPeers.put(IpAddress.valueOf(peer2Sw2Eth1),
new BgpPeer(dpid2, 1, peer2Sw2Eth1));
return configuredPeers;
}
/**
* Sets up expected point to point intent list.
*
* @return point to point intent list
*/
private List<PointToPointIntent> setUpIntentList() {
intentList = new ArrayList<PointToPointIntent>();
setUpBgpIntents();
setUpIcmpIntents();
return intentList;
}
/**
* Constructs a BGP intent and put it into the intentList.
* <p/>
* The purpose of this method is too simplify the setUpBgpIntents() method,
* and to make the setUpBgpIntents() easy to read.
*
* @param srcPrefix source IP prefix to match
* @param dstPrefix destination IP prefix to match
* @param srcTcpPort source TCP port to match
* @param dstTcpPort destination TCP port to match
* @param srcConnectPoint source connect point for PointToPointIntent
* @param dstConnectPoint destination connect point for PointToPointIntent
*/
private void bgpPathintentConstructor(String srcPrefix, String dstPrefix,
Short srcTcpPort, Short dstTcpPort,
ConnectPoint srcConnectPoint, ConnectPoint dstConnectPoint) {
TrafficSelector.Builder builder = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPProtocol(IPv4.PROTOCOL_TCP)
.matchIPSrc(IpPrefix.valueOf(srcPrefix))
.matchIPDst(IpPrefix.valueOf(dstPrefix));
if (srcTcpPort != null) {
builder.matchTcpSrc(srcTcpPort);
}
if (dstTcpPort != null) {
builder.matchTcpDst(dstTcpPort);
}
PointToPointIntent intent = new PointToPointIntent(
testIntentId, builder.build(), noTreatment,
srcConnectPoint, dstConnectPoint);
intentList.add(intent);
}
/**
* Sets up intents for BGP paths.
*/
private void setUpBgpIntents() {
Short bgpPort = Short.valueOf((short) BgpConstants.BGP_PORT);
// Start to build intents between BGP speaker1 and BGP peer1
bgpPathintentConstructor(
"192.168.10.101/32", "192.168.10.1/32", null, bgpPort,
s1Eth100, s1Eth1);
bgpPathintentConstructor(
"192.168.10.101/32", "192.168.10.1/32", bgpPort, null,
s1Eth100, s1Eth1);
bgpPathintentConstructor(
"192.168.10.1/32", "192.168.10.101/32", null, bgpPort,
s1Eth1, s1Eth100);
bgpPathintentConstructor(
"192.168.10.1/32", "192.168.10.101/32", bgpPort, null,
s1Eth1, s1Eth100);
// Start to build intents between BGP speaker1 and BGP peer2
bgpPathintentConstructor(
"192.168.20.101/32", "192.168.20.1/32", null, bgpPort,
s1Eth100, s2Eth1);
bgpPathintentConstructor(
"192.168.20.101/32", "192.168.20.1/32", bgpPort, null,
s1Eth100, s2Eth1);
bgpPathintentConstructor(
"192.168.20.1/32", "192.168.20.101/32", null, bgpPort,
s2Eth1, s1Eth100);
bgpPathintentConstructor(
"192.168.20.1/32", "192.168.20.101/32", bgpPort, null,
s2Eth1, s1Eth100);
// Start to build intents between BGP speaker1 and BGP peer3
bgpPathintentConstructor(
"192.168.20.101/32", "192.168.20.2/32", null, bgpPort,
s1Eth100, s2Eth1);
bgpPathintentConstructor(
"192.168.20.101/32", "192.168.20.2/32", bgpPort, null,
s1Eth100, s2Eth1);
bgpPathintentConstructor(
"192.168.20.2/32", "192.168.20.101/32", null, bgpPort,
s2Eth1, s1Eth100);
bgpPathintentConstructor(
"192.168.20.2/32", "192.168.20.101/32", bgpPort, null,
s2Eth1, s1Eth100);
//
// Start to build intents between BGP speaker2 and BGP peer1
bgpPathintentConstructor(
"192.168.10.102/32", "192.168.10.1/32", null, bgpPort,
s1Eth100, s1Eth1);
bgpPathintentConstructor(
"192.168.10.102/32", "192.168.10.1/32", bgpPort, null,
s1Eth100, s1Eth1);
bgpPathintentConstructor(
"192.168.10.1/32", "192.168.10.102/32", null, bgpPort,
s1Eth1, s1Eth100);
bgpPathintentConstructor(
"192.168.10.1/32", "192.168.10.102/32", bgpPort, null,
s1Eth1, s1Eth100);
// Start to build intents between BGP speaker2 and BGP peer2
bgpPathintentConstructor(
"192.168.20.102/32", "192.168.20.1/32", null, bgpPort,
s1Eth100, s2Eth1);
bgpPathintentConstructor(
"192.168.20.102/32", "192.168.20.1/32", bgpPort, null,
s1Eth100, s2Eth1);
bgpPathintentConstructor(
"192.168.20.1/32", "192.168.20.102/32", null, bgpPort,
s2Eth1, s1Eth100);
bgpPathintentConstructor(
"192.168.20.1/32", "192.168.20.102/32", bgpPort, null,
s2Eth1, s1Eth100);
// Start to build intents between BGP speaker2 and BGP peer3
bgpPathintentConstructor(
"192.168.20.102/32", "192.168.20.2/32", null, bgpPort,
s1Eth100, s2Eth1);
bgpPathintentConstructor(
"192.168.20.102/32", "192.168.20.2/32", bgpPort, null,
s1Eth100, s2Eth1);
bgpPathintentConstructor(
"192.168.20.2/32", "192.168.20.102/32", null, bgpPort,
s2Eth1, s1Eth100);
bgpPathintentConstructor(
"192.168.20.2/32", "192.168.20.102/32", bgpPort, null,
s2Eth1, s1Eth100);
//
// Start to build intents between BGP speaker3 and BGP peer1
bgpPathintentConstructor(
"192.168.10.103/32", "192.168.10.1/32", null, bgpPort,
s2Eth100, s1Eth1);
bgpPathintentConstructor(
"192.168.10.103/32", "192.168.10.1/32", bgpPort, null,
s2Eth100, s1Eth1);
bgpPathintentConstructor(
"192.168.10.1/32", "192.168.10.103/32", null, bgpPort,
s1Eth1, s2Eth100);
bgpPathintentConstructor(
"192.168.10.1/32", "192.168.10.103/32", bgpPort, null,
s1Eth1, s2Eth100);
// Start to build intents between BGP speaker3 and BGP peer2
bgpPathintentConstructor(
"192.168.20.103/32", "192.168.20.1/32", null, bgpPort,
s2Eth100, s2Eth1);
bgpPathintentConstructor(
"192.168.20.103/32", "192.168.20.1/32", bgpPort, null,
s2Eth100, s2Eth1);
bgpPathintentConstructor(
"192.168.20.1/32", "192.168.20.103/32", null, bgpPort,
s2Eth1, s2Eth100);
bgpPathintentConstructor(
"192.168.20.1/32", "192.168.20.103/32", bgpPort, null,
s2Eth1, s2Eth100);
// Start to build intents between BGP speaker3 and BGP peer3
bgpPathintentConstructor(
"192.168.20.103/32", "192.168.20.2/32", null, bgpPort,
s2Eth100, s2Eth1);
bgpPathintentConstructor(
"192.168.20.103/32", "192.168.20.2/32", bgpPort, null,
s2Eth100, s2Eth1);
bgpPathintentConstructor(
"192.168.20.2/32", "192.168.20.103/32", null, bgpPort,
s2Eth1, s2Eth100);
bgpPathintentConstructor(
"192.168.20.2/32", "192.168.20.103/32", bgpPort, null,
s2Eth1, s2Eth100);
}
/**
* Constructs a BGP intent and put it into the intentList.
* <p/>
* The purpose of this method is too simplify the setUpBgpIntents() method,
* and to make the setUpBgpIntents() easy to read.
*
* @param srcPrefix source IP prefix to match
* @param dstPrefix destination IP prefix to match
* @param srcConnectPoint source connect point for PointToPointIntent
* @param dstConnectPoint destination connect point for PointToPointIntent
*/
private void icmpPathintentConstructor(String srcPrefix, String dstPrefix,
ConnectPoint srcConnectPoint, ConnectPoint dstConnectPoint) {
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPProtocol(IPv4.PROTOCOL_ICMP)
.matchIPSrc(IpPrefix.valueOf(srcPrefix))
.matchIPDst(IpPrefix.valueOf(dstPrefix))
.build();
PointToPointIntent intent = new PointToPointIntent(
testIntentId, selector, noTreatment,
srcConnectPoint, dstConnectPoint);
intentList.add(intent);
}
/**
* Sets up intents for ICMP paths.
*/
private void setUpIcmpIntents() {
// Start to build intents between BGP speaker1 and BGP peer1
icmpPathintentConstructor(
"192.168.10.101/32", "192.168.10.1/32", s1Eth100, s1Eth1);
icmpPathintentConstructor(
"192.168.10.1/32", "192.168.10.101/32", s1Eth1, s1Eth100);
// Start to build intents between BGP speaker1 and BGP peer2
icmpPathintentConstructor(
"192.168.20.101/32", "192.168.20.1/32", s1Eth100, s2Eth1);
icmpPathintentConstructor(
"192.168.20.1/32", "192.168.20.101/32", s2Eth1, s1Eth100);
// Start to build intents between BGP speaker1 and BGP peer3
icmpPathintentConstructor(
"192.168.20.101/32", "192.168.20.2/32", s1Eth100, s2Eth1);
icmpPathintentConstructor(
"192.168.20.2/32", "192.168.20.101/32", s2Eth1, s1Eth100);
//
// Start to build intents between BGP speaker2 and BGP peer1
icmpPathintentConstructor(
"192.168.10.102/32", "192.168.10.1/32", s1Eth100, s1Eth1);
icmpPathintentConstructor(
"192.168.10.1/32", "192.168.10.102/32", s1Eth1, s1Eth100);
// Start to build intents between BGP speaker2 and BGP peer2
icmpPathintentConstructor(
"192.168.20.102/32", "192.168.20.1/32", s1Eth100, s2Eth1);
icmpPathintentConstructor(
"192.168.20.1/32", "192.168.20.102/32", s2Eth1, s1Eth100);
// Start to build intents between BGP speaker2 and BGP peer3
icmpPathintentConstructor(
"192.168.20.102/32", "192.168.20.2/32", s1Eth100, s2Eth1);
icmpPathintentConstructor(
"192.168.20.2/32", "192.168.20.102/32", s2Eth1, s1Eth100);
//
// Start to build intents between BGP speaker3 and BGP peer1
icmpPathintentConstructor(
"192.168.10.103/32", "192.168.10.1/32", s2Eth100, s1Eth1);
icmpPathintentConstructor(
"192.168.10.1/32", "192.168.10.103/32", s1Eth1, s2Eth100);
// Start to build intents between BGP speaker3 and BGP peer2
icmpPathintentConstructor(
"192.168.20.103/32", "192.168.20.1/32", s2Eth100, s2Eth1);
icmpPathintentConstructor(
"192.168.20.1/32", "192.168.20.103/32", s2Eth1, s2Eth100);
// Start to build intents between BGP speaker3 and BGP peer3
icmpPathintentConstructor(
"192.168.20.103/32", "192.168.20.2/32", s2Eth100, s2Eth1);
icmpPathintentConstructor(
"192.168.20.2/32", "192.168.20.103/32", s2Eth1, s2Eth100);
}
/**
* Initializes peer connectivity testing environment.
*/
private void initPeerConnectivity() {
configInfoService = createMock(SdnIpConfigService.class);
expect(configInfoService.getBgpPeers()).andReturn(peers).anyTimes();
expect(configInfoService.getBgpSpeakers()).andReturn(bgpSpeakers).anyTimes();
replay(configInfoService);
intentService = createMock(IntentService.class);
replay(intentService);
peerConnectivityManager = new PeerConnectivityManager(configInfoService,
interfaceService, intentService);
}
/*
* EasyMock matcher that matches {@link PointToPointIntent}s but
* ignores the {@link IntentId} when matching.
* <p/>
* The normal intent equals method tests that the intent IDs are equal,
* however in these tests we can't know what the intent IDs will be in
* advance, so we can't set up expected intents with the correct IDs. Thus,
* the solution is to use an EasyMock matcher that verifies that all the
* value properties of the provided intent match the expected values, but
* ignores the intent ID when testing equality.
*/
private static final class IdAgnosticPointToPointIntentMatcher implements
IArgumentMatcher {
private final PointToPointIntent intent;
private String providedIntentString;
/**
* Constructor taking the expected intent to match against.
*
* @param intent the expected intent
*/
public IdAgnosticPointToPointIntentMatcher(PointToPointIntent intent) {
this.intent = intent;
}
@Override
public void appendTo(StringBuffer strBuffer) {
strBuffer.append("PointToPointIntentMatcher unable to match: "
+ providedIntentString);
}
@Override
public boolean matches(Object object) {
if (!(object instanceof PointToPointIntent)) {
return false;
}
PointToPointIntent providedIntent = (PointToPointIntent) object;
providedIntentString = providedIntent.toString();
PointToPointIntent matchIntent =
new PointToPointIntent(providedIntent.id(),
intent.selector(), intent.treatment(),
intent.ingressPoint(), intent.egressPoint());
return matchIntent.equals(providedIntent);
}
}
/**
* Matcher method to set an expected intent to match against (ignoring the
* the intent ID).
*
* @param intent the expected intent
* @return something of type PointToPointIntent
*/
private static PointToPointIntent eqExceptId(
PointToPointIntent intent) {
reportMatcher(new IdAgnosticPointToPointIntentMatcher(intent));
return null;
}
/**
* Tests whether peer connectivity manager can set up correct BGP and
* ICMP intents according to specific configuration.
* <p/>
* Two tricky cases included in the configuration are: 2 peers on a same
* switch port, peer on the same switch with BGPd.
*/
@Test
public void testConnectionSetup() {
reset(intentService);
// Sets up the expected PointToPoint intents.
for (int i = 0; i < intentList.size(); i++) {
intentService.submit(eqExceptId(intentList.get(i)));
}
replay(intentService);
// Running the interface to be tested.
peerConnectivityManager.start();
verify(intentService);
}
/**
* Tests a corner case, when there are no interfaces in the configuration.
*/
@Test
public void testNullInterfaces() {
reset(interfaceService);
expect(interfaceService.getInterfaces()).andReturn(
Sets.<Interface>newHashSet()).anyTimes();
expect(interfaceService.getInterface(s2Eth1))
.andReturn(null).anyTimes();
expect(interfaceService.getInterface(s1Eth1))
.andReturn(null).anyTimes();
replay(interfaceService);
reset(configInfoService);
expect(configInfoService.getBgpPeers()).andReturn(peers).anyTimes();
expect(configInfoService.getBgpSpeakers()).andReturn(bgpSpeakers).anyTimes();
replay(configInfoService);
reset(intentService);
replay(intentService);
peerConnectivityManager.start();
verify(intentService);
}
/**
* Tests a corner case, when there are no BGP peers in the configuration.
*/
@Test
public void testNullBgpPeers() {
reset(interfaceService);
expect(interfaceService.getInterfaces()).andReturn(
Sets.newHashSet(interfaces.values())).anyTimes();
replay(interfaceService);
reset(configInfoService);
expect(configInfoService.getBgpPeers()).andReturn(
new HashMap<IpAddress, BgpPeer>()).anyTimes();
expect(configInfoService.getBgpSpeakers()).andReturn(
bgpSpeakers).anyTimes();
replay(configInfoService);
reset(intentService);
replay(intentService);
peerConnectivityManager.start();
verify(intentService);
}
/**
* Tests a corner case, when there is no BGP speakers in the configuration.
*/
@Test
public void testNullBgpSpeakers() {
reset(interfaceService);
expect(interfaceService.getInterfaces()).andReturn(
Sets.newHashSet(interfaces.values())).anyTimes();
replay(interfaceService);
reset(configInfoService);
expect(configInfoService.getBgpPeers()).andReturn(
peers).anyTimes();
expect(configInfoService.getBgpSpeakers()).andReturn(
null).anyTimes();
replay(configInfoService);
reset(intentService);
replay(intentService);
peerConnectivityManager.start();
verify(intentService);
}
/**
* Tests a corner case, when there is no Interface configured for one BGP
* peer.
*/
@Test
public void testNoPeerInterface() {
String peerSw100Eth1 = "192.168.200.1";
configuredPeers.put(IpAddress.valueOf(peerSw100Eth1),
new BgpPeer("00:00:00:00:00:00:01:00", 1, peerSw100Eth1));
testConnectionSetup();
}
/**
* Tests a corner case, when there is no Interface configured for one BGP
* speaker.
* TODO: we should add a configuration correctness checking module/method
* before testing this corner case.
*/
@Ignore
@Test
public void testNoSpeakerInterface() {
BgpSpeaker bgpSpeaker100 = new BgpSpeaker(
"bgpSpeaker100",
"00:00:00:00:00:00:01:00", 100,
"00:00:00:00:01:00");
List<InterfaceAddress> interfaceAddresses100 =
new LinkedList<InterfaceAddress>();
interfaceAddresses100.add(new InterfaceAddress(dpid1, 1, "192.168.10.201"));
interfaceAddresses100.add(new InterfaceAddress(dpid2, 1, "192.168.20.201"));
bgpSpeaker100.setInterfaceAddresses(interfaceAddresses100);
configuredBgpSpeakers.put(bgpSpeaker100.name(), bgpSpeaker100);
testConnectionSetup();
}
}
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.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
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.Before;
import org.junit.Test;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
/**
* Unit tests for the BgpRouteEntry class.
*/
public class BgpRouteEntryTest {
private BgpSession bgpSession;
private static final IpAddress BGP_SESSION_BGP_ID =
IpAddress.valueOf("10.0.0.1");
private static final IpAddress BGP_SESSION_IP_ADDRESS =
IpAddress.valueOf("20.0.0.1");
private BgpSession bgpSession2;
private static final IpAddress BGP_SESSION_BGP_ID2 =
IpAddress.valueOf("10.0.0.2");
private static final IpAddress BGP_SESSION_IP_ADDRESS2 =
IpAddress.valueOf("20.0.0.1");
private BgpSession bgpSession3;
private static final IpAddress BGP_SESSION_BGP_ID3 =
IpAddress.valueOf("10.0.0.1");
private static final IpAddress BGP_SESSION_IP_ADDRESS3 =
IpAddress.valueOf("20.0.0.2");
@Before
public void setUp() throws Exception {
// Mock objects for testing
bgpSession = createMock(BgpSession.class);
bgpSession2 = createMock(BgpSession.class);
bgpSession3 = createMock(BgpSession.class);
// Setup the BGP Sessions
expect(bgpSession.getRemoteBgpId())
.andReturn(BGP_SESSION_BGP_ID).anyTimes();
expect(bgpSession.getRemoteIp4Address())
.andReturn(BGP_SESSION_IP_ADDRESS).anyTimes();
//
expect(bgpSession2.getRemoteBgpId())
.andReturn(BGP_SESSION_BGP_ID2).anyTimes();
expect(bgpSession2.getRemoteIp4Address())
.andReturn(BGP_SESSION_IP_ADDRESS2).anyTimes();
//
expect(bgpSession3.getRemoteBgpId())
.andReturn(BGP_SESSION_BGP_ID3).anyTimes();
expect(bgpSession3.getRemoteIp4Address())
.andReturn(BGP_SESSION_IP_ADDRESS3).anyTimes();
replay(bgpSession);
replay(bgpSession2);
replay(bgpSession3);
}
/**
* Generates a BGP Route Entry.
*
* @return a generated BGP Route Entry
*/
private BgpRouteEntry generateBgpRouteEntry() {
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
byte origin = BgpConstants.Update.Origin.IGP;
// Setup the AS Path
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);
//
long localPref = 100;
long multiExitDisc = 20;
BgpRouteEntry bgpRouteEntry =
new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
localPref);
bgpRouteEntry.setMultiExitDisc(multiExitDisc);
return bgpRouteEntry;
}
/**
* Tests valid class constructor.
*/
@Test
public void testConstructor() {
BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
String expectedString =
"BgpRouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8, " +
"bgpId=10.0.0.1, origin=0, asPath=AsPath{pathSegments=" +
"[PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}, " +
"PathSegment{type=1, segmentAsNumbers=[4, 5, 6]}]}, " +
"localPref=100, multiExitDisc=20}";
assertThat(bgpRouteEntry.toString(), is(expectedString));
}
/**
* Tests invalid class constructor for null BGP Session.
*/
@Test(expected = NullPointerException.class)
public void testInvalidConstructorNullBgpSession() {
BgpSession bgpSessionNull = null;
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
byte origin = BgpConstants.Update.Origin.IGP;
// Setup the AS Path
ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
//
long localPref = 100;
new BgpRouteEntry(bgpSessionNull, prefix, nextHop, origin, asPath,
localPref);
}
/**
* Tests invalid class constructor for null AS Path.
*/
@Test(expected = NullPointerException.class)
public void testInvalidConstructorNullAsPath() {
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
byte origin = BgpConstants.Update.Origin.IGP;
BgpRouteEntry.AsPath asPath = null;
long localPref = 100;
new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
localPref);
}
/**
* Tests getting the fields of a BGP route entry.
*/
@Test
public void testGetFields() {
// Create the fields to compare against
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
byte origin = BgpConstants.Update.Origin.IGP;
// Setup the AS Path
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);
//
long localPref = 100;
long multiExitDisc = 20;
// Generate the entry to test
BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
assertThat(bgpRouteEntry.prefix(), is(prefix));
assertThat(bgpRouteEntry.nextHop(), is(nextHop));
assertThat(bgpRouteEntry.getBgpSession(), is(bgpSession));
assertThat(bgpRouteEntry.getOrigin(), is(origin));
assertThat(bgpRouteEntry.getAsPath(), is(asPath));
assertThat(bgpRouteEntry.getLocalPref(), is(localPref));
assertThat(bgpRouteEntry.getMultiExitDisc(), is(multiExitDisc));
}
/**
* Tests whether a BGP route entry is a local route.
*/
@Test
public void testIsLocalRoute() {
//
// Test non-local route
//
BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
assertThat(bgpRouteEntry.isLocalRoute(), is(false));
//
// Test local route with AS Path that begins with AS_SET
//
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
byte origin = BgpConstants.Update.Origin.IGP;
// Setup the AS Path
ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SET;
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_SEQUENCE;
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);
//
long localPref = 100;
long multiExitDisc = 20;
//
bgpRouteEntry =
new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
localPref);
bgpRouteEntry.setMultiExitDisc(multiExitDisc);
assertThat(bgpRouteEntry.isLocalRoute(), is(true));
//
// Test local route with empty AS Path
//
pathSegments = new ArrayList<>();
asPath = new BgpRouteEntry.AsPath(pathSegments);
bgpRouteEntry =
new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
localPref);
bgpRouteEntry.setMultiExitDisc(multiExitDisc);
assertThat(bgpRouteEntry.isLocalRoute(), is(true));
}
/**
* Tests getting the BGP Neighbor AS number for a route.
*/
@Test
public void testGetNeighborAs() {
//
// Get neighbor AS for non-local route
//
BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
assertThat(bgpRouteEntry.getNeighborAs(), is((long) 1));
//
// Get neighbor AS for a local route
//
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
byte origin = BgpConstants.Update.Origin.IGP;
// Setup the AS Path
ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
//
long localPref = 100;
long multiExitDisc = 20;
//
bgpRouteEntry =
new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
localPref);
bgpRouteEntry.setMultiExitDisc(multiExitDisc);
assertThat(bgpRouteEntry.getNeighborAs(), is(BgpConstants.BGP_AS_0));
}
/**
* Tests whether a BGP route entry has AS Path loop.
*/
@Test
public void testHasAsPathLoop() {
BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
// Test for loops: test each AS number in the interval [1, 6]
for (int i = 1; i <= 6; i++) {
assertThat(bgpRouteEntry.hasAsPathLoop(i), is(true));
}
// Test for non-loops
assertThat(bgpRouteEntry.hasAsPathLoop(500), is(false));
}
/**
* Tests the BGP Decision Process comparison of BGP routes.
*/
@Test
public void testBgpDecisionProcessComparison() {
BgpRouteEntry bgpRouteEntry1 = generateBgpRouteEntry();
BgpRouteEntry bgpRouteEntry2 = generateBgpRouteEntry();
//
// Compare two routes that are same
//
assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(true));
//
// Compare two routes with different LOCAL_PREF
//
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
byte origin = BgpConstants.Update.Origin.IGP;
// Setup the AS Path
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);
//
long localPref = 50; // Different
long multiExitDisc = 20;
bgpRouteEntry2 =
new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
localPref);
bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
//
assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false));
localPref = bgpRouteEntry1.getLocalPref(); // Restore
//
// Compare two routes with different AS_PATH length
//
ArrayList<BgpRouteEntry.PathSegment> pathSegments2 = new ArrayList<>();
pathSegments2.add(pathSegment1);
// Different AS Path
BgpRouteEntry.AsPath asPath2 = new BgpRouteEntry.AsPath(pathSegments2);
bgpRouteEntry2 =
new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath2,
localPref);
bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
//
assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(false));
assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(true));
//
// Compare two routes with different ORIGIN
//
origin = BgpConstants.Update.Origin.EGP; // Different
bgpRouteEntry2 =
new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
localPref);
bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
//
assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false));
origin = bgpRouteEntry1.getOrigin(); // Restore
//
// Compare two routes with different MULTI_EXIT_DISC
//
multiExitDisc = 10; // Different
bgpRouteEntry2 =
new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
localPref);
bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
//
assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false));
multiExitDisc = bgpRouteEntry1.getMultiExitDisc(); // Restore
//
// Compare two routes with different BGP ID
//
bgpRouteEntry2 =
new BgpRouteEntry(bgpSession2, prefix, nextHop, origin, asPath,
localPref);
bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
//
assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false));
//
// Compare two routes with different BGP address
//
bgpRouteEntry2 =
new BgpRouteEntry(bgpSession3, prefix, nextHop, origin, asPath,
localPref);
bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
//
assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false));
}
/**
* Tests equality of {@link BgpRouteEntry}.
*/
@Test
public void testEquality() {
BgpRouteEntry bgpRouteEntry1 = generateBgpRouteEntry();
BgpRouteEntry bgpRouteEntry2 = generateBgpRouteEntry();
assertThat(bgpRouteEntry1, is(bgpRouteEntry2));
}
/**
* Tests non-equality of {@link BgpRouteEntry}.
*/
@Test
public void testNonEquality() {
BgpRouteEntry bgpRouteEntry1 = generateBgpRouteEntry();
// Setup BGP Route 2
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
byte origin = BgpConstants.Update.Origin.IGP;
// Setup the AS Path
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);
//
long localPref = 500; // Different
long multiExitDisc = 20;
BgpRouteEntry bgpRouteEntry2 =
new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
localPref);
bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
assertThat(bgpRouteEntry1, is(not(bgpRouteEntry2)));
}
/**
* Tests object string representation.
*/
@Test
public void testToString() {
BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
String expectedString =
"BgpRouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8, " +
"bgpId=10.0.0.1, origin=0, asPath=AsPath{pathSegments=" +
"[PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}, " +
"PathSegment{type=1, segmentAsNumbers=[4, 5, 6]}]}, " +
"localPref=100, multiExitDisc=20}";
assertThat(bgpRouteEntry.toString(), is(expectedString));
}
}
package org.onlab.onos.sdnip.bgp;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertThat;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onlab.onos.sdnip.RouteListener;
import org.onlab.onos.sdnip.RouteUpdate;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.util.TestUtils;
import org.onlab.util.TestUtils.TestUtilsException;
import com.google.common.net.InetAddresses;
/**
* Unit tests for the BgpSessionManager class.
*/
public class BgpSessionManagerTest {
private static final IpAddress IP_LOOPBACK_ID =
IpAddress.valueOf("127.0.0.1");
private static final IpAddress BGP_PEER1_ID = IpAddress.valueOf("10.0.0.1");
private static final long DEFAULT_LOCAL_PREF = 10;
private static final long DEFAULT_MULTI_EXIT_DISC = 20;
// The BGP Session Manager to test
private BgpSessionManager bgpSessionManager;
// Remote Peer state
private ClientBootstrap peerBootstrap;
private TestBgpPeerChannelHandler peerChannelHandler =
new TestBgpPeerChannelHandler(BGP_PEER1_ID, DEFAULT_LOCAL_PREF);
private TestBgpPeerFrameDecoder peerFrameDecoder =
new TestBgpPeerFrameDecoder();
// The socket that the Remote Peer should connect to
private InetSocketAddress connectToSocket;
private final DummyRouteListener dummyRouteListener =
new DummyRouteListener();
/**
* Dummy implementation for the RouteListener interface.
*/
private class DummyRouteListener implements RouteListener {
@Override
public void update(RouteUpdate routeUpdate) {
// Nothing to do
}
}
@Before
public void setUp() throws Exception {
//
// Setup the BGP Session Manager to test, and start listening for BGP
// connections.
//
bgpSessionManager = new BgpSessionManager(dummyRouteListener);
// NOTE: We use port 0 to bind on any available port
bgpSessionManager.startUp(0);
// Get the port number the BGP Session Manager is listening on
Channel serverChannel = TestUtils.getField(bgpSessionManager,
"serverChannel");
SocketAddress socketAddress = serverChannel.getLocalAddress();
InetSocketAddress inetSocketAddress =
(InetSocketAddress) socketAddress;
//
// Setup the BGP Peer, i.e., the "remote" BGP router that will
// initiate the BGP connection, send BGP UPDATE messages, etc.
//
ChannelFactory channelFactory =
new NioClientSocketChannelFactory(Executors.newCachedThreadPool(),
Executors.newCachedThreadPool());
ChannelPipelineFactory pipelineFactory = new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() throws Exception {
// Setup the transmitting pipeline
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("TestBgpPeerFrameDecoder",
peerFrameDecoder);
pipeline.addLast("TestBgpPeerChannelHandler",
peerChannelHandler);
return pipeline;
}
};
peerBootstrap = new ClientBootstrap(channelFactory);
peerBootstrap.setOption("child.keepAlive", true);
peerBootstrap.setOption("child.tcpNoDelay", true);
peerBootstrap.setPipelineFactory(pipelineFactory);
InetAddress connectToAddress = InetAddresses.forString("127.0.0.1");
connectToSocket = new InetSocketAddress(connectToAddress,
inetSocketAddress.getPort());
}
@After
public void tearDown() throws Exception {
bgpSessionManager.shutDown();
bgpSessionManager = null;
}
/**
* Gets BGP RIB-IN routes by waiting until they are received.
* <p/>
* NOTE: We keep checking once a second the number of received routes,
* up to 5 seconds.
*
* @param bgpSession the BGP session that is expected to receive the
* routes
* @param expectedRoutes the expected number of routes
* @return the BGP RIB-IN routes as received within the expected
* time interval
*/
private Collection<BgpRouteEntry> waitForBgpRibIn(BgpSession bgpSession,
long expectedRoutes)
throws InterruptedException {
Collection<BgpRouteEntry> bgpRibIn = bgpSession.getBgpRibIn();
final int maxChecks = 5; // Max wait of 5 seconds
for (int i = 0; i < maxChecks; i++) {
if (bgpRibIn.size() == expectedRoutes) {
break;
}
Thread.sleep(1000);
bgpRibIn = bgpSession.getBgpRibIn();
}
return bgpRibIn;
}
/**
* Gets BGP merged routes by waiting until they are received.
* <p/>
* NOTE: We keep checking once a second the number of received routes,
* up to 5 seconds.
*
* @param expectedRoutes the expected number of routes
* @return the BGP Session Manager routes as received within the expected
* time interval
*/
private Collection<BgpRouteEntry> waitForBgpRoutes(long expectedRoutes)
throws InterruptedException {
Collection<BgpRouteEntry> bgpRoutes = bgpSessionManager.getBgpRoutes();
final int maxChecks = 5; // Max wait of 5 seconds
for (int i = 0; i < maxChecks; i++) {
if (bgpRoutes.size() == expectedRoutes) {
break;
}
Thread.sleep(1000);
bgpRoutes = bgpSessionManager.getBgpRoutes();
}
return bgpRoutes;
}
/**
* Tests that the BGP OPEN messages have been exchanged, followed by
* KEEPALIVE.
* <p>
* The BGP Peer opens the sessions and transmits OPEN Message, eventually
* followed by KEEPALIVE. The tested BGP listener should respond by
* OPEN Message, followed by KEEPALIVE.
*
* @throws TestUtilsException TestUtils error
*/
@Test
public void testExchangedBgpOpenMessages()
throws InterruptedException, TestUtilsException {
// Initiate the connection
peerBootstrap.connect(connectToSocket);
// Wait until the OPEN message is received
peerFrameDecoder.receivedOpenMessageLatch.await(2000,
TimeUnit.MILLISECONDS);
// Wait until the KEEPALIVE message is received
peerFrameDecoder.receivedKeepaliveMessageLatch.await(2000,
TimeUnit.MILLISECONDS);
//
// Test the fields from the BGP OPEN message:
// BGP version, AS number, BGP ID
//
assertThat(peerFrameDecoder.remoteBgpVersion,
is(BgpConstants.BGP_VERSION));
assertThat(peerFrameDecoder.remoteAs,
is(TestBgpPeerChannelHandler.PEER_AS));
assertThat(peerFrameDecoder.remoteBgpIdentifier, is(IP_LOOPBACK_ID));
//
// Test that a BgpSession instance has been created
//
assertThat(bgpSessionManager.getMyBgpId(), is(IP_LOOPBACK_ID));
assertThat(bgpSessionManager.getBgpSessions(), hasSize(1));
BgpSession bgpSession =
bgpSessionManager.getBgpSessions().iterator().next();
assertThat(bgpSession, notNullValue());
long sessionAs = TestUtils.getField(bgpSession, "localAs");
assertThat(sessionAs, is(TestBgpPeerChannelHandler.PEER_AS));
}
/**
* Tests that the BGP UPDATE messages have been received and processed.
*/
@Test
public void testProcessedBgpUpdateMessages() throws InterruptedException {
BgpSession bgpSession;
IpAddress nextHopRouter;
BgpRouteEntry bgpRouteEntry;
ChannelBuffer message;
Collection<BgpRouteEntry> bgpRibIn;
Collection<BgpRouteEntry> bgpRoutes;
// Initiate the connection
peerBootstrap.connect(connectToSocket);
// Wait until the OPEN message is received
peerFrameDecoder.receivedOpenMessageLatch.await(2000,
TimeUnit.MILLISECONDS);
// Wait until the KEEPALIVE message is received
peerFrameDecoder.receivedKeepaliveMessageLatch.await(2000,
TimeUnit.MILLISECONDS);
// Get the BGP Session handler
bgpSession = bgpSessionManager.getBgpSessions().iterator().next();
// Prepare routes to add/delete
nextHopRouter = IpAddress.valueOf("10.20.30.40");
Collection<IpPrefix> addedRoutes = new LinkedList<>();
Collection<IpPrefix> withdrawnRoutes = new LinkedList<>();
addedRoutes.add(IpPrefix.valueOf("0.0.0.0/0"));
addedRoutes.add(IpPrefix.valueOf("20.0.0.0/8"));
addedRoutes.add(IpPrefix.valueOf("30.0.0.0/16"));
addedRoutes.add(IpPrefix.valueOf("40.0.0.0/24"));
addedRoutes.add(IpPrefix.valueOf("50.0.0.0/32"));
withdrawnRoutes.add(IpPrefix.valueOf("60.0.0.0/8"));
withdrawnRoutes.add(IpPrefix.valueOf("70.0.0.0/16"));
withdrawnRoutes.add(IpPrefix.valueOf("80.0.0.0/24"));
withdrawnRoutes.add(IpPrefix.valueOf("90.0.0.0/32"));
// Write the routes
message = peerChannelHandler.prepareBgpUpdate(nextHopRouter,
addedRoutes,
withdrawnRoutes);
peerChannelHandler.savedCtx.getChannel().write(message);
// Check that the routes have been received, processed and stored
bgpRibIn = waitForBgpRibIn(bgpSession, 5);
assertThat(bgpRibIn, hasSize(5));
bgpRoutes = waitForBgpRoutes(5);
assertThat(bgpRoutes, hasSize(5));
// Setup the AS Path
ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
segmentAsNumbers1.add((long) 65010);
segmentAsNumbers1.add((long) 65020);
segmentAsNumbers1.add((long) 65030);
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) 65041);
segmentAsNumbers2.add((long) 65042);
segmentAsNumbers2.add((long) 65043);
BgpRouteEntry.PathSegment pathSegment2 =
new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
pathSegments.add(pathSegment2);
//
BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
//
bgpRouteEntry =
new BgpRouteEntry(bgpSession,
IpPrefix.valueOf("0.0.0.0/0"),
nextHopRouter,
(byte) BgpConstants.Update.Origin.IGP,
asPath,
DEFAULT_LOCAL_PREF);
bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
assertThat(bgpRibIn, hasItem(bgpRouteEntry));
//
bgpRouteEntry =
new BgpRouteEntry(bgpSession,
IpPrefix.valueOf("20.0.0.0/8"),
nextHopRouter,
(byte) BgpConstants.Update.Origin.IGP,
asPath,
DEFAULT_LOCAL_PREF);
bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
assertThat(bgpRibIn, hasItem(bgpRouteEntry));
//
bgpRouteEntry =
new BgpRouteEntry(bgpSession,
IpPrefix.valueOf("30.0.0.0/16"),
nextHopRouter,
(byte) BgpConstants.Update.Origin.IGP,
asPath,
DEFAULT_LOCAL_PREF);
bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
assertThat(bgpRibIn, hasItem(bgpRouteEntry));
//
bgpRouteEntry =
new BgpRouteEntry(bgpSession,
IpPrefix.valueOf("40.0.0.0/24"),
nextHopRouter,
(byte) BgpConstants.Update.Origin.IGP,
asPath,
DEFAULT_LOCAL_PREF);
bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
assertThat(bgpRibIn, hasItem(bgpRouteEntry));
//
bgpRouteEntry =
new BgpRouteEntry(bgpSession,
IpPrefix.valueOf("50.0.0.0/32"),
nextHopRouter,
(byte) BgpConstants.Update.Origin.IGP,
asPath,
DEFAULT_LOCAL_PREF);
bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
assertThat(bgpRibIn, hasItem(bgpRouteEntry));
// Delete some routes
addedRoutes = new LinkedList<>();
withdrawnRoutes = new LinkedList<>();
withdrawnRoutes.add(IpPrefix.valueOf("0.0.0.0/0"));
withdrawnRoutes.add(IpPrefix.valueOf("50.0.0.0/32"));
// Write the routes
message = peerChannelHandler.prepareBgpUpdate(nextHopRouter,
addedRoutes,
withdrawnRoutes);
peerChannelHandler.savedCtx.getChannel().write(message);
// Check that the routes have been received, processed and stored
bgpRibIn = waitForBgpRibIn(bgpSession, 3);
assertThat(bgpRibIn, hasSize(3));
bgpRoutes = waitForBgpRoutes(3);
assertThat(bgpRoutes, hasSize(3));
//
bgpRouteEntry =
new BgpRouteEntry(bgpSession,
IpPrefix.valueOf("20.0.0.0/8"),
nextHopRouter,
(byte) BgpConstants.Update.Origin.IGP,
asPath,
DEFAULT_LOCAL_PREF);
bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
assertThat(bgpRibIn, hasItem(bgpRouteEntry));
//
bgpRouteEntry =
new BgpRouteEntry(bgpSession,
IpPrefix.valueOf("30.0.0.0/16"),
nextHopRouter,
(byte) BgpConstants.Update.Origin.IGP,
asPath,
DEFAULT_LOCAL_PREF);
bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
assertThat(bgpRibIn, hasItem(bgpRouteEntry));
//
bgpRouteEntry =
new BgpRouteEntry(bgpSession,
IpPrefix.valueOf("40.0.0.0/24"),
nextHopRouter,
(byte) BgpConstants.Update.Origin.IGP,
asPath,
DEFAULT_LOCAL_PREF);
bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
assertThat(bgpRibIn, hasItem(bgpRouteEntry));
// Close the channel and test there are no routes
peerChannelHandler.closeChannel();
bgpRoutes = waitForBgpRoutes(0);
assertThat(bgpRoutes, hasSize(0));
}
}
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();
}
}
......@@ -94,7 +94,7 @@ public class FlowsListCommand extends AbstractShellCommand {
result.put("device", device.id().toString())
.put("flowCount", flows.size())
.put("flows", array);
.set("flows", array);
return result;
}
......
......@@ -90,11 +90,15 @@ public class IntentPushTestCommand extends AbstractShellCommand
service.submit(intent);
}
try {
latch.await(5, TimeUnit.SECONDS);
if (latch.await(10, TimeUnit.SECONDS)) {
printResults(count);
} else {
print("I FAIL MISERABLY -> %d", latch.getCount());
}
} catch (InterruptedException e) {
print(e.toString());
}
service.removeListener(this);
}
......@@ -140,6 +144,8 @@ public class IntentPushTestCommand extends AbstractShellCommand
} else {
log.warn("install event latch is null");
}
} else {
log.info("I FAIL -> {}", event);
}
}
}
......
package org.onlab.onos.net;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.packet.ChassisId;
import java.util.Objects;
......@@ -16,6 +17,7 @@ public class DefaultDevice extends AbstractElement implements Device {
private final String serialNumber;
private final String hwVersion;
private final String swVersion;
private final ChassisId chassisId;
// For serialization
private DefaultDevice() {
......@@ -24,6 +26,7 @@ public class DefaultDevice extends AbstractElement implements Device {
this.hwVersion = null;
this.swVersion = null;
this.serialNumber = null;
this.chassisId = null;
}
/**
......@@ -40,13 +43,15 @@ public class DefaultDevice extends AbstractElement implements Device {
*/
public DefaultDevice(ProviderId providerId, DeviceId id, Type type,
String manufacturer, String hwVersion, String swVersion,
String serialNumber, Annotations... annotations) {
String serialNumber, ChassisId chassisId,
Annotations... annotations) {
super(providerId, id, annotations);
this.type = type;
this.manufacturer = manufacturer;
this.hwVersion = hwVersion;
this.swVersion = swVersion;
this.serialNumber = serialNumber;
this.chassisId = chassisId;
}
@Override
......@@ -80,6 +85,11 @@ public class DefaultDevice extends AbstractElement implements Device {
}
@Override
public ChassisId chassisId() {
return chassisId;
}
@Override
public int hashCode() {
return Objects.hash(id, type, manufacturer, hwVersion, swVersion, serialNumber);
}
......
package org.onlab.onos.net;
import org.onlab.packet.ChassisId;
/**
* Representation of a network infrastructure device.
*/
......@@ -54,6 +56,13 @@ public interface Device extends Element {
*/
String serialNumber();
/**
* Returns the device chassis id.
*
* @return chassis id
*/
ChassisId chassisId();
// Device realizedBy(); ?
// ports are not provided directly, but rather via DeviceService.getPorts(Device device);
......
......@@ -2,6 +2,7 @@ package org.onlab.onos.net.device;
import org.onlab.onos.net.AbstractDescription;
import org.onlab.onos.net.SparseAnnotations;
import org.onlab.packet.ChassisId;
import java.net.URI;
......@@ -20,6 +21,7 @@ public class DefaultDeviceDescription extends AbstractDescription
private final String hwVersion;
private final String swVersion;
private final String serialNumber;
private final ChassisId chassisId;
/**
* Creates a device description using the supplied information.
......@@ -34,7 +36,7 @@ public class DefaultDeviceDescription extends AbstractDescription
*/
public DefaultDeviceDescription(URI uri, Type type, String manufacturer,
String hwVersion, String swVersion,
String serialNumber,
String serialNumber, ChassisId chassis,
SparseAnnotations... annotations) {
super(annotations);
this.uri = checkNotNull(uri, "Device URI cannot be null");
......@@ -43,6 +45,7 @@ public class DefaultDeviceDescription extends AbstractDescription
this.hwVersion = hwVersion;
this.swVersion = swVersion;
this.serialNumber = serialNumber;
this.chassisId = chassis;
}
/**
......@@ -54,7 +57,7 @@ public class DefaultDeviceDescription extends AbstractDescription
SparseAnnotations... annotations) {
this(base.deviceURI(), base.type(), base.manufacturer(),
base.hwVersion(), base.swVersion(), base.serialNumber(),
annotations);
base.chassisId(), annotations);
}
@Override
......@@ -88,6 +91,11 @@ public class DefaultDeviceDescription extends AbstractDescription
}
@Override
public ChassisId chassisId() {
return chassisId;
}
@Override
public String toString() {
return toStringHelper(this)
.add("uri", uri).add("type", type).add("mfr", manufacturer)
......@@ -104,5 +112,6 @@ public class DefaultDeviceDescription extends AbstractDescription
this.hwVersion = null;
this.swVersion = null;
this.serialNumber = null;
this.chassisId = null;
}
}
......
......@@ -2,6 +2,7 @@ package org.onlab.onos.net.device;
import org.onlab.onos.net.Description;
import org.onlab.onos.net.Device;
import org.onlab.packet.ChassisId;
import java.net.URI;
......@@ -54,4 +55,11 @@ public interface DeviceDescription extends Description {
*/
String serialNumber();
/**
* Returns a device chassis id.
*
* @return chassis id
*/
ChassisId chassisId();
}
......
......@@ -8,7 +8,7 @@ import org.slf4j.Logger;
public class DefaultFlowEntry extends DefaultFlowRule implements FlowEntry {
private final Logger log = getLogger(getClass());
private static final Logger log = getLogger(DefaultFlowEntry.class);
private long life;
private long packets;
......
......@@ -11,7 +11,7 @@ import org.slf4j.Logger;
public class DefaultFlowRule implements FlowRule {
private final Logger log = getLogger(getClass());
private static final Logger log = getLogger(DefaultFlowRule.class);
private final DeviceId deviceId;
private final int priority;
......
......@@ -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;
......
......@@ -62,6 +62,9 @@ public abstract class AbstractProviderRegistry<P extends Provider, S extends Pro
((AbstractProviderService) service).invalidate();
services.remove(provider.id());
providers.remove(provider.id());
if (!provider.id().isAncillary()) {
providersByScheme.remove(provider.id().scheme());
}
}
}
......
......@@ -7,6 +7,8 @@ import org.onlab.onos.net.Provided;
*/
public interface Topology extends Provided {
// FIXME: Following is not true right now. It is actually System.nanoTime(),
// which has no relation to epoch time, wall clock, etc.
/**
* Returns the time, specified in milliseconds since start of epoch,
* when the topology became active and made available.
......
......@@ -37,6 +37,15 @@ public interface ClusterCommunicationService {
boolean multicast(ClusterMessage message, Set<NodeId> nodeIds) throws IOException;
/**
* Sends a message synchronously.
* @param message message to send
* @param toNodeId recipient node identifier
* @return ClusterMessageResponse which is reply future.
* @throws IOException
*/
ClusterMessageResponse sendAndReceive(ClusterMessage message, NodeId toNodeId) throws IOException;
/**
* Adds a new subscriber for the specified message subject.
*
* @param subject message subject
......
package org.onlab.onos.store.cluster.messaging;
import java.io.IOException;
import org.onlab.onos.cluster.NodeId;
// TODO: Should payload type be ByteBuffer?
......@@ -49,4 +51,14 @@ public class ClusterMessage {
public byte[] payload() {
return payload;
}
/**
* Sends a response to the sender.
*
* @param data payload response.
* @throws IOException
*/
public void respond(byte[] data) throws IOException {
throw new IllegalStateException("One can only repond to message recived from others.");
}
}
......
package org.onlab.onos.store.cluster.messaging;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.onlab.onos.cluster.NodeId;
public interface ClusterMessageResponse {
public NodeId sender();
public byte[] get(long timeout, TimeUnit timeunit) throws TimeoutException;
public byte[] get(long timeout) throws InterruptedException;
}
......@@ -3,6 +3,7 @@ package org.onlab.onos.net;
import com.google.common.testing.EqualsTester;
import org.junit.Test;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.packet.ChassisId;
import static org.junit.Assert.assertEquals;
import static org.onlab.onos.net.Device.Type.SWITCH;
......@@ -21,14 +22,15 @@ public class DefaultDeviceTest {
static final String SW = "3.9.1";
static final String SN1 = "43311-12345";
static final String SN2 = "42346-43512";
static final ChassisId CID = new ChassisId();
@Test
public void testEquality() {
Device d1 = new DefaultDevice(PID, DID1, SWITCH, MFR, HW, SW, SN1);
Device d2 = new DefaultDevice(PID, DID1, SWITCH, MFR, HW, SW, SN1);
Device d3 = new DefaultDevice(PID, DID2, SWITCH, MFR, HW, SW, SN2);
Device d4 = new DefaultDevice(PID, DID2, SWITCH, MFR, HW, SW, SN2);
Device d5 = new DefaultDevice(PID, DID2, SWITCH, MFR, HW, SW, SN1);
Device d1 = new DefaultDevice(PID, DID1, SWITCH, MFR, HW, SW, SN1, CID);
Device d2 = new DefaultDevice(PID, DID1, SWITCH, MFR, HW, SW, SN1, CID);
Device d3 = new DefaultDevice(PID, DID2, SWITCH, MFR, HW, SW, SN2, CID);
Device d4 = new DefaultDevice(PID, DID2, SWITCH, MFR, HW, SW, SN2, CID);
Device d5 = new DefaultDevice(PID, DID2, SWITCH, MFR, HW, SW, SN1, CID);
new EqualsTester().addEqualityGroup(d1, d2)
.addEqualityGroup(d3, d4)
......@@ -38,13 +40,13 @@ public class DefaultDeviceTest {
@Test
public void basics() {
Device device = new DefaultDevice(PID, DID1, SWITCH, MFR, HW, SW, SN1);
Device device = new DefaultDevice(PID, DID1, SWITCH, MFR, HW, SW, SN1, CID);
validate(device);
}
@Test
public void annotations() {
Device device = new DefaultDevice(PID, DID1, SWITCH, MFR, HW, SW, SN1,
Device device = new DefaultDevice(PID, DID1, SWITCH, MFR, HW, SW, SN1, CID,
DefaultAnnotations.builder().set("foo", "bar").build());
validate(device);
assertEquals("incorrect provider", "bar", device.annotations().value("foo"));
......
......@@ -3,6 +3,7 @@ package org.onlab.onos.net;
import com.google.common.testing.EqualsTester;
import org.junit.Test;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.packet.ChassisId;
import static org.junit.Assert.assertEquals;
import static org.onlab.onos.net.Device.Type.SWITCH;
......@@ -22,7 +23,8 @@ public class DefaultPortTest {
@Test
public void testEquality() {
Device device = new DefaultDevice(PID, DID1, SWITCH, "m", "h", "s", "n");
Device device = new DefaultDevice(PID, DID1, SWITCH, "m", "h", "s", "n",
new ChassisId());
Port p1 = new DefaultPort(device, portNumber(1), true);
Port p2 = new DefaultPort(device, portNumber(1), true);
Port p3 = new DefaultPort(device, portNumber(2), true);
......@@ -37,7 +39,8 @@ public class DefaultPortTest {
@Test
public void basics() {
Device device = new DefaultDevice(PID, DID1, SWITCH, "m", "h", "s", "n");
Device device = new DefaultDevice(PID, DID1, SWITCH, "m", "h", "s", "n",
new ChassisId());
Port port = new DefaultPort(device, portNumber(1), true);
assertEquals("incorrect element", device, port.element());
assertEquals("incorrect number", portNumber(1), port.number());
......
package org.onlab.onos.net;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.packet.ChassisId;
import org.onlab.packet.IpPrefix;
import java.util.ArrayList;
......@@ -37,7 +38,7 @@ public final class NetTestTools {
// Crates a new device with the specified id
public static Device device(String id) {
return new DefaultDevice(PID, did(id), Device.Type.SWITCH,
"mfg", "1.0", "1.1", "1234");
"mfg", "1.0", "1.1", "1234", new ChassisId());
}
// Crates a new host with the specified id
......@@ -47,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);
}
......
package org.onlab.onos.net.device;
import org.junit.Test;
import org.onlab.packet.ChassisId;
import java.net.URI;
......@@ -18,12 +19,13 @@ public class DefaultDeviceDescriptionTest {
private static final String HW = "1.1.x";
private static final String SW = "3.9.1";
private static final String SN = "43311-12345";
private static final ChassisId CID = new ChassisId();
@Test
public void basics() {
DeviceDescription device =
new DefaultDeviceDescription(DURI, SWITCH, MFR, HW, SW, SN);
new DefaultDeviceDescription(DURI, SWITCH, MFR, HW, SW, SN, CID);
assertEquals("incorrect uri", DURI, device.deviceURI());
assertEquals("incorrect type", SWITCH, device.type());
assertEquals("incorrect manufacturer", MFR, device.manufacturer());
......@@ -31,6 +33,7 @@ public class DefaultDeviceDescriptionTest {
assertEquals("incorrect sw", SW, device.swVersion());
assertEquals("incorrect serial", SN, device.serialNumber());
assertTrue("incorrect toString", device.toString().contains("uri=of:foo"));
assertTrue("Incorrect chassis", device.chassisId().value() == 0);
}
}
......
......@@ -11,6 +11,7 @@ import org.onlab.onos.net.Device;
import org.onlab.onos.net.Port;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.packet.ChassisId;
/**
* Tests of the device event.
......@@ -19,7 +20,7 @@ public class DeviceEventTest extends AbstractEventTest {
private Device createDevice() {
return new DefaultDevice(new ProviderId("of", "foo"), deviceId("of:foo"),
Device.Type.SWITCH, "box", "hw", "sw", "sn");
Device.Type.SWITCH, "box", "hw", "sw", "sn", new ChassisId());
}
@Override
......
......@@ -18,9 +18,9 @@ public class DefaultGraphDescriptionTest {
private static final DeviceId D3 = deviceId("3");
static final Device DEV1 = new DefaultDevice(PID, D1, SWITCH, "", "", "", "");
static final Device DEV2 = new DefaultDevice(PID, D2, SWITCH, "", "", "", "");
static final Device DEV3 = new DefaultDevice(PID, D3, SWITCH, "", "", "", "");
static final Device DEV1 = new DefaultDevice(PID, D1, SWITCH, "", "", "", "", null);
static final Device DEV2 = new DefaultDevice(PID, D2, SWITCH, "", "", "", "", null);
static final Device DEV3 = new DefaultDevice(PID, D3, SWITCH, "", "", "", "", null);
@Test
public void basics() {
......
<?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-core</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-json</artifactId>
<packaging>bundle</packaging>
<description>ONOS JSON encode/decode facilities</description>
<dependencies>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onos-api</artifactId>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onos-api</artifactId>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
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
......@@ -42,23 +42,6 @@
<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>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
......
......@@ -388,7 +388,7 @@ public class DeviceManager
new DefaultDeviceDescription(
did.uri(), device.type(), device.manufacturer(),
device.hwVersion(), device.swVersion(),
device.serialNumber()));
device.serialNumber(), device.chassisId()));
}
//TODO re-collect device information to fix potential staleness
applyRole(did, MastershipRole.MASTER);
......
......@@ -401,7 +401,7 @@ public class FlowRuleManager
CompletedBatchOperation completed;
for (Future<CompletedBatchOperation> future : futures) {
completed = future.get();
success = validateBatchOperation(failed, completed, future);
success = validateBatchOperation(failed, completed);
}
return finalizeBatchOperation(success, failed);
......@@ -426,14 +426,13 @@ public class FlowRuleManager
long now = System.nanoTime();
long thisTimeout = end - now;
completed = future.get(thisTimeout, TimeUnit.NANOSECONDS);
success = validateBatchOperation(failed, completed, future);
success = validateBatchOperation(failed, completed);
}
return finalizeBatchOperation(success, failed);
}
private boolean validateBatchOperation(List<FlowEntry> failed,
CompletedBatchOperation completed,
Future<CompletedBatchOperation> future) {
CompletedBatchOperation completed) {
if (isCancelled()) {
throw new CancellationException();
......
......@@ -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() {
......
......@@ -96,7 +96,7 @@ public class PathIntentInstaller implements IntentInstaller<PathIntent> {
FlowRule rule = new DefaultFlowRule(link.src().deviceId(),
builder.build(), treatment,
123, appId, 600);
123, appId, 15);
rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, rule));
prev = link.dst();
}
......
......@@ -43,7 +43,7 @@ public class PointToPointIntentCompiler
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostService hostService;
private IdGenerator<IntentId> intentIdGenerator;
protected IdGenerator<IntentId> intentIdGenerator;
@Activate
public void activate() {
......
......@@ -244,6 +244,7 @@ public class LinkManager
return;
}
log.info("Links for connection point {} vanished", connectPoint);
// FIXME: This will remove links registered by other providers
removeLinks(getLinks(connectPoint));
}
......
......@@ -167,6 +167,7 @@ public class ProxyArpManager implements ProxyArpService {
return;
}
// TODO find the correct IP address
Ethernet arpReply = buildArpReply(dst.ipAddresses().iterator().next(),
dst.mac(), eth);
// TODO: check send status with host service.
......
......@@ -23,6 +23,7 @@ import org.onlab.onos.net.topology.TopologyProviderRegistry;
import org.onlab.onos.net.topology.TopologyProviderService;
import org.slf4j.Logger;
import java.util.Collections;
import java.util.List;
import java.util.Timer;
import java.util.concurrent.ExecutorService;
......@@ -88,7 +89,7 @@ public class DefaultTopologyProvider extends AbstractProvider
linkService.addListener(linkListener);
isStarted = true;
triggerTopologyBuild(null);
triggerTopologyBuild(Collections.<Event>emptyList());
log.info("Started");
}
......
......@@ -37,6 +37,7 @@ import org.onlab.onos.net.device.PortDescription;
import org.onlab.onos.net.provider.AbstractProvider;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.trivial.impl.SimpleDeviceStore;
import org.onlab.packet.ChassisId;
import org.onlab.packet.IpPrefix;
import java.util.ArrayList;
......@@ -62,6 +63,7 @@ public class DeviceManagerTest {
private static final String SW1 = "3.8.1";
private static final String SW2 = "3.9.5";
private static final String SN = "43311-12345";
private static final ChassisId CID = new ChassisId();
private static final PortNumber P1 = PortNumber.portNumber(1);
private static final PortNumber P2 = PortNumber.portNumber(2);
......@@ -111,7 +113,7 @@ public class DeviceManagerTest {
private void connectDevice(DeviceId deviceId, String swVersion) {
DeviceDescription description =
new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
HW, swVersion, SN);
HW, swVersion, SN, CID);
providerService.deviceConnected(deviceId, description);
assertNotNull("device should be found", service.getDevice(DID1));
}
......
......@@ -8,7 +8,9 @@ import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_UPDATED;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
......@@ -54,6 +56,7 @@ import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.trivial.impl.SimpleFlowRuleStore;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
......@@ -68,7 +71,7 @@ public class FlowRuleManagerTest {
private static final DeviceId DID = DeviceId.deviceId("of:001");
private static final int TIMEOUT = 10;
private static final Device DEV = new DefaultDevice(
PID, DID, Type.SWITCH, "", "", "", "");
PID, DID, Type.SWITCH, "", "", "", "", null);
private FlowRuleManager mgr;
......@@ -166,16 +169,17 @@ public class FlowRuleManagerTest {
}
// TODO: If preserving iteration order is a requirement, redo FlowRuleStore.
//backing store is sensitive to the order of additions/removals
private boolean validateState(FlowEntryState... state) {
private boolean validateState(Map<FlowRule, FlowEntryState> expected) {
Map<FlowRule, FlowEntryState> expectedToCheck = new HashMap<>(expected);
Iterable<FlowEntry> rules = service.getFlowEntries(DID);
int i = 0;
for (FlowEntry f : rules) {
if (f.state() != state[i]) {
return false;
}
i++;
assertTrue("Unexpected FlowRule " + f, expectedToCheck.containsKey(f));
assertEquals("FlowEntry" + f, expectedToCheck.get(f), f.state());
expectedToCheck.remove(f);
}
assertEquals(Collections.emptySet(), expectedToCheck.entrySet());
return true;
}
......@@ -191,8 +195,10 @@ public class FlowRuleManagerTest {
mgr.applyFlowRules(r1, r2, r3);
assertEquals("3 rules should exist", 3, flowCount());
assertTrue("Entries should be pending add.",
validateState(FlowEntryState.PENDING_ADD, FlowEntryState.PENDING_ADD,
FlowEntryState.PENDING_ADD));
validateState(ImmutableMap.of(
r1, FlowEntryState.PENDING_ADD,
r2, FlowEntryState.PENDING_ADD,
r3, FlowEntryState.PENDING_ADD)));
}
@Test
......@@ -213,8 +219,10 @@ public class FlowRuleManagerTest {
validateEvents();
assertEquals("3 rule should exist", 3, flowCount());
assertTrue("Entries should be pending remove.",
validateState(FlowEntryState.PENDING_REMOVE, FlowEntryState.PENDING_REMOVE,
FlowEntryState.ADDED));
validateState(ImmutableMap.of(
f1, FlowEntryState.PENDING_REMOVE,
f2, FlowEntryState.PENDING_REMOVE,
f3, FlowEntryState.ADDED)));
mgr.removeFlowRules(f1);
assertEquals("3 rule should still exist", 3, flowCount());
......@@ -263,8 +271,10 @@ public class FlowRuleManagerTest {
providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2));
assertTrue("Entries should be added.",
validateState(FlowEntryState.ADDED, FlowEntryState.ADDED,
FlowEntryState.PENDING_ADD));
validateState(ImmutableMap.of(
f1, FlowEntryState.ADDED,
f2, FlowEntryState.ADDED,
f3, FlowEntryState.PENDING_ADD)));
validateEvents(RULE_ADDED, RULE_ADDED);
}
......@@ -336,7 +346,9 @@ public class FlowRuleManagerTest {
//only check that we are in pending remove. Events and actual remove state will
// be set by flowRemoved call.
validateState(FlowEntryState.PENDING_REMOVE, FlowEntryState.PENDING_REMOVE);
validateState(ImmutableMap.of(
f1, FlowEntryState.PENDING_REMOVE,
f2, FlowEntryState.PENDING_REMOVE));
}
@Test
......@@ -360,7 +372,9 @@ public class FlowRuleManagerTest {
Lists.newArrayList(fbe1, fbe2));
Future<CompletedBatchOperation> future = mgr.applyBatch(fbo);
assertTrue("Entries in wrong state",
validateState(FlowEntryState.PENDING_REMOVE, FlowEntryState.PENDING_ADD));
validateState(ImmutableMap.of(
f1, FlowEntryState.PENDING_REMOVE,
f2, FlowEntryState.PENDING_ADD)));
CompletedBatchOperation completed = null;
try {
completed = future.get();
......@@ -381,9 +395,18 @@ public class FlowRuleManagerTest {
mgr.applyFlowRules(f1);
assertTrue("Entries in wrong state",
validateState(ImmutableMap.of(
f1, FlowEntryState.PENDING_ADD)));
FlowEntry fe1 = new DefaultFlowEntry(f1);
providerService.pushFlowMetrics(DID, Collections.<FlowEntry>singletonList(fe1));
assertTrue("Entries in wrong state",
validateState(ImmutableMap.of(
f1, FlowEntryState.ADDED)));
FlowRuleBatchEntry fbe1 = new FlowRuleBatchEntry(
FlowRuleBatchEntry.FlowRuleOperation.REMOVE, f1);
......@@ -403,9 +426,9 @@ public class FlowRuleManagerTest {
* state.
*/
assertTrue("Entries in wrong state",
validateState(FlowEntryState.PENDING_REMOVE,
FlowEntryState.PENDING_ADD));
validateState(ImmutableMap.of(
f2, FlowEntryState.PENDING_REMOVE,
f1, FlowEntryState.PENDING_ADD)));
}
......
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 testLinksDifferentEquals() {
links1.add(link1);
links1.add(link2);
links2.add(link3);
links2.add(link1);
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 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 testBaseDifferentEquals() {
links1.add(link1);
links1.add(link2);
assertThat(i1.equals(i2), is(true));
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;
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.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.connectPoint;
/**
* Unit tests for the HostToHostIntent class.
*/
public class TestPointToPointIntent {
private TrafficSelector selector = new IntentTestsMocks.MockSelector();
private TrafficTreatment treatment = new IntentTestsMocks.MockTreatment();
private ConnectPoint point1 = connectPoint("dev1", 1);
private ConnectPoint point2 = connectPoint("dev2", 1);
private PointToPointIntent makePointToPoint(long id,
ConnectPoint ingress,
ConnectPoint egress) {
return new PointToPointIntent(new IntentId(id),
selector,
treatment,
ingress,
egress);
}
/**
* Tests the equals() method where two PointToPointIntents have references
* to the same ingress and egress points. These should compare equal.
*/
@Test
public void testSameEquals() {
PointToPointIntent i1 = makePointToPoint(12, point1, point2);
PointToPointIntent i2 = makePointToPoint(12, point1, point2);
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() {
PointToPointIntent i1 = makePointToPoint(12, point1, point2);
PointToPointIntent i2 = makePointToPoint(12, point2, point1);
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() {
PointToPointIntent i1 = makePointToPoint(12, point1, point2);
PointToPointIntent i2 = makePointToPoint(11, point1, point2);
assertThat(i1, is(not(equalTo(i2))));
}
/**
* Tests that the hashCode() values for two equivalent HostToHostIntent
* objects are the same.
*/
@Test
public void testHashCodeEquals() {
PointToPointIntent i1 = makePointToPoint(12, point1, point2);
PointToPointIntent i2 = makePointToPoint(12, point1, point2);
assertThat(i1.hashCode(), is(equalTo(i2.hashCode())));
}
/**
* Tests that the hashCode() values for two distinct LinkCollectionIntent
* objects are different.
*/
@Test
public void testHashCodeDifferent() {
PointToPointIntent i1 = makePointToPoint(12, point1, point2);
PointToPointIntent i2 = makePointToPoint(22, point1, point2);
assertThat(i1.hashCode(), is(not(equalTo(i2.hashCode()))));
}
}
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));
}
}
}
package org.onlab.onos.net.intent.impl;
import java.util.List;
import org.hamcrest.Matchers;
import org.junit.Test;
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.PathIntent;
import org.onlab.onos.net.intent.PointToPointIntent;
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.intent.LinksHaveEntryWithSourceDestinationPairMatcher.linksHasPath;
/**
* Unit tests for the HostToHost intent compiler.
*/
public class TestPointToPointIntentCompiler {
private TrafficSelector selector = new IntentTestsMocks.MockSelector();
private TrafficTreatment treatment = new IntentTestsMocks.MockTreatment();
/**
* Creates a PointToPoint intent based on ingress and egress device Ids.
*
* @param ingressIdString string for id of ingress device
* @param egressIdString string for id of egress device
* @return PointToPointIntent for the two devices
*/
private PointToPointIntent makeIntent(String ingressIdString,
String egressIdString) {
return new PointToPointIntent(new IntentId(12),
selector,
treatment,
connectPoint(ingressIdString, 1),
connectPoint(egressIdString, 1));
}
/**
* Creates a compiler for HostToHost intents.
*
* @param hops string array describing the path hops to use when compiling
* @return HostToHost intent compiler
*/
private PointToPointIntentCompiler makeCompiler(String[] hops) {
PointToPointIntentCompiler compiler =
new PointToPointIntentCompiler();
compiler.pathService = new IntentTestsMocks.MockPathService(hops);
IdBlockAllocator idBlockAllocator = new DummyIdBlockAllocator();
compiler.intentIdGenerator =
new IdBlockAllocatorBasedIntentIdGenerator(idBlockAllocator);
return compiler;
}
/**
* Tests a pair of devices in an 8 hop path, forward direction.
*/
@Test
public void testForwardPathCompilation() {
PointToPointIntent intent = makeIntent("d1", "d8");
assertThat(intent, is(notNullValue()));
String[] hops = {"d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8"};
PointToPointIntentCompiler compiler = makeCompiler(hops);
assertThat(compiler, is(notNullValue()));
List<Intent> result = compiler.compile(intent);
assertThat(result, is(Matchers.notNullValue()));
assertThat(result, hasSize(1));
Intent forwardResultIntent = result.get(0);
assertThat(forwardResultIntent instanceof PathIntent, is(true));
if (forwardResultIntent instanceof PathIntent) {
PathIntent forwardPathIntent = (PathIntent) forwardResultIntent;
// 7 links for the hops, plus one default lnk on ingress and egress
assertThat(forwardPathIntent.path().links(), hasSize(hops.length + 1));
assertThat(forwardPathIntent.path().links(), linksHasPath("d1", "d2"));
assertThat(forwardPathIntent.path().links(), linksHasPath("d2", "d3"));
assertThat(forwardPathIntent.path().links(), linksHasPath("d3", "d4"));
assertThat(forwardPathIntent.path().links(), linksHasPath("d4", "d5"));
assertThat(forwardPathIntent.path().links(), linksHasPath("d5", "d6"));
assertThat(forwardPathIntent.path().links(), linksHasPath("d6", "d7"));
assertThat(forwardPathIntent.path().links(), linksHasPath("d7", "d8"));
}
}
/**
* Tests a pair of devices in an 8 hop path, forward direction.
*/
@Test
public void testReversePathCompilation() {
PointToPointIntent intent = makeIntent("d8", "d1");
assertThat(intent, is(notNullValue()));
String[] hops = {"d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8"};
PointToPointIntentCompiler compiler = makeCompiler(hops);
assertThat(compiler, is(notNullValue()));
List<Intent> result = compiler.compile(intent);
assertThat(result, is(Matchers.notNullValue()));
assertThat(result, hasSize(1));
Intent reverseResultIntent = result.get(0);
assertThat(reverseResultIntent instanceof PathIntent, is(true));
if (reverseResultIntent instanceof PathIntent) {
PathIntent reversePathIntent = (PathIntent) reverseResultIntent;
assertThat(reversePathIntent.path().links(), hasSize(hops.length + 1));
assertThat(reversePathIntent.path().links(), linksHasPath("d2", "d1"));
assertThat(reversePathIntent.path().links(), linksHasPath("d3", "d2"));
assertThat(reversePathIntent.path().links(), linksHasPath("d4", "d3"));
assertThat(reversePathIntent.path().links(), linksHasPath("d5", "d4"));
assertThat(reversePathIntent.path().links(), linksHasPath("d6", "d5"));
assertThat(reversePathIntent.path().links(), linksHasPath("d7", "d6"));
assertThat(reversePathIntent.path().links(), linksHasPath("d8", "d7"));
}
}
}
......@@ -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>
......@@ -67,15 +52,12 @@
<artifactId>easymock</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onos-api</artifactId>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
......
......@@ -4,6 +4,9 @@ import static com.google.common.base.Preconditions.checkArgument;
import java.io.IOException;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
......@@ -17,6 +20,7 @@ import org.onlab.onos.store.cluster.impl.ClusterMembershipEvent;
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.ClusterMessageResponse;
import org.onlab.onos.store.cluster.messaging.MessageSubject;
import org.onlab.onos.store.serializers.ClusterMessageSerializer;
import org.onlab.onos.store.serializers.KryoPoolUtil;
......@@ -28,6 +32,7 @@ import org.onlab.netty.Message;
import org.onlab.netty.MessageHandler;
import org.onlab.netty.MessagingService;
import org.onlab.netty.NettyMessagingService;
import org.onlab.netty.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -114,7 +119,23 @@ 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;
}
}
@Override
public ClusterMessageResponse sendAndReceive(ClusterMessage message, NodeId toNodeId) throws IOException {
ControllerNode node = clusterService.getNode(toNodeId);
checkArgument(node != null, "Unknown nodeId: %s", toNodeId);
Endpoint nodeEp = new Endpoint(node.ip().toString(), node.tcpPort());
try {
Response responseFuture =
messagingService.sendAndReceive(nodeEp, message.subject().value(), SERIALIZER.encode(message));
return new InternalClusterMessageResponse(toNodeId, responseFuture);
} catch (IOException e) {
log.error("Failed interaction with remote nodeId: " + toNodeId, e);
throw e;
}
}
......@@ -137,11 +158,52 @@ public class ClusterCommunicationManager
public void handle(Message message) {
try {
ClusterMessage clusterMessage = SERIALIZER.decode(message.payload());
handler.handle(clusterMessage);
handler.handle(new InternalClusterMessage(clusterMessage, message));
} catch (Exception e) {
log.error("Exception caught during ClusterMessageHandler", e);
throw e;
}
}
}
public static final class InternalClusterMessage extends ClusterMessage {
private final Message rawMessage;
public InternalClusterMessage(ClusterMessage clusterMessage, Message rawMessage) {
super(clusterMessage.sender(), clusterMessage.subject(), clusterMessage.payload());
this.rawMessage = rawMessage;
}
@Override
public void respond(byte[] response) throws IOException {
rawMessage.respond(response);
}
}
private static final class InternalClusterMessageResponse implements ClusterMessageResponse {
private final NodeId sender;
private final Response responseFuture;
public InternalClusterMessageResponse(NodeId sender, Response responseFuture) {
this.sender = sender;
this.responseFuture = responseFuture;
}
@Override
public NodeId sender() {
return sender;
}
@Override
public byte[] get(long timeout, TimeUnit timeunit)
throws TimeoutException {
return responseFuture.get(timeout, timeunit);
}
@Override
public byte[] get(long timeout) throws InterruptedException {
return responseFuture.get();
}
}
}
......
/**
* 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,9 +38,10 @@ 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;
import org.onlab.util.KryoPool;
import org.onlab.util.NewConcurrentHashMap;
import org.slf4j.Logger;
......@@ -746,6 +747,7 @@ public class GossipDeviceStore
String hwVersion = base.hwVersion();
String swVersion = base.swVersion();
String serialNumber = base.serialNumber();
ChassisId chassisId = base.chassisId();
DefaultAnnotations annotations = DefaultAnnotations.builder().build();
annotations = merge(annotations, base.annotations());
......@@ -763,7 +765,8 @@ public class GossipDeviceStore
}
return new DefaultDevice(primary, deviceId , type, manufacturer,
hwVersion, swVersion, serialNumber, annotations);
hwVersion, swVersion, serialNumber,
chassisId, annotations);
}
/**
......@@ -1137,7 +1140,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);
/**
* Adds the specified replica placement info change listener.
*
* @param listener the replica placement info change listener
*/
void addListener(ReplicaInfoEventListener listener);
/**
* Removes the specified replica placement info change listener.
*
* @param listener the replica placement info change listener
*/
void removeListener(ReplicaInfoEventListener listener);
}
......@@ -3,14 +3,20 @@ package org.onlab.onos.store.flow.impl;
import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
import static org.slf4j.LoggerFactory.getLogger;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
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.ApplicationId;
import org.onlab.onos.cluster.ClusterService;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.flow.DefaultFlowEntry;
import org.onlab.onos.net.flow.FlowEntry;
......@@ -21,6 +27,14 @@ import org.onlab.onos.net.flow.FlowRuleEvent.Type;
import org.onlab.onos.net.flow.FlowRuleStore;
import org.onlab.onos.net.flow.FlowRuleStoreDelegate;
import org.onlab.onos.store.AbstractStore;
import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
import org.onlab.onos.store.cluster.messaging.ClusterMessage;
import org.onlab.onos.store.cluster.messaging.ClusterMessageResponse;
import org.onlab.onos.store.flow.ReplicaInfo;
import org.onlab.onos.store.flow.ReplicaInfoService;
import org.onlab.onos.store.serializers.DistributedStoreSerializers;
import org.onlab.onos.store.serializers.KryoSerializer;
import org.onlab.util.KryoPool;
import org.slf4j.Logger;
import com.google.common.collect.ArrayListMultimap;
......@@ -28,9 +42,8 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
/**
* Manages inventory of flow rules using trivial in-memory implementation.
* Manages inventory of flow rules using a distributed state management protocol.
*/
//FIXME I LIE. I AIN'T DISTRIBUTED
@Component(immediate = true)
@Service
public class DistributedFlowRuleStore
......@@ -46,6 +59,28 @@ public class DistributedFlowRuleStore
private final Multimap<Short, FlowRule> flowEntriesById =
ArrayListMultimap.<Short, FlowRule>create();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private ReplicaInfoService replicaInfoManager;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private ClusterCommunicationService clusterCommunicator;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private ClusterService clusterService;
protected static final KryoSerializer SERIALIZER = new KryoSerializer() {
@Override
protected void setupKryoPool() {
serializerPool = KryoPool.newBuilder()
.register(DistributedStoreSerializers.COMMON)
.build()
.populate(1);
}
};
// TODO: make this configurable
private static final long FLOW_RULE_STORE_TIMEOUT_MILLIS = 1000;
@Activate
public void activate() {
log.info("Started");
......@@ -91,26 +126,92 @@ public class DistributedFlowRuleStore
}
@Override
public synchronized void storeFlowRule(FlowRule rule) {
FlowEntry f = new DefaultFlowEntry(rule);
DeviceId did = f.deviceId();
if (!flowEntries.containsEntry(did, f)) {
flowEntries.put(did, f);
flowEntriesById.put(rule.appId(), f);
public void storeFlowRule(FlowRule rule) {
ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(rule.deviceId());
if (replicaInfo.master().get().equals(clusterService.getLocalNode().id())) {
storeFlowEntryInternal(rule);
return;
}
ClusterMessage message = new ClusterMessage(
clusterService.getLocalNode().id(),
FlowStoreMessageSubjects.STORE_FLOW_RULE,
SERIALIZER.encode(rule));
try {
ClusterMessageResponse response = clusterCommunicator.sendAndReceive(message, replicaInfo.master().get());
response.get(FLOW_RULE_STORE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
} catch (IOException | TimeoutException e) {
// FIXME: throw a FlowStoreException
throw new RuntimeException(e);
}
}
private synchronized void storeFlowEntryInternal(FlowRule flowRule) {
FlowEntry flowEntry = new DefaultFlowEntry(flowRule);
DeviceId deviceId = flowRule.deviceId();
// write to local copy.
if (!flowEntries.containsEntry(deviceId, flowEntry)) {
flowEntries.put(deviceId, flowEntry);
flowEntriesById.put(flowRule.appId(), flowEntry);
}
// write to backup.
// TODO: write to a hazelcast map.
}
@Override
public synchronized void deleteFlowRule(FlowRule rule) {
FlowEntry entry = getFlowEntry(rule);
ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(rule.deviceId());
if (replicaInfo.master().get().equals(clusterService.getLocalNode().id())) {
deleteFlowRuleInternal(rule);
return;
}
ClusterMessage message = new ClusterMessage(
clusterService.getLocalNode().id(),
FlowStoreMessageSubjects.DELETE_FLOW_RULE,
SERIALIZER.encode(rule));
try {
ClusterMessageResponse response = clusterCommunicator.sendAndReceive(message, replicaInfo.master().get());
response.get(FLOW_RULE_STORE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
} catch (IOException | TimeoutException e) {
// FIXME: throw a FlowStoreException
throw new RuntimeException(e);
}
}
private synchronized void deleteFlowRuleInternal(FlowRule flowRule) {
FlowEntry entry = getFlowEntry(flowRule);
if (entry == null) {
return;
}
entry.setState(FlowEntryState.PENDING_REMOVE);
// TODO: also update backup.
}
@Override
public synchronized FlowRuleEvent addOrUpdateFlowRule(FlowEntry rule) {
public FlowRuleEvent addOrUpdateFlowRule(FlowEntry rule) {
ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(rule.deviceId());
if (replicaInfo.master().get().equals(clusterService.getLocalNode().id())) {
return addOrUpdateFlowRuleInternal(rule);
}
ClusterMessage message = new ClusterMessage(
clusterService.getLocalNode().id(),
FlowStoreMessageSubjects.ADD_OR_UPDATE_FLOW_RULE,
SERIALIZER.encode(rule));
try {
ClusterMessageResponse response = clusterCommunicator.sendAndReceive(message, replicaInfo.master().get());
return SERIALIZER.decode(response.get(FLOW_RULE_STORE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
} catch (IOException | TimeoutException e) {
// FIXME: throw a FlowStoreException
throw new RuntimeException(e);
}
}
private synchronized FlowRuleEvent addOrUpdateFlowRuleInternal(FlowEntry rule) {
DeviceId did = rule.deviceId();
// check if this new rule is an update to an existing entry
......@@ -128,15 +229,39 @@ public class DistributedFlowRuleStore
flowEntries.put(did, rule);
return null;
// TODO: also update backup.
}
@Override
public synchronized FlowRuleEvent removeFlowRule(FlowEntry rule) {
public FlowRuleEvent removeFlowRule(FlowEntry rule) {
ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(rule.deviceId());
if (replicaInfo.master().get().equals(clusterService.getLocalNode().id())) {
// bypass and handle it locally
return removeFlowRuleInternal(rule);
}
ClusterMessage message = new ClusterMessage(
clusterService.getLocalNode().id(),
FlowStoreMessageSubjects.REMOVE_FLOW_RULE,
SERIALIZER.encode(rule));
try {
ClusterMessageResponse response = clusterCommunicator.sendAndReceive(message, replicaInfo.master().get());
return SERIALIZER.decode(response.get(FLOW_RULE_STORE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
} catch (IOException | TimeoutException e) {
// FIXME: throw a FlowStoreException
throw new RuntimeException(e);
}
}
private synchronized FlowRuleEvent removeFlowRuleInternal(FlowEntry rule) {
// This is where one could mark a rule as removed and still keep it in the store.
if (flowEntries.remove(rule.deviceId(), rule)) {
return new FlowRuleEvent(RULE_REMOVED, rule);
} else {
return null;
}
// TODO: also update backup.
}
}
......
package org.onlab.onos.store.flow.impl;
import org.onlab.onos.store.cluster.messaging.MessageSubject;
/**
* MessageSubjects used by DistributedFlowRuleStore peer-peer communication.
*/
public final class FlowStoreMessageSubjects {
private FlowStoreMessageSubjects() {}
public static final MessageSubject STORE_FLOW_RULE = new MessageSubject("peer-forward-store-flow-rule");
public static final MessageSubject DELETE_FLOW_RULE = new MessageSubject("peer-forward-delete-flow-rule");
public static final MessageSubject ADD_OR_UPDATE_FLOW_RULE =
new MessageSubject("peer-forward-add-or-update-flow-rule");
public static final MessageSubject REMOVE_FLOW_RULE = new MessageSubject("peer-forward-remove-flow-rule");
}
package org.onlab.onos.store.flow.impl;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
import static org.onlab.onos.store.flow.ReplicaInfoEvent.Type.MASTER_CHANGED;
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());
}
@Override
public void addListener(ReplicaInfoEventListener listener) {
listenerRegistry.addListener(checkNotNull(listener));
}
@Override
public void removeListener(ReplicaInfoEventListener listener) {
listenerRegistry.removeListener(checkNotNull(listener));
}
final class InternalMastershipListener implements MastershipListener {
@Override
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;
......@@ -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.DistributedStoreSerializers;
import org.onlab.onos.store.serializers.KryoSerializer;
import org.onlab.packet.IpPrefix;
......@@ -534,7 +534,7 @@ public class GossipHostStore
try {
unicastMessage(peer, GossipHostStoreMessageSubjects.HOST_ANTI_ENTROPY_ADVERTISEMENT, ad);
} catch (IOException e) {
log.debug("Failed to send anti-entropy advertisement", e);
log.debug("Failed to send anti-entropy advertisement to {}", peer);
return;
}
} catch (Exception e) {
......
/**
* Implementation of host store using distributed p2p synchronization protocol.
* 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
......
......@@ -39,7 +39,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.DistributedStoreSerializers;
import org.onlab.onos.store.serializers.KryoSerializer;
import org.onlab.util.KryoPool;
......
......@@ -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;
......
/**
* Implementation of distributed topology store using p2p synchronization protocol.
*/
package org.onlab.onos.store.topology.impl;
......@@ -53,6 +53,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.packet.ChassisId;
import org.onlab.packet.IpPrefix;
import com.google.common.collect.Iterables;
......@@ -74,6 +75,7 @@ public class GossipDeviceStoreTest {
private static final String SW1 = "3.8.1";
private static final String SW2 = "3.9.5";
private static final String SN = "43311-12345";
private static final ChassisId CID = new ChassisId();
private static final PortNumber P1 = PortNumber.portNumber(1);
private static final PortNumber P2 = PortNumber.portNumber(2);
......@@ -158,7 +160,7 @@ public class GossipDeviceStoreTest {
SparseAnnotations... annotations) {
DeviceDescription description =
new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
HW, swVersion, SN, annotations);
HW, swVersion, SN, CID, annotations);
reset(clusterCommunicator);
try {
expect(clusterCommunicator.broadcast(anyObject(ClusterMessage.class)))
......@@ -175,7 +177,7 @@ public class GossipDeviceStoreTest {
SparseAnnotations... annotations) {
DeviceDescription description =
new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
HW, swVersion, SN, annotations);
HW, swVersion, SN, CID, annotations);
deviceStore.createOrUpdateDevice(PIDA, deviceId, description);
}
......@@ -315,7 +317,7 @@ public class GossipDeviceStoreTest {
public final void testCreateOrUpdateDevice() throws IOException {
DeviceDescription description =
new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
HW, SW1, SN);
HW, SW1, SN, CID);
Capture<ClusterMessage> bcast = new Capture<>();
resetCommunicatorExpectingSingleBroadcast(bcast);
......@@ -328,7 +330,7 @@ public class GossipDeviceStoreTest {
DeviceDescription description2 =
new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
HW, SW2, SN);
HW, SW2, SN, CID);
resetCommunicatorExpectingSingleBroadcast(bcast);
DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
assertEquals(DEVICE_UPDATED, event2.type());
......@@ -346,7 +348,7 @@ public class GossipDeviceStoreTest {
// add
DeviceDescription description =
new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
HW, SW1, SN, A2);
HW, SW1, SN, CID, A2);
Capture<ClusterMessage> bcast = new Capture<>();
resetCommunicatorExpectingSingleBroadcast(bcast);
......@@ -362,7 +364,7 @@ public class GossipDeviceStoreTest {
// update from primary
DeviceDescription description2 =
new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
HW, SW2, SN, A1);
HW, SW2, SN, CID, A1);
resetCommunicatorExpectingSingleBroadcast(bcast);
DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
......@@ -392,7 +394,7 @@ public class GossipDeviceStoreTest {
// But, Ancillary annotations will be in effect
DeviceDescription description3 =
new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
HW, SW1, SN, A2_2);
HW, SW1, SN, CID, A2_2);
resetCommunicatorExpectingSingleBroadcast(bcast);
DeviceEvent event3 = deviceStore.createOrUpdateDevice(PIDA, DID1, description3);
......@@ -775,7 +777,7 @@ public class GossipDeviceStoreTest {
DeviceDescription description =
new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
HW, SW1, SN);
HW, SW1, SN, CID);
deviceStore.setDelegate(checkAdd);
deviceStore.createOrUpdateDevice(PID, DID1, description);
assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS));
......@@ -783,7 +785,7 @@ public class GossipDeviceStoreTest {
DeviceDescription description2 =
new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
HW, SW2, SN);
HW, SW2, SN, CID);
deviceStore.unsetDelegate(checkAdd);
deviceStore.setDelegate(checkUpdate);
deviceStore.createOrUpdateDevice(PID, DID1, description2);
......
package org.onlab.onos.store.flow.impl;
import static com.google.common.base.Preconditions.checkState;
import static org.junit.Assert.*;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onlab.onos.cluster.NodeId;
import org.onlab.onos.event.AbstractListenerRegistry;
import org.onlab.onos.event.DefaultEventSinkRegistry;
import org.onlab.onos.event.Event;
import org.onlab.onos.event.EventDeliveryService;
import org.onlab.onos.event.EventSink;
import org.onlab.onos.mastership.MastershipEvent;
import org.onlab.onos.mastership.MastershipEvent.Type;
import org.onlab.onos.mastership.MastershipListener;
import org.onlab.onos.mastership.MastershipService;
import org.onlab.onos.mastership.MastershipServiceAdapter;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.store.flow.ReplicaInfo;
import org.onlab.onos.store.flow.ReplicaInfoEvent;
import org.onlab.onos.store.flow.ReplicaInfoEventListener;
import org.onlab.onos.store.flow.ReplicaInfoService;
import com.google.common.base.Optional;
import com.google.common.collect.Maps;
public class ReplicaInfoManagerTest {
private static final DeviceId DID1 = DeviceId.deviceId("of:1");
private static final DeviceId DID2 = DeviceId.deviceId("of:2");
private static final NodeId NID1 = new NodeId("foo");
private ReplicaInfoManager mgr;
private ReplicaInfoService service;
private AbstractListenerRegistry<MastershipEvent, MastershipListener>
mastershipListenerRegistry;
private TestEventDispatcher eventDispatcher;
@Before
public void setUp() throws Exception {
mastershipListenerRegistry = new AbstractListenerRegistry<>();
mgr = new ReplicaInfoManager();
service = mgr;
eventDispatcher = new TestEventDispatcher();
mgr.eventDispatcher = eventDispatcher;
mgr.mastershipService = new TestMastershipService();
// register dummy mastership event source
mgr.eventDispatcher.addSink(MastershipEvent.class, mastershipListenerRegistry);
mgr.activate();
}
@After
public void tearDown() throws Exception {
mgr.deactivate();
}
@Test
public void testGetReplicaInfoFor() {
ReplicaInfo info1 = service.getReplicaInfoFor(DID1);
assertEquals(Optional.of(NID1), info1.master());
// backups are always empty for now
assertEquals(Collections.emptyList(), info1.backups());
ReplicaInfo info2 = service.getReplicaInfoFor(DID2);
assertEquals("There's no master", Optional.absent(), info2.master());
// backups are always empty for now
assertEquals(Collections.emptyList(), info2.backups());
}
@Test
public void testReplicaInfoEvent() throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
service.addListener(new MasterNodeCheck(latch, DID1, NID1));
// fake MastershipEvent
eventDispatcher.post(new MastershipEvent(Type.MASTER_CHANGED, DID1, NID1));
assertTrue(latch.await(1, TimeUnit.SECONDS));
}
private final class MasterNodeCheck implements ReplicaInfoEventListener {
private final CountDownLatch latch;
private Optional<NodeId> expectedMaster;
private DeviceId expectedDevice;
MasterNodeCheck(CountDownLatch latch, DeviceId did,
NodeId nid) {
this.latch = latch;
this.expectedMaster = Optional.fromNullable(nid);
this.expectedDevice = did;
}
@Override
public void event(ReplicaInfoEvent event) {
assertEquals(expectedDevice, event.subject());
assertEquals(expectedMaster, event.replicaInfo().master());
// backups are always empty for now
assertEquals(Collections.emptyList(), event.replicaInfo().backups());
latch.countDown();
}
}
private final class TestMastershipService
extends MastershipServiceAdapter
implements MastershipService {
private Map<DeviceId, NodeId> masters;
TestMastershipService() {
masters = Maps.newHashMap();
masters.put(DID1, NID1);
// DID2 has no master
}
@Override
public NodeId getMasterFor(DeviceId deviceId) {
return masters.get(deviceId);
}
@Override
public void addListener(MastershipListener listener) {
mastershipListenerRegistry.addListener(listener);
}
@Override
public void removeListener(MastershipListener listener) {
mastershipListenerRegistry.removeListener(listener);
}
}
// code clone
/**
* Implements event delivery system that delivers events synchronously, or
* in-line with the post method invocation.
*/
private static class TestEventDispatcher extends DefaultEventSinkRegistry
implements EventDeliveryService {
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public void post(Event event) {
EventSink sink = getSink(event.getClass());
checkState(sink != null, "No sink for event %s", event);
sink.process(event);
}
}
}
package org.onlab.onos.store.common.impl;
package org.onlab.onos.store.impl;
import static org.junit.Assert.*;
......@@ -6,7 +6,6 @@ import java.nio.ByteBuffer;
import org.junit.Test;
import org.onlab.onos.store.Timestamp;
import org.onlab.onos.store.impl.MastershipBasedTimestamp;
import org.onlab.util.KryoPool;
import com.google.common.testing.EqualsTester;
......
......@@ -19,10 +19,6 @@
<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>
......@@ -38,23 +34,6 @@
<scope>test</scope>
<version>${project.version}</version>
</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>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
......
......@@ -19,34 +19,13 @@
<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.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
......
......@@ -21,30 +21,9 @@
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-misc</artifactId>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-junit</artifactId>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
......
<?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">
<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>
......@@ -23,20 +25,13 @@
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-misc</artifactId>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-junit</artifactId>
<artifactId>onos-api</artifactId>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
</dependency>
</dependencies>
......@@ -44,7 +39,7 @@
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<artifactId>maven-scr-plugin</artifactId>
</plugin>
</plugins>
</build>
......
......@@ -18,14 +18,6 @@
<dependencies>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onos-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
</dependency>
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
</dependency>
......@@ -36,13 +28,4 @@
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
......
......@@ -25,11 +25,13 @@ import org.onlab.onos.net.Port;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.device.DefaultDeviceDescription;
import org.onlab.onos.net.device.DefaultPortDescription;
import org.onlab.onos.net.flow.DefaultFlowRule;
import org.onlab.onos.net.host.DefaultHostDescription;
import org.onlab.onos.net.host.HostDescription;
import org.onlab.onos.net.link.DefaultLinkDescription;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.Timestamp;
import org.onlab.packet.ChassisId;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
......@@ -70,6 +72,7 @@ public final class KryoPoolUtil {
//
ControllerNode.State.class,
Device.Type.class,
ChassisId.class,
DefaultAnnotations.class,
DefaultControllerNode.class,
DefaultDevice.class,
......@@ -82,7 +85,8 @@ public final class KryoPoolUtil {
Timestamp.class,
HostId.class,
HostDescription.class,
DefaultHostDescription.class
DefaultHostDescription.class,
DefaultFlowRule.class
)
.register(URI.class, new URISerializer())
.register(NodeId.class, new NodeIdSerializer())
......
......@@ -25,6 +25,7 @@ import org.onlab.onos.net.LinkKey;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.SparseAnnotations;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.packet.ChassisId;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.util.KryoPool;
......@@ -48,7 +49,9 @@ public class KryoSerializerTest {
private static final String SW1 = "3.8.1";
private static final String SW2 = "3.9.5";
private static final String SN = "43311-12345";
private static final Device DEV1 = new DefaultDevice(PID, DID1, Device.Type.SWITCH, MFR, HW, SW1, SN);
private static final ChassisId CID = new ChassisId();
private static final Device DEV1 = new DefaultDevice(PID, DID1, Device.Type.SWITCH, MFR, HW,
SW1, SN, CID);
private static final SparseAnnotations A1 = DefaultAnnotations.builder()
.set("A1", "a1")
.set("B1", "b1")
......
......@@ -18,26 +18,9 @@
<dependencies>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onos-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
......
......@@ -5,8 +5,6 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.concurrent.ConcurrentException;
import org.apache.commons.lang3.concurrent.ConcurrentInitializer;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
......@@ -30,11 +28,13 @@ import org.onlab.onos.net.device.DeviceStoreDelegate;
import org.onlab.onos.net.device.PortDescription;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.AbstractStore;
import org.onlab.packet.ChassisId;
import org.onlab.util.NewConcurrentHashMap;
import org.slf4j.Logger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
......@@ -71,8 +71,7 @@ public class SimpleDeviceStore
public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
// collection of Description given from various providers
private final ConcurrentMap<DeviceId,
ConcurrentMap<ProviderId, DeviceDescriptions>>
private final ConcurrentMap<DeviceId, Map<ProviderId, DeviceDescriptions>>
deviceDescs = Maps.newConcurrentMap();
// cache of Device and Ports generated by compositing descriptions from providers
......@@ -117,15 +116,16 @@ public class SimpleDeviceStore
DeviceId deviceId,
DeviceDescription deviceDescription) {
ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
= getDeviceDescriptions(deviceId);
Map<ProviderId, DeviceDescriptions> providerDescs
= getOrCreateDeviceDescriptions(deviceId);
synchronized (providerDescs) {
// locking per device
DeviceDescriptions descs
= createIfAbsentUnchecked(providerDescs, providerId,
new InitDeviceDescs(deviceDescription));
= getOrCreateProviderDeviceDescriptions(providerDescs,
providerId,
deviceDescription);
Device oldDevice = devices.get(deviceId);
// update description
......@@ -192,8 +192,8 @@ public class SimpleDeviceStore
@Override
public DeviceEvent markOffline(DeviceId deviceId) {
ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
= getDeviceDescriptions(deviceId);
Map<ProviderId, DeviceDescriptions> providerDescs
= getOrCreateDeviceDescriptions(deviceId);
// locking device
synchronized (providerDescs) {
......@@ -218,7 +218,7 @@ public class SimpleDeviceStore
Device device = devices.get(deviceId);
checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
List<DeviceEvent> events = new ArrayList<>();
......@@ -287,12 +287,12 @@ public class SimpleDeviceStore
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();
}
}
......@@ -306,10 +306,36 @@ public class SimpleDeviceStore
NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
}
private ConcurrentMap<ProviderId, DeviceDescriptions> getDeviceDescriptions(
private Map<ProviderId, DeviceDescriptions> getOrCreateDeviceDescriptions(
DeviceId deviceId) {
return createIfAbsentUnchecked(deviceDescs, deviceId,
NewConcurrentHashMap.<ProviderId, DeviceDescriptions>ifNeeded());
Map<ProviderId, DeviceDescriptions> r;
r = deviceDescs.get(deviceId);
if (r != null) {
return r;
}
r = new HashMap<>();
final Map<ProviderId, DeviceDescriptions> concurrentlyAdded;
concurrentlyAdded = deviceDescs.putIfAbsent(deviceId, r);
if (concurrentlyAdded != null) {
return concurrentlyAdded;
} else {
return r;
}
}
// Guarded by deviceDescs value (=Device lock)
private DeviceDescriptions getOrCreateProviderDeviceDescriptions(
Map<ProviderId, DeviceDescriptions> device,
ProviderId providerId, DeviceDescription deltaDesc) {
synchronized (device) {
DeviceDescriptions r = device.get(providerId);
if (r == null) {
r = new DeviceDescriptions(deltaDesc);
device.put(providerId, r);
}
return r;
}
}
@Override
......@@ -318,12 +344,12 @@ public class SimpleDeviceStore
Device device = devices.get(deviceId);
checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
synchronized (descsMap) {
DeviceDescriptions descs = descsMap.get(providerId);
// assuming all providers must to give DeviceDescription
// assuming all providers must give DeviceDescription first
checkArgument(descs != null,
"Device description for Device ID %s from Provider %s was not found",
deviceId, providerId);
......@@ -367,7 +393,7 @@ public class SimpleDeviceStore
@Override
public DeviceEvent removeDevice(DeviceId deviceId) {
ConcurrentMap<ProviderId, DeviceDescriptions> descs = getDeviceDescriptions(deviceId);
Map<ProviderId, DeviceDescriptions> descs = getOrCreateDeviceDescriptions(deviceId);
synchronized (descs) {
Device device = devices.remove(deviceId);
// should DEVICE_REMOVED carry removed ports?
......@@ -390,7 +416,7 @@ public class SimpleDeviceStore
* @return Device instance
*/
private Device composeDevice(DeviceId deviceId,
ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
Map<ProviderId, DeviceDescriptions> providerDescs) {
checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
......@@ -404,6 +430,7 @@ public class SimpleDeviceStore
String hwVersion = base.hwVersion();
String swVersion = base.swVersion();
String serialNumber = base.serialNumber();
ChassisId chassisId = base.chassisId();
DefaultAnnotations annotations = DefaultAnnotations.builder().build();
annotations = merge(annotations, base.annotations());
......@@ -421,7 +448,8 @@ public class SimpleDeviceStore
}
return new DefaultDevice(primary, deviceId , type, manufacturer,
hwVersion, swVersion, serialNumber, annotations);
hwVersion, swVersion, serialNumber,
chassisId, annotations);
}
/**
......@@ -429,14 +457,14 @@ public class SimpleDeviceStore
*
* @param device device the port is on
* @param number port number
* @param providerDescs Collection of Descriptions from multiple providers
* @param descsMap Collection of Descriptions from multiple providers
* @return Port instance
*/
private Port composePort(Device device, PortNumber number,
ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
Map<ProviderId, DeviceDescriptions> descsMap) {
ProviderId primary = pickPrimaryPID(providerDescs);
DeviceDescriptions primDescs = providerDescs.get(primary);
ProviderId primary = pickPrimaryPID(descsMap);
DeviceDescriptions primDescs = descsMap.get(primary);
// if no primary, assume not enabled
// TODO: revisit this default port enabled/disabled behavior
boolean isEnabled = false;
......@@ -448,7 +476,7 @@ public class SimpleDeviceStore
annotations = merge(annotations, portDesc.annotations());
}
for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
if (e.getKey().equals(primary)) {
continue;
}
......@@ -470,10 +498,9 @@ public class SimpleDeviceStore
/**
* @return primary ProviderID, or randomly chosen one if none exists
*/
private ProviderId pickPrimaryPID(
ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
private ProviderId pickPrimaryPID(Map<ProviderId, DeviceDescriptions> descsMap) {
ProviderId fallBackPrimary = null;
for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
if (!e.getKey().isAncillary()) {
return e.getKey();
} else if (fallBackPrimary == null) {
......@@ -484,21 +511,6 @@ public class SimpleDeviceStore
return fallBackPrimary;
}
public static final class InitDeviceDescs
implements ConcurrentInitializer<DeviceDescriptions> {
private final DeviceDescription deviceDesc;
public InitDeviceDescs(DeviceDescription deviceDesc) {
this.deviceDesc = checkNotNull(deviceDesc);
}
@Override
public DeviceDescriptions get() throws ConcurrentException {
return new DeviceDescriptions(deviceDesc);
}
}
/**
* Collection of Description of a Device and it's Ports given from a Provider.
*/
......
......@@ -2,9 +2,13 @@ package org.onlab.onos.store.trivial.impl;
import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
import static org.slf4j.LoggerFactory.getLogger;
import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
import static java.util.Collections.unmodifiableCollection;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
......@@ -15,18 +19,16 @@ import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.flow.DefaultFlowEntry;
import org.onlab.onos.net.flow.FlowEntry;
import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
import org.onlab.onos.net.flow.FlowId;
import org.onlab.onos.net.flow.FlowRule;
import org.onlab.onos.net.flow.FlowRuleEvent;
import org.onlab.onos.net.flow.FlowRuleEvent.Type;
import org.onlab.onos.net.flow.FlowRuleStore;
import org.onlab.onos.net.flow.FlowRuleStoreDelegate;
import org.onlab.onos.store.AbstractStore;
import org.onlab.util.NewConcurrentHashMap;
import org.slf4j.Logger;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
/**
* Manages inventory of flow rules using trivial in-memory implementation.
*/
......@@ -38,12 +40,11 @@ public class SimpleFlowRuleStore
private final Logger log = getLogger(getClass());
// store entries as a pile of rules, no info about device tables
private final Multimap<DeviceId, FlowEntry> flowEntries =
ArrayListMultimap.<DeviceId, FlowEntry>create();
private final Multimap<Short, FlowRule> flowEntriesById =
ArrayListMultimap.<Short, FlowRule>create();
// inner Map is Device flow table
// Assumption: FlowId cannot have synonyms
private final ConcurrentMap<DeviceId, ConcurrentMap<FlowId, FlowEntry>>
flowEntries = new ConcurrentHashMap<>();
@Activate
public void activate() {
......@@ -52,88 +53,130 @@ public class SimpleFlowRuleStore
@Deactivate
public void deactivate() {
flowEntries.clear();
log.info("Stopped");
}
@Override
public int getFlowRuleCount() {
return flowEntries.size();
int sum = 0;
for (ConcurrentMap<FlowId, FlowEntry> ft : flowEntries.values()) {
sum += ft.size();
}
return sum;
}
@Override
public synchronized FlowEntry getFlowEntry(FlowRule rule) {
for (FlowEntry f : flowEntries.get(rule.deviceId())) {
if (f.equals(rule)) {
return f;
private static NewConcurrentHashMap<FlowId, FlowEntry> lazyEmptyFlowTable() {
return NewConcurrentHashMap.<FlowId, FlowEntry>ifNeeded();
}
/**
* Returns the flow table for specified device.
*
* @param deviceId identifier of the device
* @return Map representing Flow Table of given device.
*/
private ConcurrentMap<FlowId, FlowEntry> getFlowTable(DeviceId deviceId) {
return createIfAbsentUnchecked(flowEntries,
deviceId, lazyEmptyFlowTable());
}
return null;
private FlowEntry getFlowEntry(DeviceId deviceId, FlowId flowId) {
return getFlowTable(deviceId).get(flowId);
}
@Override
public synchronized Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
Collection<FlowEntry> rules = flowEntries.get(deviceId);
if (rules == null) {
return Collections.emptyList();
public FlowEntry getFlowEntry(FlowRule rule) {
return getFlowEntry(rule.deviceId(), rule.id());
}
return ImmutableSet.copyOf(rules);
@Override
public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
return unmodifiableCollection(getFlowTable(deviceId).values());
}
@Override
public synchronized Iterable<FlowRule> getFlowRulesByAppId(ApplicationId appId) {
Collection<FlowRule> rules = flowEntriesById.get(appId.id());
if (rules == null) {
return Collections.emptyList();
public Iterable<FlowRule> getFlowRulesByAppId(ApplicationId appId) {
Set<FlowRule> rules = new HashSet<>();
for (DeviceId did : flowEntries.keySet()) {
ConcurrentMap<FlowId, FlowEntry> ft = getFlowTable(did);
for (FlowEntry fe : ft.values()) {
if (fe.appId() == appId.id()) {
rules.add(fe);
}
return ImmutableSet.copyOf(rules);
}
}
return rules;
}
@Override
public synchronized void storeFlowRule(FlowRule rule) {
public void storeFlowRule(FlowRule rule) {
final boolean added = storeFlowRuleInternal(rule);
}
private boolean storeFlowRuleInternal(FlowRule rule) {
FlowEntry f = new DefaultFlowEntry(rule);
DeviceId did = f.deviceId();
if (!flowEntries.containsEntry(did, f)) {
flowEntries.put(did, f);
flowEntriesById.put(rule.appId(), f);
final DeviceId did = f.deviceId();
final FlowId fid = f.id();
FlowEntry existing = getFlowTable(did).putIfAbsent(fid, f);
if (existing != null) {
// was already there? ignore
return false;
}
// new flow rule added
// TODO: notify through delegate about remote event?
return true;
}
@Override
public synchronized void deleteFlowRule(FlowRule rule) {
FlowEntry entry = getFlowEntry(rule);
public void deleteFlowRule(FlowRule rule) {
FlowEntry entry = getFlowEntry(rule.deviceId(), rule.id());
if (entry == null) {
//log.warn("Cannot find rule {}", rule);
log.warn("Cannot find rule {}", rule);
System.err.println("Cannot find rule " + rule);
return;
}
synchronized (entry) {
entry.setState(FlowEntryState.PENDING_REMOVE);
}
}
@Override
public synchronized FlowRuleEvent addOrUpdateFlowRule(FlowEntry rule) {
DeviceId did = rule.deviceId();
public FlowRuleEvent addOrUpdateFlowRule(FlowEntry rule) {
// check if this new rule is an update to an existing entry
FlowEntry stored = getFlowEntry(rule);
FlowEntry stored = getFlowEntry(rule.deviceId(), rule.id());
if (stored != null) {
synchronized (stored) {
stored.setBytes(rule.bytes());
stored.setLife(rule.life());
stored.setPackets(rule.packets());
if (stored.state() == FlowEntryState.PENDING_ADD) {
stored.setState(FlowEntryState.ADDED);
// TODO: Do we need to change `rule` state?
return new FlowRuleEvent(Type.RULE_ADDED, rule);
}
return new FlowRuleEvent(Type.RULE_UPDATED, rule);
}
}
// should not reach here
// storeFlowRule was expected to be called
log.error("FlowRule was not found in store {} to update", rule);
//flowEntries.put(did, rule);
return null;
}
@Override
public synchronized FlowRuleEvent removeFlowRule(FlowEntry rule) {
public FlowRuleEvent removeFlowRule(FlowEntry rule) {
// This is where one could mark a rule as removed and still keep it in the store.
if (flowEntries.remove(rule.deviceId(), rule)) {
final DeviceId did = rule.deviceId();
ConcurrentMap<FlowId, FlowEntry> ft = getFlowTable(did);
if (ft.remove(rule.id(), rule)) {
return new FlowRuleEvent(RULE_REMOVED, rule);
} else {
return null;
......
package org.onlab.onos.store.trivial.impl;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.SetMultimap;
import org.apache.commons.lang3.concurrent.ConcurrentUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
......@@ -20,7 +18,6 @@ import org.onlab.onos.net.Link;
import org.onlab.onos.net.SparseAnnotations;
import org.onlab.onos.net.Link.Type;
import org.onlab.onos.net.LinkKey;
import org.onlab.onos.net.Provided;
import org.onlab.onos.net.link.DefaultLinkDescription;
import org.onlab.onos.net.link.LinkDescription;
import org.onlab.onos.net.link.LinkEvent;
......@@ -28,11 +25,12 @@ import org.onlab.onos.net.link.LinkStore;
import org.onlab.onos.net.link.LinkStoreDelegate;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.AbstractStore;
import org.onlab.util.NewConcurrentHashMap;
import org.slf4j.Logger;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
......@@ -47,6 +45,7 @@ import static org.onlab.onos.net.link.LinkEvent.Type.*;
import static org.slf4j.LoggerFactory.getLogger;
import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
import static com.google.common.base.Predicates.notNull;
import static com.google.common.base.Verify.verifyNotNull;
/**
* Manages inventory of infrastructure links using trivial in-memory structures
......@@ -61,8 +60,7 @@ public class SimpleLinkStore
private final Logger log = getLogger(getClass());
// Link inventory
private final ConcurrentMap<LinkKey,
ConcurrentMap<ProviderId, LinkDescription>>
private final ConcurrentMap<LinkKey, Map<ProviderId, LinkDescription>>
linkDescs = new ConcurrentHashMap<>();
// Link instance cache
......@@ -151,7 +149,7 @@ public class SimpleLinkStore
LinkDescription linkDescription) {
LinkKey key = linkKey(linkDescription.src(), linkDescription.dst());
ConcurrentMap<ProviderId, LinkDescription> descs = getLinkDescriptions(key);
Map<ProviderId, LinkDescription> descs = getOrCreateLinkDescriptions(key);
synchronized (descs) {
final Link oldLink = links.get(key);
// update description
......@@ -166,7 +164,7 @@ public class SimpleLinkStore
// Guarded by linkDescs value (=locking each Link)
private LinkDescription createOrUpdateLinkDescription(
ConcurrentMap<ProviderId, LinkDescription> descs,
Map<ProviderId, LinkDescription> descs,
ProviderId providerId,
LinkDescription linkDescription) {
......@@ -227,7 +225,7 @@ public class SimpleLinkStore
@Override
public LinkEvent removeLink(ConnectPoint src, ConnectPoint dst) {
final LinkKey key = linkKey(src, dst);
ConcurrentMap<ProviderId, LinkDescription> descs = getLinkDescriptions(key);
Map<ProviderId, LinkDescription> descs = getOrCreateLinkDescriptions(key);
synchronized (descs) {
Link link = links.remove(key);
descs.clear();
......@@ -247,8 +245,8 @@ public class SimpleLinkStore
/**
* @return primary ProviderID, or randomly chosen one if none exists
*/
private ProviderId pickPrimaryPID(
ConcurrentMap<ProviderId, LinkDescription> providerDescs) {
// Guarded by linkDescs value (=locking each Link)
private ProviderId getBaseProviderId(Map<ProviderId, LinkDescription> providerDescs) {
ProviderId fallBackPrimary = null;
for (Entry<ProviderId, LinkDescription> e : providerDescs.entrySet()) {
......@@ -262,9 +260,10 @@ public class SimpleLinkStore
return fallBackPrimary;
}
private Link composeLink(ConcurrentMap<ProviderId, LinkDescription> descs) {
ProviderId primary = pickPrimaryPID(descs);
LinkDescription base = descs.get(primary);
// Guarded by linkDescs value (=locking each Link)
private Link composeLink(Map<ProviderId, LinkDescription> descs) {
ProviderId primary = getBaseProviderId(descs);
LinkDescription base = descs.get(verifyNotNull(primary));
ConnectPoint src = base.src();
ConnectPoint dst = base.dst();
......@@ -289,9 +288,20 @@ public class SimpleLinkStore
return new DefaultLink(primary , src, dst, type, annotations);
}
private ConcurrentMap<ProviderId, LinkDescription> getLinkDescriptions(LinkKey key) {
return ConcurrentUtils.createIfAbsentUnchecked(linkDescs, key,
NewConcurrentHashMap.<ProviderId, LinkDescription>ifNeeded());
private Map<ProviderId, LinkDescription> getOrCreateLinkDescriptions(LinkKey key) {
Map<ProviderId, LinkDescription> r;
r = linkDescs.get(key);
if (r != null) {
return r;
}
r = new HashMap<>();
final Map<ProviderId, LinkDescription> concurrentlyAdded;
concurrentlyAdded = linkDescs.putIfAbsent(key, r);
if (concurrentlyAdded == null) {
return r;
} else {
return concurrentlyAdded;
}
}
private final Function<LinkKey, Link> lookupLink = new LookupLink();
......@@ -302,20 +312,11 @@ public class SimpleLinkStore
private final class LookupLink implements Function<LinkKey, Link> {
@Override
public Link apply(LinkKey input) {
if (input == null) {
return null;
} else {
return links.get(input);
}
}
private static final Predicate<Provided> IS_PRIMARY = new IsPrimary();
private static final Predicate<Provided> isPrimary() {
return IS_PRIMARY;
}
private static final class IsPrimary implements Predicate<Provided> {
@Override
public boolean apply(Provided input) {
return !input.providerId().isAncillary();
}
}
}
......
......@@ -17,6 +17,7 @@ import org.onlab.onos.net.topology.GraphDescription;
import org.onlab.onos.net.topology.LinkWeight;
import org.onlab.onos.net.topology.TopologyCluster;
import org.onlab.onos.net.topology.TopologyEdge;
import org.onlab.packet.ChassisId;
import java.util.Set;
......@@ -119,7 +120,7 @@ public class DefaultTopologyTest {
// Crates a new device with the specified id
public static Device device(String id) {
return new DefaultDevice(PID, did(id), Device.Type.SWITCH,
"mfg", "1.0", "1.1", "1234");
"mfg", "1.0", "1.1", "1234", new ChassisId());
}
// Short-hand for producing a device id from a string
......
......@@ -40,6 +40,7 @@ import org.onlab.onos.net.provider.ProviderId;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import org.onlab.packet.ChassisId;
/**
* Test of the simple DeviceStore implementation.
......@@ -55,6 +56,7 @@ public class SimpleDeviceStoreTest {
private static final String SW1 = "3.8.1";
private static final String SW2 = "3.9.5";
private static final String SN = "43311-12345";
private static final ChassisId CID = new ChassisId();
private static final PortNumber P1 = PortNumber.portNumber(1);
private static final PortNumber P2 = PortNumber.portNumber(2);
......@@ -107,7 +109,7 @@ public class SimpleDeviceStoreTest {
SparseAnnotations... annotations) {
DeviceDescription description =
new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
HW, swVersion, SN, annotations);
HW, swVersion, SN, CID, annotations);
deviceStore.createOrUpdateDevice(PID, deviceId, description);
}
......@@ -115,7 +117,7 @@ public class SimpleDeviceStoreTest {
SparseAnnotations... annotations) {
DeviceDescription description =
new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
HW, swVersion, SN, annotations);
HW, swVersion, SN, CID, annotations);
deviceStore.createOrUpdateDevice(PIDA, deviceId, description);
}
......@@ -193,14 +195,14 @@ public class SimpleDeviceStoreTest {
public final void testCreateOrUpdateDevice() {
DeviceDescription description =
new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
HW, SW1, SN);
HW, SW1, SN, CID);
DeviceEvent event = deviceStore.createOrUpdateDevice(PID, DID1, description);
assertEquals(DEVICE_ADDED, event.type());
assertDevice(DID1, SW1, event.subject());
DeviceDescription description2 =
new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
HW, SW2, SN);
HW, SW2, SN, CID);
DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
assertEquals(DEVICE_UPDATED, event2.type());
assertDevice(DID1, SW2, event2.subject());
......@@ -212,7 +214,7 @@ public class SimpleDeviceStoreTest {
public final void testCreateOrUpdateDeviceAncillary() {
DeviceDescription description =
new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
HW, SW1, SN, A2);
HW, SW1, SN, CID, A2);
DeviceEvent event = deviceStore.createOrUpdateDevice(PIDA, DID1, description);
assertEquals(DEVICE_ADDED, event.type());
assertDevice(DID1, SW1, event.subject());
......@@ -222,7 +224,7 @@ public class SimpleDeviceStoreTest {
DeviceDescription description2 =
new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
HW, SW2, SN, A1);
HW, SW2, SN, CID, A1);
DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
assertEquals(DEVICE_UPDATED, event2.type());
assertDevice(DID1, SW2, event2.subject());
......@@ -238,7 +240,7 @@ public class SimpleDeviceStoreTest {
// But, Ancillary annotations will be in effect
DeviceDescription description3 =
new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
HW, SW1, SN, A2_2);
HW, SW1, SN, CID, A2_2);
DeviceEvent event3 = deviceStore.createOrUpdateDevice(PIDA, DID1, description3);
assertEquals(DEVICE_UPDATED, event3.type());
// basic information will be the one from Primary
......@@ -508,7 +510,7 @@ public class SimpleDeviceStoreTest {
DeviceDescription description =
new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
HW, SW1, SN);
HW, SW1, SN, CID);
deviceStore.setDelegate(checkAdd);
deviceStore.createOrUpdateDevice(PID, DID1, description);
assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS));
......@@ -516,7 +518,7 @@ public class SimpleDeviceStoreTest {
DeviceDescription description2 =
new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
HW, SW2, SN);
HW, SW2, SN, CID);
deviceStore.unsetDelegate(checkAdd);
deviceStore.setDelegate(checkUpdate);
deviceStore.createOrUpdateDevice(PID, DID1, description2);
......
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"
name="onos-1.0.0">
<repository>mvn:org.onlab.onos/onos-features/1.0.0-SNAPSHOT/xml/features</repository>
<repository>mvn:org.onlab.onos/onos-features/1.0.0-SNAPSHOT/xml/features
</repository>
<feature name="onos-thirdparty-base" version="1.0.0"
description="ONOS 3rd party dependencies">
......@@ -37,7 +38,8 @@
description="ONOS 3rd party dependencies">
<feature>war</feature>
<bundle>mvn:com.fasterxml.jackson.core/jackson-core/2.4.2</bundle>
<bundle>mvn:com.fasterxml.jackson.core/jackson-annotations/2.4.2</bundle>
<bundle>mvn:com.fasterxml.jackson.core/jackson-annotations/2.4.2
</bundle>
<bundle>mvn:com.fasterxml.jackson.core/jackson-databind/2.4.2</bundle>
<bundle>mvn:com.sun.jersey/jersey-core/1.18.1</bundle>
<bundle>mvn:com.sun.jersey/jersey-server/1.18.1</bundle>
......@@ -102,9 +104,9 @@
<bundle>mvn:org.onlab.onos/onos-of-api/1.0.0-SNAPSHOT</bundle>
<bundle>mvn:org.onlab.onos/onos-of-ctl/1.0.0-SNAPSHOT</bundle>
<bundle>mvn:org.onlab.onos/onos-lldp-provider/1.0.0-SNAPSHOT</bundle>
<bundle>mvn:org.onlab.onos/onos-host-provider/1.0.0-SNAPSHOT</bundle>
<bundle>mvn:org.onlab.onos/onos-of-provider-device/1.0.0-SNAPSHOT</bundle>
<bundle>mvn:org.onlab.onos/onos-of-provider-link/1.0.0-SNAPSHOT</bundle>
<bundle>mvn:org.onlab.onos/onos-of-provider-host/1.0.0-SNAPSHOT</bundle>
<bundle>mvn:org.onlab.onos/onos-of-provider-packet/1.0.0-SNAPSHOT</bundle>
<bundle>mvn:org.onlab.onos/onos-of-provider-flow/1.0.0-SNAPSHOT</bundle>
......@@ -154,10 +156,24 @@
<bundle>mvn:org.onlab.onos/onos-app-config/1.0.0-SNAPSHOT</bundle>
</feature>
<feature name="onos-app-optical" version="1.0.0"
description="ONOS optical network config">
<feature>onos-api</feature>
<bundle>mvn:org.onlab.onos/onos-app-optical/1.0.0-SNAPSHOT</bundle>
</feature>
<feature name="onos-app-sdnip" version="1.0.0"
description="SDN-IP peering application">
<feature>onos-api</feature>
<bundle>mvn:org.onlab.onos/onos-app-sdnip/1.0.0-SNAPSHOT</bundle>
</feature>
<feature name="onos-app-calendar" version="1.0.0"
description="REST interface for scheduling intents from an external calendar">
<feature>onos-api</feature>
<feature>onos-thirdparty-web</feature>
<bundle>mvn:org.onlab.onos/onos-app-calendar/1.0.0-SNAPSHOT</bundle>
</feature>
</features>
......
......@@ -431,7 +431,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>2.12.1</version>
<version>2.13</version>
<dependencies>
<dependency>
<groupId>org.onlab.tools</groupId>
......@@ -521,7 +521,7 @@
<group>
<title>Core Subsystems</title>
<packages>
org.onlab.onos.impl:org.onlab.onos.cluster.impl:org.onlab.onos.net.device.impl:org.onlab.onos.net.link.impl:org.onlab.onos.net.host.impl:org.onlab.onos.net.topology.impl:org.onlab.onos.net.packet.impl:org.onlab.onos.net.flow.impl:org.onlab.onos.store.trivial.*:org.onlab.onos.net.*.impl:org.onlab.onos.event.impl:org.onlab.onos.store.*:org.onlab.onos.net.intent.impl:org.onlab.onos.net.proxyarp.impl:org.onlab.onos.mastership.impl
org.onlab.onos.impl:org.onlab.onos.cluster.impl:org.onlab.onos.net.device.impl:org.onlab.onos.net.link.impl:org.onlab.onos.net.host.impl:org.onlab.onos.net.topology.impl:org.onlab.onos.net.packet.impl:org.onlab.onos.net.flow.impl:org.onlab.onos.store.trivial.*:org.onlab.onos.net.*.impl:org.onlab.onos.event.impl:org.onlab.onos.store.*:org.onlab.onos.net.intent.impl:org.onlab.onos.net.proxyarp.impl:org.onlab.onos.mastership.impl:org.onlab.onos.json:org.onlab.onos.json.*
</packages>
</group>
<group>
......@@ -546,10 +546,11 @@
<group>
<title>Sample Applications</title>
<packages>
org.onlab.onos.tvue:org.onlab.onos.fwd:org.onlab.onos.ifwd:org.onlab.onos.mobility:org.onlab.onos.proxyarp:org.onlab.onos.foo
org.onlab.onos.tvue:org.onlab.onos.fwd:org.onlab.onos.ifwd:org.onlab.onos.mobility:org.onlab.onos.proxyarp:org.onlab.onos.foo:org.onlab.onos.calendar
</packages>
</group>
</groups>
<excludePackageNames>org.onlab.thirdparty</excludePackageNames>
</configuration>
</plugin>
......
<?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-of-providers</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-of-provider-host</artifactId>
<packaging>bundle</packaging>
<description>ONOS OpenFlow protocol host provider</description>
</project>
<?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-providers</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-host-provider</artifactId>
<packaging>bundle</packaging>
<description>ONOS host tracking provider</description>
<dependencies>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onos-api</artifactId>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
</dependencies>
</project>
package org.onlab.onos.provider.host.impl;
import static org.slf4j.LoggerFactory.getLogger;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.Host;
import org.onlab.onos.net.HostId;
import org.onlab.onos.net.HostLocation;
import org.onlab.onos.net.host.DefaultHostDescription;
import org.onlab.onos.net.host.HostDescription;
import org.onlab.onos.net.host.HostProvider;
import org.onlab.onos.net.host.HostProviderRegistry;
import org.onlab.onos.net.host.HostProviderService;
import org.onlab.onos.net.packet.PacketContext;
import org.onlab.onos.net.packet.PacketProcessor;
import org.onlab.onos.net.packet.PacketService;
import org.onlab.onos.net.provider.AbstractProvider;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.net.topology.Topology;
import org.onlab.onos.net.topology.TopologyService;
import org.onlab.packet.ARP;
import org.onlab.packet.Ethernet;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.VlanId;
import org.slf4j.Logger;
/**
* Provider which uses an OpenFlow controller to detect network
* end-station hosts.
*/
@Component(immediate = true)
public class HostLocationProvider extends AbstractProvider implements HostProvider {
private final Logger log = getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostProviderRegistry providerRegistry;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PacketService pktService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected TopologyService topologyService;
private HostProviderService providerService;
private final InternalHostProvider processor = new InternalHostProvider();
/**
* Creates an OpenFlow host provider.
*/
public HostLocationProvider() {
super(new ProviderId("of", "org.onlab.onos.provider.host"));
}
@Activate
public void activate() {
providerService = providerRegistry.register(this);
pktService.addProcessor(processor, 1);
log.info("Started");
}
@Deactivate
public void deactivate() {
providerRegistry.unregister(this);
pktService.removeProcessor(processor);
providerService = null;
log.info("Stopped");
}
@Override
public void triggerProbe(Host host) {
log.info("Triggering probe on device {}", host);
}
private class InternalHostProvider implements PacketProcessor {
@Override
public void process(PacketContext context) {
if (context == null) {
return;
}
Ethernet eth = context.inPacket().parsed();
VlanId vlan = VlanId.vlanId(eth.getVlanID());
ConnectPoint heardOn = context.inPacket().receivedFrom();
// If this is not an edge port, bail out.
Topology topology = topologyService.currentTopology();
if (topologyService.isInfrastructure(topology, heardOn)) {
return;
}
HostLocation hloc = new HostLocation(heardOn, System.currentTimeMillis());
HostId hid = HostId.hostId(eth.getSourceMAC(), vlan);
// Potentially a new or moved host
if (eth.getEtherType() == Ethernet.TYPE_ARP) {
ARP arp = (ARP) eth.getPayload();
IpPrefix ip = IpPrefix.valueOf(arp.getSenderProtocolAddress(),
IpPrefix.MAX_INET_MASK);
HostDescription hdescr =
new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc, ip);
providerService.hostDetected(hid, hdescr);
} else if (eth.getEtherType() == Ethernet.TYPE_IPV4) {
//Do not learn new ip from ip packet.
HostDescription hdescr =
new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc);
providerService.hostDetected(hid, hdescr);
}
}
}
}
/**
* Provider that uses packet service as a means of host discovery and tracking.
*/
package org.onlab.onos.provider.host.impl;
package org.onlab.onos.provider.host.impl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.nio.ByteBuffer;
import java.util.Set;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.HostId;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.flow.TrafficTreatment;
import org.onlab.onos.net.host.HostDescription;
import org.onlab.onos.net.host.HostProvider;
import org.onlab.onos.net.host.HostProviderRegistry;
import org.onlab.onos.net.host.HostProviderService;
import org.onlab.onos.net.packet.DefaultInboundPacket;
import org.onlab.onos.net.packet.InboundPacket;
import org.onlab.onos.net.packet.OutboundPacket;
import org.onlab.onos.net.packet.PacketContext;
import org.onlab.onos.net.packet.PacketProcessor;
import org.onlab.onos.net.packet.PacketService;
import org.onlab.onos.net.provider.AbstractProviderService;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.net.topology.Topology;
import org.onlab.onos.net.topology.TopologyServiceAdapter;
import org.onlab.packet.ARP;
import org.onlab.packet.Ethernet;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
public class HostLocationProviderTest {
private static final Integer INPORT = 10;
private static final String DEV1 = "of:1";
private static final String DEV2 = "of:2";
private static final String DEV3 = "of:3";
private static final VlanId VLAN = VlanId.vlanId();
private static final MacAddress MAC = MacAddress.valueOf("00:00:11:00:00:01");
private static final MacAddress BCMAC = MacAddress.valueOf("ff:ff:ff:ff:ff:ff");
private static final byte[] IP = new byte[]{10, 0, 0, 1};
private final HostLocationProvider provider = new HostLocationProvider();
private final TestHostRegistry hostService = new TestHostRegistry();
private final TestTopologyService topoService = new TestTopologyService();
private final TestPacketService packetService = new TestPacketService();
private PacketProcessor testProcessor;
private TestHostProviderService providerService;
@Before
public void setUp() {
provider.providerRegistry = hostService;
provider.topologyService = topoService;
provider.pktService = packetService;
provider.activate();
}
@Test
public void basics() {
assertNotNull("registration expected", providerService);
assertEquals("incorrect provider", provider, providerService.provider());
}
@Test
public void events() {
// new host
testProcessor.process(new TestPacketContext(DEV1));
assertNotNull("new host expected", providerService.added);
assertNull("host motion unexpected", providerService.moved);
// the host moved to new switch
testProcessor.process(new TestPacketContext(DEV2));
assertNotNull("host motion expected", providerService.moved);
// the host was misheard on a spine
testProcessor.process(new TestPacketContext(DEV3));
assertNull("host misheard on spine switch", providerService.spine);
}
@After
public void tearDown() {
provider.deactivate();
provider.providerRegistry = null;
}
private class TestHostRegistry implements HostProviderRegistry {
@Override
public HostProviderService register(HostProvider provider) {
providerService = new TestHostProviderService(provider);
return providerService;
}
@Override
public void unregister(HostProvider provider) {
}
@Override
public Set<ProviderId> getProviders() {
return null;
}
}
private class TestHostProviderService
extends AbstractProviderService<HostProvider>
implements HostProviderService {
DeviceId added = null;
DeviceId moved = null;
DeviceId spine = null;
protected TestHostProviderService(HostProvider provider) {
super(provider);
}
@Override
public void hostDetected(HostId hostId, HostDescription hostDescription) {
DeviceId descr = hostDescription.location().deviceId();
if (added == null) {
added = descr;
} else if ((moved == null) && !descr.equals(added)) {
moved = descr;
} else {
spine = descr;
}
}
@Override
public void hostVanished(HostId hostId) {
}
}
private class TestPacketService implements PacketService {
@Override
public void addProcessor(PacketProcessor processor, int priority) {
testProcessor = processor;
}
@Override
public void removeProcessor(PacketProcessor processor) {
}
@Override
public void emit(OutboundPacket packet) {
}
}
private class TestTopologyService extends TopologyServiceAdapter {
@Override
public boolean isInfrastructure(Topology topology,
ConnectPoint connectPoint) {
//simulate DPID3 as an infrastructure switch
if ((connectPoint.deviceId()).equals(DeviceId.deviceId(DEV3))) {
return true;
}
return false;
}
}
private class TestPacketContext implements PacketContext {
private final String deviceId;
public TestPacketContext(String deviceId) {
this.deviceId = deviceId;
}
@Override
public long time() {
return 0;
}
@Override
public InboundPacket inPacket() {
ARP arp = new ARP();
arp.setSenderProtocolAddress(IP)
.setSenderHardwareAddress(MAC.toBytes())
.setTargetHardwareAddress(BCMAC.toBytes())
.setTargetProtocolAddress(IP);
Ethernet eth = new Ethernet();
eth.setEtherType(Ethernet.TYPE_ARP)
.setVlanID(VLAN.toShort())
.setSourceMACAddress(MAC.toBytes())
.setDestinationMACAddress(BCMAC.getAddress())
.setPayload(arp);
ConnectPoint receivedFrom = new ConnectPoint(DeviceId.deviceId(deviceId),
PortNumber.portNumber(INPORT));
return new DefaultInboundPacket(receivedFrom, eth,
ByteBuffer.wrap(eth.serialize()));
}
@Override
public OutboundPacket outPacket() {
return null;
}
@Override
public TrafficTreatment.Builder treatmentBuilder() {
return null;
}
@Override
public void send() {
}
@Override
public boolean block() {
return false;
}
@Override
public boolean isHandled() {
return false;
}
}
}
<?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-providers</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-lldp-provider</artifactId>
<packaging>bundle</packaging>
<description>ONOS LLDP Link Discovery</description>
</project>
package org.onlab.onos.provider.lldp.impl;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.onos.mastership.MastershipService;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.Device;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Port;
import org.onlab.onos.net.device.DeviceEvent;
import org.onlab.onos.net.device.DeviceListener;
import org.onlab.onos.net.device.DeviceService;
import org.onlab.onos.net.link.LinkProvider;
import org.onlab.onos.net.link.LinkProviderRegistry;
import org.onlab.onos.net.link.LinkProviderService;
import org.onlab.onos.net.packet.PacketContext;
import org.onlab.onos.net.packet.PacketProcessor;
import org.onlab.onos.net.packet.PacketService;
import org.onlab.onos.net.provider.AbstractProvider;
import org.onlab.onos.net.provider.ProviderId;
import org.slf4j.Logger;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Provider which uses an OpenFlow controller to detect network
* infrastructure links.
*/
@Component(immediate = true)
public class LLDPLinkProvider extends AbstractProvider implements LinkProvider {
private final Logger log = getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected LinkProviderRegistry providerRegistry;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PacketService packetSevice;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected MastershipService masterService;
private LinkProviderService providerService;
private final boolean useBDDP = true;
private final InternalLinkProvider listener = new InternalLinkProvider();
protected final Map<DeviceId, LinkDiscovery> discoverers = new ConcurrentHashMap<>();
/**
* Creates an OpenFlow link provider.
*/
public LLDPLinkProvider() {
super(new ProviderId("lldp", "org.onlab.onos.provider.lldp"));
}
@Activate
public void activate() {
providerService = providerRegistry.register(this);
deviceService.addListener(listener);
packetSevice.addProcessor(listener, 0);
log.info("Started");
}
@Deactivate
public void deactivate() {
for (LinkDiscovery ld : discoverers.values()) {
ld.stop();
}
providerRegistry.unregister(this);
deviceService.removeListener(listener);
packetSevice.removeProcessor(listener);
providerService = null;
log.info("Stopped");
}
private class InternalLinkProvider implements PacketProcessor, DeviceListener {
@Override
public void event(DeviceEvent event) {
LinkDiscovery ld = null;
Device device = event.subject();
Port port = event.port();
switch (event.type()) {
case DEVICE_ADDED:
discoverers.put(device.id(),
new LinkDiscovery(device, packetSevice, masterService,
providerService, useBDDP));
break;
case PORT_ADDED:
case PORT_UPDATED:
if (event.port().isEnabled()) {
ld = discoverers.get(device.id());
if (ld == null) {
return;
}
ld.addPort(port);
} else {
ConnectPoint point = new ConnectPoint(device.id(),
port.number());
providerService.linksVanished(point);
}
break;
case PORT_REMOVED:
ConnectPoint point = new ConnectPoint(device.id(),
port.number());
providerService.linksVanished(point);
break;
case DEVICE_REMOVED:
case DEVICE_SUSPENDED:
ld = discoverers.get(device.id());
if (ld == null) {
return;
}
ld.stop();
providerService.linksVanished(device.id());
break;
case DEVICE_AVAILABILITY_CHANGED:
ld = discoverers.get(device.id());
if (ld == null) {
return;
}
if (deviceService.isAvailable(device.id())) {
ld.start();
} else {
providerService.linksVanished(device.id());
ld.stop();
}
break;
case DEVICE_UPDATED:
case DEVICE_MASTERSHIP_CHANGED:
break;
default:
log.debug("Unknown event {}", event);
}
}
@Override
public void process(PacketContext context) {
if (context == null) {
return;
}
LinkDiscovery ld = discoverers.get(
context.inPacket().receivedFrom().deviceId());
if (ld == null) {
return;
}
if (ld.handleLLDP(context)) {
context.block();
}
}
}
}
/*******************************************************************************
* Copyright 2014 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.onlab.onos.provider.lldp.impl;
import static org.slf4j.LoggerFactory.getLogger;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.jboss.netty.util.Timeout;
import org.jboss.netty.util.TimerTask;
import org.onlab.onos.mastership.MastershipService;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.Device;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Link.Type;
import org.onlab.onos.net.MastershipRole;
import org.onlab.onos.net.Port;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.flow.DefaultTrafficTreatment;
import org.onlab.onos.net.link.DefaultLinkDescription;
import org.onlab.onos.net.link.LinkDescription;
import org.onlab.onos.net.link.LinkProviderService;
import org.onlab.onos.net.packet.DefaultOutboundPacket;
import org.onlab.onos.net.packet.OutboundPacket;
import org.onlab.onos.net.packet.PacketContext;
import org.onlab.onos.net.packet.PacketService;
import org.onlab.packet.Ethernet;
import org.onlab.packet.ONOSLLDP;
import org.onlab.util.Timer;
import org.slf4j.Logger;
/**
* Run discovery process from a physical switch. Ports are initially labeled as
* slow ports. When an LLDP is successfully received, label the remote port as
* fast. Every probeRate milliseconds, loop over all fast ports and send an
* LLDP, send an LLDP for a single slow port. Based on FlowVisor topology
* discovery implementation.
*
* TODO: add 'fast discovery' mode: drop LLDPs in destination switch but listen
* for flow_removed messages
*/
public class LinkDiscovery implements TimerTask {
private final Device device;
// send 1 probe every probeRate milliseconds
private final long probeRate;
private final Set<Long> slowPorts;
private final Set<Long> fastPorts;
// number of unacknowledged probes per port
private final Map<Long, AtomicInteger> portProbeCount;
// number of probes to send before link is removed
private static final short MAX_PROBE_COUNT = 3;
private final Logger log = getLogger(getClass());
private final ONOSLLDP lldpPacket;
private final Ethernet ethPacket;
private Ethernet bddpEth;
private final boolean useBDDP;
private final LinkProviderService linkProvider;
private final PacketService pktService;
private final MastershipService mastershipService;
private Timeout timeout;
/**
* Instantiates discovery manager for the given physical switch. Creates a
* generic LLDP packet that will be customized for the port it is sent out on.
* Starts the the timer for the discovery process.
* @param device the physical switch
* @param masterService
* @param useBDDP flag to also use BDDP for discovery
*/
public LinkDiscovery(Device device, PacketService pktService,
MastershipService masterService, LinkProviderService providerService, Boolean... useBDDP) {
this.device = device;
this.probeRate = 3000;
this.linkProvider = providerService;
this.pktService = pktService;
this.mastershipService = masterService;
this.slowPorts = Collections.synchronizedSet(new HashSet<Long>());
this.fastPorts = Collections.synchronizedSet(new HashSet<Long>());
this.portProbeCount = new HashMap<>();
this.lldpPacket = new ONOSLLDP();
this.lldpPacket.setChassisId(device.chassisId());
this.lldpPacket.setDevice(device.id().toString());
this.ethPacket = new Ethernet();
this.ethPacket.setEtherType(Ethernet.TYPE_LLDP);
this.ethPacket.setDestinationMACAddress(ONOSLLDP.LLDP_NICIRA);
this.ethPacket.setPayload(this.lldpPacket);
this.ethPacket.setPad(true);
this.useBDDP = useBDDP.length > 0 ? useBDDP[0] : false;
if (this.useBDDP) {
this.bddpEth = new Ethernet();
this.bddpEth.setPayload(this.lldpPacket);
this.bddpEth.setEtherType(Ethernet.TYPE_BSN);
this.bddpEth.setDestinationMACAddress(ONOSLLDP.BDDP_MULTICAST);
this.bddpEth.setPad(true);
log.info("Using BDDP to discover network");
}
start();
this.log.debug("Started discovery manager for switch {}",
device.id());
}
/**
* Add physical port port to discovery process.
* Send out initial LLDP and label it as slow port.
*
* @param port the port
*/
public void addPort(final Port port) {
this.log.debug("sending init probe to port {}",
port.number().toLong());
sendProbes(port.number().toLong());
synchronized (this) {
this.slowPorts.add(port.number().toLong());
}
}
/**
* Removes physical port from discovery process.
*
* @param port the port
*/
public void removePort(final Port port) {
// Ignore ports that are not on this switch
long portnum = port.number().toLong();
synchronized (this) {
if (this.slowPorts.contains(portnum)) {
this.slowPorts.remove(portnum);
} else if (this.fastPorts.contains(portnum)) {
this.fastPorts.remove(portnum);
this.portProbeCount.remove(portnum);
// no iterator to update
} else {
this.log.warn(
"tried to dynamically remove non-existing port {}",
portnum);
}
}
}
/**
* Method called by remote port to acknowledge receipt of LLDP sent by
* this port. If slow port, updates label to fast. If fast port, decrements
* number of unacknowledged probes.
*
* @param portNumber the port
*/
public void ackProbe(final Long portNumber) {
synchronized (this) {
if (this.slowPorts.contains(portNumber)) {
this.log.debug("Setting slow port to fast: {}:{}",
this.device.id(), portNumber);
this.slowPorts.remove(portNumber);
this.fastPorts.add(portNumber);
this.portProbeCount.put(portNumber, new AtomicInteger(0));
} else if (this.fastPorts.contains(portNumber)) {
this.portProbeCount.get(portNumber).set(0);
} else {
this.log.debug(
"Got ackProbe for non-existing port: {}",
portNumber);
}
}
}
/**
* Handles an incoming LLDP packet. Creates link in topology and sends ACK
* to port where LLDP originated.
*/
public boolean handleLLDP(PacketContext context) {
Ethernet eth = context.inPacket().parsed();
ONOSLLDP onoslldp = ONOSLLDP.parseONOSLLDP(eth);
if (onoslldp != null) {
final PortNumber dstPort =
context.inPacket().receivedFrom().port();
final PortNumber srcPort = PortNumber.portNumber(onoslldp.getPort());
final DeviceId srcDeviceId = DeviceId.deviceId(onoslldp.getDeviceString());
final DeviceId dstDeviceId = context.inPacket().receivedFrom().deviceId();
this.ackProbe(srcPort.toLong());
ConnectPoint src = new ConnectPoint(srcDeviceId, srcPort);
ConnectPoint dst = new ConnectPoint(dstDeviceId, dstPort);
LinkDescription ld;
if (eth.getEtherType() == Ethernet.TYPE_BSN) {
ld = new DefaultLinkDescription(src, dst, Type.INDIRECT);
} else {
ld = new DefaultLinkDescription(src, dst, Type.DIRECT);
}
linkProvider.linkDetected(ld);
return true;
}
return false;
}
/**
* Execute this method every t milliseconds. Loops over all ports
* labeled as fast and sends out an LLDP. Send out an LLDP on a single slow
* port.
*
* @param t timeout
* @throws Exception
*/
@Override
public void run(final Timeout t) {
this.log.debug("sending probes");
synchronized (this) {
final Iterator<Long> fastIterator = this.fastPorts.iterator();
Long portNumber;
Integer probeCount;
while (fastIterator.hasNext()) {
portNumber = fastIterator.next();
probeCount = this.portProbeCount.get(portNumber)
.getAndIncrement();
if (probeCount < LinkDiscovery.MAX_PROBE_COUNT) {
this.log.debug("sending fast probe to port");
sendProbes(portNumber);
} else {
// Update fast and slow ports
//fastIterator.remove();
//this.slowPorts.add(portNumber);
//this.portProbeCount.remove(portNumber);
this.portProbeCount.get(portNumber).set(0);
ConnectPoint cp = new ConnectPoint(
device.id(),
PortNumber.portNumber(portNumber));
log.debug("Link down -> {}", cp);
linkProvider.linksVanished(cp);
}
}
// send a probe for the next slow port
if (!this.slowPorts.isEmpty()) {
Iterator<Long> slowIterator = this.slowPorts.iterator();
while (slowIterator.hasNext()) {
portNumber = slowIterator.next();
this.log.debug("sending slow probe to port {}", portNumber);
sendProbes(portNumber);
}
}
}
// reschedule timer
timeout = Timer.getTimer().newTimeout(this, this.probeRate,
TimeUnit.MILLISECONDS);
}
public void stop() {
timeout.cancel();
}
public void start() {
timeout = Timer.getTimer().newTimeout(this, 0,
TimeUnit.MILLISECONDS);
}
/**
* Creates packet_out LLDP for specified output port.
*
* @param port the port
* @return Packet_out message with LLDP data
*/
private OutboundPacket createOutBoundLLDP(final Long port) {
if (port == null) {
return null;
}
this.lldpPacket.setPortId(port.intValue());
this.ethPacket.setSourceMACAddress("DE:AD:BE:EF:BA:11");
final byte[] lldp = this.ethPacket.serialize();
OutboundPacket outboundPacket = new DefaultOutboundPacket(
this.device.id(),
DefaultTrafficTreatment.builder().setOutput(
PortNumber.portNumber(port)).build(),
ByteBuffer.wrap(lldp));
return outboundPacket;
}
/**
* Creates packet_out BDDP for specified output port.
*
* @param port the port
* @return Packet_out message with LLDP data
*/
private OutboundPacket createOutBoundBDDP(final Long port) {
if (port == null) {
return null;
}
this.lldpPacket.setPortId(port.intValue());
this.bddpEth.setSourceMACAddress("DE:AD:BE:EF:BA:11");
final byte[] bddp = this.bddpEth.serialize();
OutboundPacket outboundPacket = new DefaultOutboundPacket(
this.device.id(),
DefaultTrafficTreatment.builder()
.setOutput(PortNumber.portNumber(port)).build(),
ByteBuffer.wrap(bddp));
return outboundPacket;
}
private void sendProbes(Long portNumber) {
if (mastershipService.getLocalRole(this.device.id()) ==
MastershipRole.MASTER) {
OutboundPacket pkt = this.createOutBoundLLDP(portNumber);
pktService.emit(pkt);
if (useBDDP) {
OutboundPacket bpkt = this.createOutBoundBDDP(portNumber);
pktService.emit(bpkt);
}
}
}
}
/**
* Provider that uses the core as a means of infrastructure link inference.
*/
package org.onlab.onos.provider.lldp.impl;
......@@ -23,6 +23,7 @@ import org.onlab.onos.openflow.controller.OpenFlowController;
import org.onlab.onos.openflow.controller.OpenFlowSwitch;
import org.onlab.onos.openflow.controller.OpenFlowSwitchListener;
import org.onlab.onos.openflow.controller.RoleState;
import org.onlab.packet.ChassisId;
import org.projectfloodlight.openflow.protocol.OFPortConfig;
import org.projectfloodlight.openflow.protocol.OFPortDesc;
import org.projectfloodlight.openflow.protocol.OFPortState;
......@@ -117,13 +118,14 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
}
DeviceId did = deviceId(uri(dpid));
OpenFlowSwitch sw = controller.getSwitch(dpid);
ChassisId cId = new ChassisId(dpid.value());
DeviceDescription description =
new DefaultDeviceDescription(did.uri(), Device.Type.SWITCH,
sw.manfacturerDescription(),
sw.hardwareDescription(),
sw.softwareDescription(),
sw.serialNumber());
sw.serialNumber(),
cId);
providerService.deviceConnected(did, description);
providerService.updatePorts(did, buildPortDescriptions(sw.getPorts()));
}
......@@ -170,7 +172,7 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
*/
private List<PortDescription> buildPortDescriptions(
List<OFPortDesc> ports) {
final List<PortDescription> portDescs = new ArrayList<>();
final List<PortDescription> portDescs = new ArrayList<>(ports.size());
for (OFPortDesc port : ports) {
portDescs.add(buildPortDescription(port));
}
......
......@@ -59,7 +59,7 @@ public class OpenFlowDeviceProviderTest {
private static final List<OFPortDesc> PLIST = Lists.newArrayList(PD1, PD2);
private static final Device DEV1 =
new DefaultDevice(PID, DID1, SWITCH, "", "", "", "");
new DefaultDevice(PID, DID1, SWITCH, "", "", "", "", null);
private static final TestOpenFlowSwitch SW1 = new TestOpenFlowSwitch();
......
......@@ -35,6 +35,7 @@ import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyAct
import org.projectfloodlight.openflow.protocol.match.Match;
import org.projectfloodlight.openflow.protocol.match.MatchField;
import org.projectfloodlight.openflow.types.IPv4Address;
import org.projectfloodlight.openflow.types.Masked;
import org.slf4j.Logger;
import com.google.common.collect.Lists;
......@@ -218,23 +219,35 @@ public class FlowEntryBuilder {
builder.matchEthType((short) ethType);
break;
case IPV4_DST:
IPv4Address di = match.get(MatchField.IPV4_DST);
IpPrefix dip;
if (di.isCidrMask()) {
dip = IpPrefix.valueOf(di.getInt(), di.asCidrMaskLength());
if (match.isPartiallyMasked(MatchField.IPV4_DST)) {
Masked<IPv4Address> maskedIp = match.getMasked(MatchField.IPV4_DST);
dip = IpPrefix.valueOf(
maskedIp.getValue().getInt(),
maskedIp.getMask().asCidrMaskLength());
} else {
dip = IpPrefix.valueOf(di.getInt());
dip = IpPrefix.valueOf(
match.get(MatchField.IPV4_DST).getInt(),
IpPrefix.MAX_INET_MASK);
}
builder.matchIPDst(dip);
break;
case IPV4_SRC:
IPv4Address si = match.get(MatchField.IPV4_SRC);
IpPrefix sip;
if (si.isCidrMask()) {
sip = IpPrefix.valueOf(si.getInt(), si.asCidrMaskLength());
if (match.isPartiallyMasked(MatchField.IPV4_SRC)) {
Masked<IPv4Address> maskedIp = match.getMasked(MatchField.IPV4_SRC);
sip = IpPrefix.valueOf(
maskedIp.getValue().getInt(),
maskedIp.getMask().asCidrMaskLength());
} else {
sip = IpPrefix.valueOf(si.getInt());
sip = IpPrefix.valueOf(
match.get(MatchField.IPV4_SRC).getInt(),
IpPrefix.MAX_INET_MASK);
}
builder.matchIPSrc(sip);
break;
case IP_PROTO:
......@@ -249,6 +262,12 @@ public class FlowEntryBuilder {
VlanId vlanId = VlanId.vlanId(match.get(MatchField.VLAN_VID).getVlan());
builder.matchVlanId(vlanId);
break;
case TCP_DST:
builder.matchTcpDst((short) match.get(MatchField.TCP_DST).getPort());
break;
case TCP_SRC:
builder.matchTcpSrc((short) match.get(MatchField.TCP_SRC).getPort());
break;
case ARP_OP:
case ARP_SHA:
case ARP_SPA:
......@@ -272,8 +291,6 @@ public class FlowEntryBuilder {
case MPLS_TC:
case SCTP_DST:
case SCTP_SRC:
case TCP_DST:
case TCP_SRC:
case TUNNEL_ID:
case UDP_DST:
case UDP_SRC:
......
......@@ -15,6 +15,7 @@ import org.onlab.onos.net.flow.criteria.Criteria.EthTypeCriterion;
import org.onlab.onos.net.flow.criteria.Criteria.IPCriterion;
import org.onlab.onos.net.flow.criteria.Criteria.IPProtocolCriterion;
import org.onlab.onos.net.flow.criteria.Criteria.PortCriterion;
import org.onlab.onos.net.flow.criteria.Criteria.TcpPortCriterion;
import org.onlab.onos.net.flow.criteria.Criteria.VlanIdCriterion;
import org.onlab.onos.net.flow.criteria.Criteria.VlanPcpCriterion;
import org.onlab.onos.net.flow.criteria.Criterion;
......@@ -42,6 +43,7 @@ import org.projectfloodlight.openflow.types.Masked;
import org.projectfloodlight.openflow.types.OFBufferId;
import org.projectfloodlight.openflow.types.OFPort;
import org.projectfloodlight.openflow.types.OFVlanVidMatch;
import org.projectfloodlight.openflow.types.TransportPort;
import org.projectfloodlight.openflow.types.U64;
import org.projectfloodlight.openflow.types.VlanPcp;
import org.projectfloodlight.openflow.types.VlanVid;
......@@ -199,6 +201,7 @@ public class FlowModBuilder {
Match.Builder mBuilder = factory.buildMatch();
EthCriterion eth;
IPCriterion ip;
TcpPortCriterion tp;
for (Criterion c : selector.criteria()) {
switch (c.type()) {
case IN_PORT:
......@@ -250,6 +253,14 @@ public class FlowModBuilder {
mBuilder.setExact(MatchField.VLAN_VID,
OFVlanVidMatch.ofVlanVid(VlanVid.ofVlan(vid.vlanId().toShort())));
break;
case TCP_DST:
tp = (TcpPortCriterion) c;
mBuilder.setExact(MatchField.TCP_DST, TransportPort.of(tp.tcpPort()));
break;
case TCP_SRC:
tp = (TcpPortCriterion) c;
mBuilder.setExact(MatchField.TCP_SRC, TransportPort.of(tp.tcpPort()));
break;
case ARP_OP:
case ARP_SHA:
case ARP_SPA:
......@@ -276,8 +287,6 @@ public class FlowModBuilder {
case PBB_ISID:
case SCTP_DST:
case SCTP_SRC:
case TCP_DST:
case TCP_SRC:
case TUNNEL_ID:
case UDP_DST:
case UDP_SRC:
......
package org.onlab.onos.provider.of.flow.impl;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
......@@ -68,10 +57,20 @@ import org.projectfloodlight.openflow.types.OFPort;
import org.projectfloodlight.openflow.types.U32;
import org.slf4j.Logger;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Provider which uses an OpenFlow controller to detect network
......@@ -166,6 +165,16 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
for (FlowRuleBatchEntry fbe : batch.getOperations()) {
FlowRule flowRule = fbe.getTarget();
OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId().uri()));
if (sw == null) {
/*
* if a switch we are supposed to install to is gone then
* cancel (ie. rollback) the work that has been done so far
* and return the associated future.
*/
InstallationFuture failed = new InstallationFuture(sws, fmXids);
failed.cancel(true);
return failed;
}
sws.add(new Dpid(sw.getId()));
FlowModBuilder builder = new FlowModBuilder(flowRule, sw.factory());
switch (fbe.getOperator()) {
......@@ -322,6 +331,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
public void fail(OFErrorMsg msg, Dpid dpid) {
ok.set(false);
removeRequirement(dpid);
FlowEntry fe = null;
FlowRuleBatchEntry fbe = fms.get(msg.getXid());
FlowRule offending = fbe.getTarget();
......@@ -374,11 +384,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
public void satisfyRequirement(Dpid dpid) {
log.warn("Satisfaction from switch {}", dpid);
sws.remove(dpid);
countDownLatch.countDown();
cleanUp();
log.debug("Satisfaction from switch {}", dpid);
removeRequirement(dpid);
}
......@@ -395,6 +402,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
ok.set(false);
this.state = BatchState.CANCELLED;
cleanUp();
for (FlowRuleBatchEntry fbe : fms.values()) {
......@@ -438,7 +446,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
}
private void cleanUp() {
if (sws.isEmpty()) {
if (isDone() || isCancelled()) {
pendingFutures.remove(pendingXid);
for (Long xid : fms.keySet()) {
pendingFMs.remove(xid);
......@@ -446,6 +454,12 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
}
}
private void removeRequirement(Dpid dpid) {
countDownLatch.countDown();
sws.remove(dpid);
cleanUp();
}
}
}
......
package org.onlab.onos.provider.of.host.impl;
import static org.onlab.onos.net.DeviceId.deviceId;
import static org.onlab.onos.net.PortNumber.portNumber;
import static org.slf4j.LoggerFactory.getLogger;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
......@@ -29,15 +33,12 @@ import org.onlab.packet.IpPrefix;
import org.onlab.packet.VlanId;
import org.slf4j.Logger;
import static org.onlab.onos.net.DeviceId.deviceId;
import static org.onlab.onos.net.PortNumber.portNumber;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Provider which uses an OpenFlow controller to detect network
* end-station hosts.
*/
@Component(immediate = true)
@Deprecated
public class OpenFlowHostProvider extends AbstractProvider implements HostProvider {
private final Logger log = getLogger(getClass());
......@@ -109,14 +110,16 @@ public class OpenFlowHostProvider extends AbstractProvider implements HostProvid
// Potentially a new or moved host
if (eth.getEtherType() == Ethernet.TYPE_ARP) {
ARP arp = (ARP) eth.getPayload();
IpPrefix ip = IpPrefix.valueOf(arp.getSenderProtocolAddress());
IpPrefix ip = IpPrefix.valueOf(arp.getSenderProtocolAddress(),
IpPrefix.MAX_INET_MASK);
HostDescription hdescr =
new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc, ip);
providerService.hostDetected(hid, hdescr);
} else if (ipLearn && eth.getEtherType() == Ethernet.TYPE_IPV4) {
IPv4 pip = (IPv4) eth.getPayload();
IpPrefix ip = IpPrefix.valueOf(pip.getSourceAddress());
IpPrefix ip = IpPrefix.valueOf(pip.getSourceAddress(),
IpPrefix.MAX_INET_MASK);
HostDescription hdescr =
new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc, ip);
providerService.hostDetected(hid, hdescr);
......
......@@ -66,6 +66,7 @@ import org.slf4j.Logger;
* TODO: add 'fast discovery' mode: drop LLDPs in destination switch but listen
* for flow_removed messages
*/
@Deprecated
public class LinkDiscovery implements TimerTask {
private final OpenFlowSwitch sw;
......
......@@ -35,6 +35,7 @@ import org.slf4j.Logger;
* infrastructure links.
*/
@Component(immediate = true)
@Deprecated
public class OpenFlowLinkProvider extends AbstractProvider implements LinkProvider {
private final Logger log = getLogger(getClass());
......
......@@ -28,7 +28,6 @@ import org.onlab.onos.openflow.controller.OpenFlowController;
import org.onlab.onos.openflow.controller.OpenFlowPacketContext;
import org.onlab.onos.openflow.controller.OpenFlowSwitch;
import org.onlab.onos.openflow.controller.PacketListener;
import org.onlab.packet.Ethernet;
import org.projectfloodlight.openflow.protocol.OFPacketOut;
import org.projectfloodlight.openflow.protocol.OFPortDesc;
import org.projectfloodlight.openflow.protocol.action.OFAction;
......@@ -96,13 +95,13 @@ public class OpenFlowPacketProvider extends AbstractProvider implements PacketPr
return;
}
Ethernet eth = new Ethernet();
eth.deserialize(packet.data().array(), 0, packet.data().array().length);
//Ethernet eth = new Ethernet();
//eth.deserialize(packet.data().array(), 0, packet.data().array().length);
OFPortDesc p = null;
for (Instruction inst : packet.treatment().instructions()) {
if (inst.type().equals(Instruction.Type.OUTPUT)) {
p = portDesc(((OutputInstruction) inst).port());
OFPacketOut po = packetOut(sw, eth, p.getPortNo());
OFPacketOut po = packetOut(sw, packet.data().array(), p.getPortNo());
sw.sendMsg(po);
}
}
......@@ -116,7 +115,7 @@ public class OpenFlowPacketProvider extends AbstractProvider implements PacketPr
return builder.build();
}
private OFPacketOut packetOut(OpenFlowSwitch sw, Ethernet eth, OFPort out) {
private OFPacketOut packetOut(OpenFlowSwitch sw, byte[] eth, OFPort out) {
OFPacketOut.Builder builder = sw.factory().buildPacketOut();
OFAction act = sw.factory().actions()
.buildOutput()
......@@ -126,7 +125,7 @@ public class OpenFlowPacketProvider extends AbstractProvider implements PacketPr
.setBufferId(OFBufferId.NO_BUFFER)
.setInPort(OFPort.NO_MASK)
.setActions(Collections.singletonList(act))
.setData(eth.serialize())
.setData(eth)
.build();
}
......
......@@ -18,6 +18,8 @@
<modules>
<module>openflow</module>
<module>lldp</module>
<module>host</module>
</modules>
<dependencies>
......
......@@ -45,6 +45,7 @@ alias pub='onos-push-update-bundle'
# Short-hand for tailing the ONOS (karaf) log
alias tl='$ONOS_ROOT/tools/dev/bin/onos-local-log'
alias tlo='tl | grep --colour=always org.onlab'
alias ll='less $KARAF_LOG'
# Pretty-print JSON output
alias pp='python -m json.tool'
......@@ -67,13 +68,14 @@ function cell {
[ ! -f $ONOS_ROOT/tools/test/cells/$1 ] && \
echo "No such cell: $1" >&2 && return 1
unset ONOS_CELL ONOS_NIC ONOS_FEATURES
unset OC1 OC2 OC3 OC4 OC5 OC6 OC7 OC8 OC9 OCN OCI
unset OC0 OC1 OC2 OC3 OC4 OC5 OC6 OC7 OC8 OC9 OCN OCI
export ONOS_CELL=$1
. $ONOS_ROOT/tools/test/cells/$1
cell
else
env | egrep "ONOS_CELL"
env | egrep "OCI"
env | egrep "OC[1-9]+" | sort
env | egrep "OC[0-9]+" | sort
env | egrep "OCN"
env | egrep "ONOS_" | egrep -v 'ONOS_ROOT|ONOS_CELL'
fi
......
#!/bin/tcsh
# ONOS developer csh/tcsh profile conveniences
# Simply include in your own $HOME/.cshrc file. E.g.:
#
# setenv ONOS_ROOT $HOME/onos
# if ( -f $ONOS_ROOT/tools/dev/onos.cshrc ) then
# source $ONOS_ROOT/tools/dev/onos.cshrc
# endif
#
# Root of the ONOS source tree
if ( ! $?ONOS_ROOT ) then
setenv ONOS_ROOT $HOME/onos
endif
# Setup some environmental context for developers
if ( ! $?JAVA_HOME ) then
if ( -x /usr/libexec/java_home ) then
setenv JAVA_HOME `/usr/libexec/java_home -v 1.7`
else if ( -d /usr/lib/jvm/java-7-openjdk-amd64 ) then
setenv JAVA_HOME /usr/lib/jvm/java-7-openjdk-amd64
endif
endif
if ( ! $?MAVEN ) then
setenv MAVEN $HOME/Applications/apache-maven-3.2.2
endif
if ( ! $?KARAF ) then
setenv KARAF $HOME/Applications/apache-karaf-3.0.1
endif
setenv KARAF_LOG $KARAF/data/log/karaf.log
alias onos-setup-cell ' ( $ONOS_ROOT/tools/test/bin/onos-show-cell \!^ ) && setenv ONOS_CELL \!^'
set path=( $path $ONOS_ROOT/tools/dev/bin $ONOS_ROOT/tools/test/bin )
set path=( $path $ONOS_ROOT/tools/build )
set path=( $path $KARAF/bin )
......@@ -6,4 +6,39 @@
[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
. $ONOS_ROOT/tools/build/envDefaults
ssh $ONOS_USER@${1:-$OCI} "sudo service onos ${2:-status}"
function print_usage {
command_name=`basename $0`
echo "Remotely administer the ONOS service on a single node or the current ONOS cell."
echo
echo "Usage: $command_name <TARGET> [COMMAND]"
echo " $command_name [-h | --help]"
echo "Options:"
echo " TARGET The target of the command"
echo " COMMAND The command to execute. Default value is 'status'"
echo " [-h | --help] Print this help"
echo ""
echo "TARGET: <hostname | --cell>"
echo " hostname Execute on the specified host name"
echo " --cell Execute on the current ONOS cell"
echo ""
echo "COMMAND: [start|stop|restart|status]"
echo ""
}
# Print usage
if [ "${1}" = "-h" -o "${1}" = "--help" ]; then
print_usage
exit 0
fi
# Select the target
if [ "${1}" = "--cell" ]; then
nodes=$(env | sort | egrep "OC[0-9]+" | cut -d= -f2)
else
nodes=${1:-$OCI}
fi
# Execute the remote commands
for node in $nodes; do
ssh $ONOS_USER@${node} "sudo service onos ${2:-status}"
done
......
......@@ -42,7 +42,7 @@ fi
echo "ONOS_CELL=${ONOS_CELL}"
echo "ONOS_NIC=${ONOS_NIC}"
for n in {1..9}; do
for n in {0..9}; do
ocn="OC${n}"
if [ -n "${!ocn}" ]; then
echo "$ocn=${!ocn}"
......
......@@ -8,4 +8,4 @@ export OC2="192.168.56.102"
export OCN="192.168.56.103"
export OCI="${OC1}"
export ONOS_FEATURES=""
export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core,onos-cli,onos-openflow,onos-app-fwd,onos-app-proxyarp,onos-app-tvue}"
......
......@@ -2,8 +2,8 @@
export ONOS_CELL="office"
export ONOS_NIC="10.128.4.*"
export OC1="10.128.4.60"
export ONOS_NIC="10.1.10.*"
export OC1="10.1.10.223"
export OCI="${OC1}"
export ONOS_FEATURES="webconsole,onos-api,onos-core-trivial,onos-cli,onos-openflow,onos-app-fwd,onos-app-mobility,onos-app-tvue,onos-app-proxyarp"
......
......@@ -7,4 +7,4 @@ export OC1="192.168.56.101"
export OCN="192.168.56.103"
export OCI="${OC1}"
export ONOS_FEATURES=""
export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core-trivial,onos-cli,onos-openflow,onos-app-fwd,onos-app-proxyarp,onos-app-tvue}"
......
# Local VirtualBox-based single ONOS instance & ONOS mininet box
export ONOS_NIC=192.168.56.*
export OC1="192.168.56.101"
export OCN="192.168.56.103"
export ONOS_FEATURES=webconsole,onos-api,onos-core-trivial,onos-cli,onos-openflow,onos-app-fwd,onos-app-mobility,onos-app-tvue,onos-app-optical
package org.onlab.packet;
/**
* The class representing a network device chassisId.
* This class is immutable.
*/
// TODO: Move this to a reasonable place.
public final class ChassisId {
private static final long UNKNOWN = 0;
private final long value;
/**
* Default constructor.
*/
public ChassisId() {
this.value = ChassisId.UNKNOWN;
}
/**
* Constructor from a long value.
*
* @param value the value to use.
*/
public ChassisId(long value) {
this.value = value;
}
/**
* Constructor from a string.
*
* @param value the value to use.
*/
public ChassisId(String value) {
this.value = Long.valueOf(value);
}
/**
* Get the value of the chassis id.
*
* @return the value of the chassis id.
*/
public long value() {
return value;
}
/**
* Convert the Chassis Id value to a ':' separated hexadecimal string.
*
* @return the Chassis Id value as a ':' separated hexadecimal string.
*/
@Override
public String toString() {
return Long.toHexString(this.value);
}
@Override
public boolean equals(Object other) {
if (!(other instanceof ChassisId)) {
return false;
}
ChassisId otherChassisId = (ChassisId) other;
return value == otherChassisId.value;
}
@Override
public int hashCode() {
int hash = 17;
hash += 31 * hash + (int) (value ^ value >>> 32);
return hash;
}
}
......@@ -58,6 +58,7 @@ public class Ethernet extends BasePacket {
Ethernet.etherTypeClassMap.put(Ethernet.TYPE_RARP, ARP.class);
Ethernet.etherTypeClassMap.put(Ethernet.TYPE_IPV4, IPv4.class);
Ethernet.etherTypeClassMap.put(Ethernet.TYPE_LLDP, LLDP.class);
Ethernet.etherTypeClassMap.put(Ethernet.TYPE_BSN, LLDP.class);
}
protected MacAddress destinationMACAddress;
......
......@@ -150,7 +150,7 @@ public class LLDP extends BasePacket {
final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
LLDPTLV tlv;
do {
tlv = new LLDPTLV().deserialize(bb);
tlv = new LLDPOrganizationalTLV().deserialize(bb);
// if there was a failure to deserialize stop processing TLVs
if (tlv == null) {
......@@ -169,6 +169,7 @@ public class LLDP extends BasePacket {
case 0x3:
this.ttl = tlv;
break;
default:
this.optionalTLVList.add(tlv);
break;
......
......@@ -140,6 +140,9 @@ public class LLDPOrganizationalTLV extends LLDPTLV {
@Override
public byte[] serialize() {
if (this.type != LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
return super.serialize();
}
final int valueLength = LLDPOrganizationalTLV.OUI_LENGTH
+ LLDPOrganizationalTLV.SUBTYPE_LENGTH + this.infoString.length;
this.value = new byte[valueLength];
......@@ -152,7 +155,11 @@ public class LLDPOrganizationalTLV extends LLDPTLV {
@Override
public LLDPTLV deserialize(final ByteBuffer bb) {
super.deserialize(bb);
LLDPTLV tlv = super.deserialize(bb);
if (tlv.getType() != LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
return tlv;
}
final ByteBuffer optionalField = ByteBuffer.wrap(this.value);
final byte[] oui = new byte[LLDPOrganizationalTLV.OUI_LENGTH];
......
......@@ -111,6 +111,7 @@ public class LLDPTLV {
sscratch = bb.getShort();
this.type = (byte) (sscratch >> 9 & 0x7f);
this.length = (short) (sscratch & 0x1ff);
if (this.length > 0) {
this.value = new byte[this.length];
......@@ -120,6 +121,7 @@ public class LLDPTLV {
}
bb.get(this.value);
}
return this;
}
......
......@@ -30,6 +30,7 @@ import org.slf4j.LoggerFactory;
* Refer to IEEE Std 802.1ABTM-2009 for more information.
*
*/
@Deprecated
public class ONLabLddp extends LLDP {
private static final Logger LOG = LoggerFactory.getLogger(ONLabLddp.class);
......
package org.onlab.packet;
import com.google.common.collect.Lists;
import org.apache.commons.lang.ArrayUtils;
import java.nio.ByteBuffer;
/**
* ONOS LLDP containing organizational TLV for ONOS device dicovery.
*/
public class ONOSLLDP extends LLDP {
public static final byte[] ONLAB_OUI = {(byte) 0xa4, 0x23, 0x05};
public static final String DEFAULT_DEVICE = "INVALID";
public static final String DEFAULT_NAME = "ONOS Discovery";
public static final byte[] LLDP_NICIRA = {0x01, 0x23, 0x20, 0x00, 0x00,
0x01};
public static final byte[] LLDP_MULTICAST = {0x01, (byte) 0x80,
(byte) 0xc2, 0x00, 0x00, 0x0e};
public static final byte[] BDDP_MULTICAST = {(byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
private static final byte NAME_SUBTYPE = 1;
private static final byte DEVICE_SUBTYPE = 2;
private static final short NAME_LENGTH = 4; //1 for subtype + 3 for OUI
private static final short DEVICE_LENGTH = 4; //1 for subtype + 3 for OUI
private final LLDPOrganizationalTLV nameTLV = new LLDPOrganizationalTLV();
private final LLDPOrganizationalTLV deviceTLV = new LLDPOrganizationalTLV();
// TLV constants: type, size and subtype
// Organizationally specific TLV also have packet offset and contents of TLV
// header
private static final byte CHASSIS_TLV_TYPE = 1;
private static final byte CHASSIS_TLV_SIZE = 7;
private static final byte CHASSIS_TLV_SUBTYPE = 4;
private static final byte PORT_TLV_TYPE = 2;
private static final byte PORT_TLV_SIZE = 5;
private static final byte PORT_TLV_SUBTYPE = 2;
private static final byte TTL_TLV_TYPE = 3;
private final byte[] ttlValue = new byte[] {0, 0x78};
public ONOSLLDP() {
super();
setName(DEFAULT_NAME);
setDevice(DEFAULT_DEVICE);
setOptionalTLVList(Lists.<LLDPTLV>newArrayList(nameTLV, deviceTLV));
setTtl(new LLDPTLV().setType((byte) TTL_TLV_TYPE)
.setLength((short) ttlValue.length)
.setValue(ttlValue));
}
private ONOSLLDP(LLDP lldp) {
this.portId = lldp.getPortId();
this.chassisId = lldp.getChassisId();
this.ttl = lldp.getTtl();
this.optionalTLVList = lldp.getOptionalTLVList();
}
public void setName(String name) {
nameTLV.setLength((short) (name.length() + NAME_LENGTH));
nameTLV.setInfoString(name);
nameTLV.setSubType(NAME_SUBTYPE);
nameTLV.setOUI(ONLAB_OUI);
}
public void setDevice(String device) {
deviceTLV.setInfoString(device);
deviceTLV.setLength((short) (device.length() + DEVICE_LENGTH));
deviceTLV.setSubType(DEVICE_SUBTYPE);
deviceTLV.setOUI(ONLAB_OUI);
}
public void setChassisId(final ChassisId chassisId) {
MacAddress chassisMac = MacAddress.valueOf(chassisId.value());
byte[] chassis = ArrayUtils.addAll(new byte[] {CHASSIS_TLV_SUBTYPE},
chassisMac.getAddress());
LLDPTLV chassisTLV = new LLDPTLV();
chassisTLV.setLength(CHASSIS_TLV_SIZE);
chassisTLV.setType(CHASSIS_TLV_TYPE);
chassisTLV.setValue(chassis);
this.setChassisId(chassisTLV);
}
public void setPortId(final int portNumber) {
byte[] port = ArrayUtils.addAll(new byte[] {PORT_TLV_SUBTYPE},
ByteBuffer.allocate(4).putInt(portNumber).array());
LLDPTLV portTLV = new LLDPTLV();
portTLV.setLength(PORT_TLV_SIZE);
portTLV.setType(PORT_TLV_TYPE);
portTLV.setValue(port);
this.setPortId(portTLV);
}
public LLDPOrganizationalTLV getNameTLV() {
for (LLDPTLV tlv : this.getOptionalTLVList()) {
if (tlv.getType() == LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
LLDPOrganizationalTLV orgTLV = (LLDPOrganizationalTLV) tlv;
if (orgTLV.getSubType() == NAME_SUBTYPE) {
return orgTLV;
}
}
}
return null;
}
public LLDPOrganizationalTLV getDeviceTLV() {
for (LLDPTLV tlv : this.getOptionalTLVList()) {
if (tlv.getType() == LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
LLDPOrganizationalTLV orgTLV = (LLDPOrganizationalTLV) tlv;
if (orgTLV.getSubType() == DEVICE_SUBTYPE) {
return orgTLV;
}
}
}
return null;
}
public String getNameString() {
LLDPOrganizationalTLV tlv = getNameTLV();
if (tlv != null) {
return new String(tlv.getInfoString());
}
return null;
}
public String getDeviceString() {
LLDPOrganizationalTLV tlv = getDeviceTLV();
if (tlv != null) {
return new String(tlv.getInfoString());
}
return null;
}
public Integer getPort() {
ByteBuffer portBB = ByteBuffer.wrap(this.getPortId().getValue());
portBB.position(1);
return portBB.getInt();
}
/**
* Given an ethernet packet, determines if this is an LLDP from
* ONOS and returns the device the LLDP came from.
* @param eth an ethernet packet
* @return a the lldp packet or null
*/
public static ONOSLLDP parseONOSLLDP(Ethernet eth) {
if (eth.getEtherType() == Ethernet.TYPE_LLDP ||
eth.getEtherType() == Ethernet.TYPE_BSN) {
ONOSLLDP onosLldp = new ONOSLLDP((LLDP) eth.getPayload()); //(ONOSLLDP) eth.getPayload();
if (ONOSLLDP.DEFAULT_NAME.equals(onosLldp.getNameString())) {
return onosLldp;
}
}
return null;
}
}
package org.onlab.util;
public final class HexString {
private HexString() {
}
/**
* Convert a string of bytes to a ':' separated hex string.
*
* @param bytes
* @return "0f:ca:fe:de:ad:be:ef"
*/
public static String toHexString(final byte[] bytes) {
int i;
StringBuilder ret = new StringBuilder();
String tmp;
for (i = 0; i < bytes.length; i++) {
if (i > 0) {
ret.append(':');
}
tmp = Integer.toHexString((bytes[i] & 0xff));
if (tmp.length() == 1) {
ret.append('0');
}
ret.append(tmp);
}
return ret.toString();
}
public static String toHexString(final long val, final int padTo) {
char[] arr = Long.toHexString(val).toCharArray();
String ret = "";
// prepend the right number of leading zeros
int i = 0;
for (; i < (padTo * 2 - arr.length); i++) {
ret += "0";
if ((i % 2) != 0) {
ret += ":";
}
}
for (int j = 0; j < arr.length; j++) {
ret += arr[j];
if ((((i + j) % 2) != 0) && (j < (arr.length - 1))) {
ret += ":";
}
}
return ret;
}
public static String toHexString(final long val) {
return toHexString(val, 8);
}
/**
* Convert a string of hex values into a string of bytes.
*
* @param values
* "0f:ca:fe:de:ad:be:ef"
* @return [15, 5 ,2, 5, 17]
* @throws NumberFormatException
* If the string can not be parsed
*/
public static byte[] fromHexString(final String values) {
String[] octets = values.split(":");
byte[] ret = new byte[octets.length];
for (int i = 0; i < octets.length; i++) {
if (octets[i].length() > 2) {
throw new NumberFormatException("Invalid octet length");
}
ret[i] = Integer.valueOf(octets[i], 16).byteValue();
}
return ret;
}
public static long toLong(String value) {
String[] octets = value.split(":");
if (octets.length > 8) {
throw new NumberFormatException("Input string is too big to fit in long: " + value);
}
long l = 0;
for (String octet: octets) {
if (octet.length() > 2) {
throw new NumberFormatException(
"Each colon-separated byte component must consist of 1 or 2 hex digits: " + value);
}
short s = Short.parseShort(octet, 16);
l = (l << 8) + s;
}
return l;
}
}
package org.onlab.util;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Utilities for testing.
*/
public final class TestUtils {
/**
* Sets the field, bypassing scope restriction.
*
* @param subject Object where the field belongs
* @param fieldName name of the field to set
* @param value value to set to the field.
* @param <T> subject type
* @param <U> value type
* @throws TestUtilsException if there are reflection errors while setting
* the field
*/
public static <T, U> void setField(T subject, String fieldName, U value)
throws TestUtilsException {
@SuppressWarnings("unchecked")
Class<T> clazz = (Class<T>) subject.getClass();
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(subject, value);
} catch (NoSuchFieldException | SecurityException |
IllegalArgumentException | IllegalAccessException e) {
throw new TestUtilsException("setField failed", e);
}
}
/**
* Gets the field, bypassing scope restriction.
*
* @param subject Object where the field belongs
* @param fieldName name of the field to get
* @return value of the field.
* @param <T> subject type
* @param <U> field value type
* @throws TestUtilsException if there are reflection errors while getting
* the field
*/
public static <T, U> U getField(T subject, String fieldName)
throws TestUtilsException {
try {
@SuppressWarnings("unchecked")
Class<T> clazz = (Class<T>) subject.getClass();
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
@SuppressWarnings("unchecked")
U result = (U) field.get(subject);
return result;
} catch (NoSuchFieldException | SecurityException |
IllegalArgumentException | IllegalAccessException e) {
throw new TestUtilsException("getField failed", e);
}
}
/**
* Calls the method, bypassing scope restriction.
*
* @param subject Object where the method belongs
* @param methodName name of the method to call
* @param paramTypes formal parameter type array
* @param args arguments
* @return return value or null if void
* @param <T> subject type
* @param <U> return value type
* @throws TestUtilsException if there are reflection errors while calling
* the method
*/
public static <T, U> U callMethod(T subject, String methodName,
Class<?>[] paramTypes, Object...args) throws TestUtilsException {
try {
@SuppressWarnings("unchecked")
Class<T> clazz = (Class<T>) subject.getClass();
final Method method;
if (paramTypes == null || paramTypes.length == 0) {
method = clazz.getDeclaredMethod(methodName);
} else {
method = clazz.getDeclaredMethod(methodName, paramTypes);
}
method.setAccessible(true);
@SuppressWarnings("unchecked")
U result = (U) method.invoke(subject, args);
return result;
} catch (NoSuchMethodException | SecurityException |
IllegalAccessException | IllegalArgumentException |
InvocationTargetException e) {
throw new TestUtilsException("callMethod failed", e);
}
}
/**
* Calls the method, bypassing scope restriction.
*
* @param subject Object where the method belongs
* @param methodName name of the method to call
* @param paramType formal parameter type
* @param arg argument
* @return return value or null if void
* @param <T> subject type
* @param <U> return value type
* @throws TestUtilsException if there are reflection errors while calling
* the method
*/
public static <T, U> U callMethod(T subject, String methodName,
Class<?> paramType, Object arg) throws TestUtilsException {
return callMethod(subject, methodName, new Class<?>[]{paramType}, arg);
}
/**
* Triggers an allocation of an object of type <T> and forces a call to
* the private constructor.
*
* @param constructor Constructor to call
* @param <T> type of the object to create
* @return created object of type <T>
* @throws TestUtilsException if there are reflection errors while calling
* the constructor
*/
public static <T> T callConstructor(Constructor<T> constructor)
throws TestUtilsException {
try {
constructor.setAccessible(true);
return constructor.newInstance();
} catch (InstantiationException | IllegalAccessException |
InvocationTargetException error) {
throw new TestUtilsException("callConstructor failed", error);
}
}
/**
* Avoid instantiation.
*/
private TestUtils() {}
/**
* Exception that can be thrown if problems are encountered while executing
* the utility method. These are usually problems accessing fields/methods
* through reflection. The original exception can be found by examining the
* cause.
*/
public static class TestUtilsException extends Exception {
private static final long serialVersionUID = 1L;
/**
* Constructs a new exception with the specified detail message and
* cause.
*
* @param message the detail message
* @param cause the original cause of this exception
*/
public TestUtilsException(String message, Throwable cause) {
super(message, cause);
}
}
}
package org.onlab.util;
import org.junit.Test;
import com.esotericsoftware.minlog.Log;
import junit.framework.TestCase;
/**
* Test of the Hexstring.
*
*/
public class HexStringTest extends TestCase {
@Test
public void testMarshalling() throws Exception {
String dpidStr = "00:00:00:23:20:2d:16:71";
long dpid = HexString.toLong(dpidStr);
String testStr = HexString.toHexString(dpid);
TestCase.assertEquals(dpidStr, testStr);
}
@Test
public void testToLong() {
String dpidStr = "3e:1f:01:fc:72:8c:63:31";
long valid = 0x3e1f01fc728c6331L;
long testLong = HexString.toLong(dpidStr);
TestCase.assertEquals(valid, testLong);
}
@Test
public void testToLongMSB() {
String dpidStr = "ca:7c:5e:d1:64:7a:95:9b";
long valid = -3856102927509056101L;
long testLong = HexString.toLong(dpidStr);
TestCase.assertEquals(valid, testLong);
}
@Test
public void testToLongError() {
String dpidStr = "09:08:07:06:05:04:03:02:01";
try {
HexString.toLong(dpidStr);
fail("HexString.toLong() should have thrown a NumberFormatException");
} catch (NumberFormatException expected) {
Log.info("HexString.toLong() have thrown a NumberFormatException");
}
}
@Test
public void testToStringBytes() {
byte[] dpid = {0, 0, 0, 0, 0, 0, 0, -1 };
String valid = "00:00:00:00:00:00:00:ff";
String testString = HexString.toHexString(dpid);
TestCase.assertEquals(valid, testString);
}
@Test
public void testFromHexStringError() {
String invalidStr = "00:00:00:00:00:00:ffff";
try {
HexString.fromHexString(invalidStr);
fail("HexString.fromHexString() should have thrown a NumberFormatException");
} catch (NumberFormatException expected) {
Log.info("HexString.toLong() have thrown a NumberFormatException");
}
}
}
package org.onlab.util;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import org.junit.Before;
import org.junit.Test;
import org.onlab.util.TestUtils.TestUtilsException;
/**
* Test and usage examples for TestUtils.
*/
public class TestUtilsTest {
/**
* Test data.
*/
private static final class TestClass {
@SuppressWarnings("unused")
private int privateField = 42;
@SuppressWarnings("unused")
protected int protectedField = 2501; // CHECKSTYLE IGNORE THIS LINE
/**
* Protected method with multiple argument.
*
* @param x simply returns
* @param y not used
* @return x
*/
@SuppressWarnings("unused")
private int privateMethod(Number x, Long y) {
return x.intValue();
}
/**
* Protected method with no argument.
*
* @return int
*/
@SuppressWarnings("unused")
protected int protectedMethod() {
return 42;
}
/**
* Method returning array.
*
* @param ary random array
* @return ary
*/
@SuppressWarnings("unused")
private int[] arrayReturnMethod(int[] ary) {
return ary;
}
/**
* Method without return value.
*
* @param s ignored
*/
@SuppressWarnings("unused")
private void voidMethod(String s) {
System.out.println(s);
}
}
private TestClass test;
/**
* Sets up the test fixture.
*/
@Before
public void setUp() {
test = new TestClass();
}
/**
* Example to access private field.
*
* @throws TestUtilsException TestUtils error
*/
@Test
public void testSetGetPrivateField() throws TestUtilsException {
assertEquals(42, TestUtils.getField(test, "privateField"));
TestUtils.setField(test, "privateField", 0xDEAD);
assertEquals(0xDEAD, TestUtils.getField(test, "privateField"));
}
/**
* Example to access protected field.
*
* @throws TestUtilsException TestUtils error
*/
@Test
public void testSetGetProtectedField() throws TestUtilsException {
assertEquals(2501, TestUtils.getField(test, "protectedField"));
TestUtils.setField(test, "protectedField", 0xBEEF);
assertEquals(0xBEEF, TestUtils.getField(test, "protectedField"));
}
/**
* Example to call private method and multiple parameters.
* <p/>
* It also illustrates that paramTypes must match declared type,
* not the runtime types of arguments.
*
* @throws TestUtilsException TestUtils error
*/
@Test
public void testCallPrivateMethod() throws TestUtilsException {
int result = TestUtils.callMethod(test, "privateMethod",
new Class<?>[] {Number.class, Long.class},
Long.valueOf(42), Long.valueOf(32));
assertEquals(42, result);
}
/**
* Example to call protected method and no parameters.
*
* @throws TestUtilsException TestUtils error
*/
@Test
public void testCallProtectedMethod() throws TestUtilsException {
int result = TestUtils.callMethod(test, "protectedMethod",
new Class<?>[] {});
assertEquals(42, result);
}
/**
* Example to call method returning array.
* <p/>
* Note: It is not required to receive as Object.
* Following is just verifying it is not Boxed arrays.
*
* @throws TestUtilsException TestUtils error
*/
@Test
public void testCallArrayReturnMethod() throws TestUtilsException {
int[] array = {1, 2, 3};
Object aryResult = TestUtils.callMethod(test, "arrayReturnMethod",
new Class<?>[] {int[].class}, array);
assertEquals(int[].class, aryResult.getClass());
assertArrayEquals(array, (int[]) aryResult);
}
/**
* Example to call void returning method.
* <p/>
* Note: Return value will be null for void methods.
*
* @throws TestUtilsException TestUtils error
*/
@Test
public void testCallVoidReturnMethod() throws TestUtilsException {
Object voidResult = TestUtils.callMethod(test, "voidMethod",
String.class, "foobar");
assertNull(voidResult);
}
}