Merge pull request #7 from OPENNETWORKINGLAB/master
Merge with Master changes
diff --git a/README.md b/README.md
index 08878d4..b71b9aa 100644
--- a/README.md
+++ b/README.md
@@ -41,8 +41,9 @@
Edit file (ONOS-INSTALL-DIR)/start-cassandra.sh and set variable
"CASSANDRA_DIR" to point to the Cassandra directory.
-Running ONOS
-------------
+Running ONOS with Cassandra as a separate process
+-------------------------------------------------
+[See below for information how to run ONOS with Embedded Cassandra]
1. Start Zookeeper
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
index c36d4a5..2124f78 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
@@ -25,7 +25,6 @@
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
import net.floodlightcontroller.core.util.SingletonTask;
-import net.floodlightcontroller.devicemanager.IDeviceService;
import net.floodlightcontroller.packet.Ethernet;
import net.floodlightcontroller.packet.IPv4;
import net.floodlightcontroller.restserver.IRestApiService;
@@ -71,24 +70,24 @@
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
+import com.google.common.net.InetAddresses;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
public class BgpRoute implements IFloodlightModule, IBgpRouteService,
- ITopologyListener, IOFSwitchListener,
- IArpRequester {
+ ITopologyListener, IArpRequester,
+ IOFSwitchListener {
protected static Logger log = LoggerFactory.getLogger(BgpRoute.class);
protected IFloodlightProviderService floodlightProvider;
protected ITopologyService topology;
protected ITopoRouteService topoRouteService;
- protected IDeviceService devices;
protected IRestApiService restApi;
protected ProxyArpManager proxyArp;
- //protected static Ptree ptree;
- protected IPatriciaTrie ptree;
+ protected IPatriciaTrie<RibEntry> ptree;
+ protected IPatriciaTrie<Interface> interfacePtrie;
protected BlockingQueue<RibUpdate> ribUpdates;
protected String bgpdRestIp;
@@ -107,6 +106,7 @@
//Forwarding uses priority 0, and the mac rewrite entries in ingress switches
//need to be higher priority than this otherwise the rewrite may not get done
protected final short SDNIP_PRIORITY = 10;
+ protected final short ARP_PRIORITY = 20;
protected final short BGP_PORT = 179;
@@ -117,6 +117,7 @@
protected Map<String, Interface> interfaces;
protected Map<InetAddress, BgpPeer> bgpPeers;
protected SwitchPort bgpdAttachmentPoint;
+ protected MACAddress bgpdMacAddress;
//True when all switches have connected
protected volatile boolean switchesConnected = false;
@@ -127,30 +128,17 @@
protected SingletonTask topologyChangeDetectorTask;
protected SetMultimap<InetAddress, RibUpdate> prefixesWaitingOnArp;
- protected SetMultimap<InetAddress, PathUpdate> pathsWaitingOnArp;
+
+ protected Map<InetAddress, Path> pathsWaitingOnArp;
protected ExecutorService bgpUpdatesExecutor;
+ protected Map<InetAddress, Path> pushedPaths;
+ protected Map<Prefix, Path> prefixToPath;
protected Multimap<Prefix, PushedFlowMod> pushedFlows;
- private class PushedFlowMod {
- private long dpid;
- private OFFlowMod flowMod;
+ protected volatile Map<Long, ?> topoRouteTopology = null;
- public PushedFlowMod(long dpid, OFFlowMod flowMod) {
- this.dpid = dpid;
- this.flowMod = flowMod;
- }
-
- public long getDpid() {
- return dpid;
- }
-
- public OFFlowMod getFlowMod() {
- return flowMod;
- }
- }
-
protected class TopologyChangeDetector implements Runnable {
@Override
public void run() {
@@ -174,16 +162,18 @@
}
}
- if (linkUpdates.isEmpty()){
- //All updates have been seen in network map.
- //We can check if topology is ready
- log.debug("No known changes outstanding. Checking topology now");
- checkStatus();
- }
- else {
- //We know of some link updates that haven't propagated to the database yet
- log.debug("Some changes not found in network map - {} links missing", linkUpdates.size());
- topologyChangeDetectorTask.reschedule(TOPO_DETECTION_WAIT, TimeUnit.SECONDS);
+ if (!topologyReady) {
+ if (linkUpdates.isEmpty()){
+ //All updates have been seen in network map.
+ //We can check if topology is ready
+ log.debug("No known changes outstanding. Checking topology now");
+ checkStatus();
+ }
+ else {
+ //We know of some link updates that haven't propagated to the database yet
+ log.debug("Some changes not found in network map - {} links missing", linkUpdates.size());
+ topologyChangeDetectorTask.reschedule(TOPO_DETECTION_WAIT, TimeUnit.SECONDS);
+ }
}
}
}
@@ -209,6 +199,7 @@
new Dpid(config.getBgpdAttachmentDpid()),
new Port(config.getBgpdAttachmentPort()));
+ bgpdMacAddress = config.getBgpdMacAddress();
} catch (JsonParseException e) {
log.error("Error in JSON file", e);
System.exit(1);
@@ -219,6 +210,12 @@
log.error("Error reading JSON file", e);
System.exit(1);
}
+
+ //Populate the interface Patricia Trie
+ for (Interface intf : interfaces.values()) {
+ Prefix prefix = new Prefix(intf.getIpAddress().getAddress(), intf.getPrefixLength());
+ interfacePtrie.put(prefix, intf);
+ }
}
@Override
@@ -243,7 +240,6 @@
= new ArrayList<Class<? extends IFloodlightService>>();
l.add(IFloodlightProviderService.class);
l.add(ITopologyService.class);
- l.add(IDeviceService.class);
l.add(IRestApiService.class);
return l;
}
@@ -252,20 +248,19 @@
public void init(FloodlightModuleContext context)
throws FloodlightModuleException {
- //ptree = new Ptree(32);
- ptree = new PatriciaTrie(32);
+ ptree = new PatriciaTrie<RibEntry>(32);
+ interfacePtrie = new PatriciaTrie<Interface>(32);
ribUpdates = new LinkedBlockingQueue<RibUpdate>();
// Register floodlight provider and REST handler.
floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
topology = context.getServiceImpl(ITopologyService.class);
- devices = context.getServiceImpl(IDeviceService.class);
restApi = context.getServiceImpl(IRestApiService.class);
//TODO We'll initialise this here for now, but it should really be done as
//part of the controller core
- proxyArp = new ProxyArpManager(floodlightProvider, topology, devices);
+ proxyArp = new ProxyArpManager(floodlightProvider, topology);
linkUpdates = new ArrayList<LDUpdate>();
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
@@ -273,11 +268,12 @@
topoRouteService = new TopoRouteService("");
- pathsWaitingOnArp = Multimaps.synchronizedSetMultimap(
- HashMultimap.<InetAddress, PathUpdate>create());
+ pathsWaitingOnArp = new HashMap<InetAddress, Path>();
prefixesWaitingOnArp = Multimaps.synchronizedSetMultimap(
HashMultimap.<InetAddress, RibUpdate>create());
+ pushedPaths = new HashMap<InetAddress, Path>();
+ prefixToPath = new HashMap<Prefix, Path>();
pushedFlows = HashMultimap.<Prefix, PushedFlowMod>create();
bgpUpdatesExecutor = Executors.newSingleThreadExecutor(
@@ -309,19 +305,30 @@
log.debug("Config file set to {}", configFilename);
readGatewaysConfiguration(configFilename);
- // Test.
- //test();
+
+ proxyArp.setL3Mode(interfacePtrie, interfaces.values(), bgpdMacAddress);
+ }
+
+ @Override
+ public void startUp(FloodlightModuleContext context) {
+ restApi.addRestletRoutable(new BgpRouteWebRoutable());
+ topology.addListener(this);
+ floodlightProvider.addOFSwitchListener(this);
+
+ proxyArp.startUp();
+
+ floodlightProvider.addOFMessageListener(OFType.PACKET_IN, proxyArp);
+
+ //Retrieve the RIB from BGPd during startup
+ retrieveRib();
}
- //public Ptree getPtree() {
- public IPatriciaTrie getPtree() {
+ public IPatriciaTrie<RibEntry> getPtree() {
return ptree;
}
public void clearPtree() {
- //ptree = null;
- //ptree = new Ptree(32);
- ptree = new PatriciaTrie(32);
+ ptree = new PatriciaTrie<RibEntry>(32);
}
public String getBGPdRestIp() {
@@ -332,105 +339,6 @@
return routerId;
}
- // Return nexthop address as byte array.
- /*
- public RibEntry lookupRib(byte[] dest) {
- if (ptree == null) {
- log.debug("lookupRib: ptree null");
- return null;
- }
-
- PtreeNode node = ptree.match(dest, 32);
- if (node == null) {
- log.debug("lookupRib: ptree node null");
- return null;
- }
-
- if (node.rib == null) {
- log.debug("lookupRib: ptree rib null");
- return null;
- }
-
- ptree.delReference(node);
-
- return node.rib;
- }
- */
-
- /*
- //TODO looks like this should be a unit test
- @SuppressWarnings("unused")
- private void test() throws UnknownHostException {
- System.out.println("Here it is");
- Prefix p = new Prefix("128.0.0.0", 8);
- Prefix q = new Prefix("8.0.0.0", 8);
- Prefix r = new Prefix("10.0.0.0", 24);
- Prefix a = new Prefix("10.0.0.1", 32);
-
- ptree.acquire(p.getAddress(), p.getPrefixLength());
- ptree.acquire(q.getAddress(), q.getPrefixLength());
- ptree.acquire(r.getAddress(), r.getPrefixLength());
-
- System.out.println("Traverse start");
- for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
- Prefix p_result = new Prefix(node.key, node.keyBits);
- }
-
- PtreeNode n = ptree.match(a.getAddress(), a.getPrefixLength());
- if (n != null) {
- System.out.println("Matched prefix for 10.0.0.1:");
- Prefix x = new Prefix(n.key, n.keyBits);
- ptree.delReference(n);
- }
-
- n = ptree.lookup(p.getAddress(), p.getPrefixLength());
- if (n != null) {
- ptree.delReference(n);
- ptree.delReference(n);
- }
- System.out.println("Traverse start");
- for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
- Prefix p_result = new Prefix(node.key, node.keyBits);
- }
-
- n = ptree.lookup(q.getAddress(), q.getPrefixLength());
- if (n != null) {
- ptree.delReference(n);
- ptree.delReference(n);
- }
- System.out.println("Traverse start");
- for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
- Prefix p_result = new Prefix(node.key, node.keyBits);
- }
-
- n = ptree.lookup(r.getAddress(), r.getPrefixLength());
- if (n != null) {
- ptree.delReference(n);
- ptree.delReference(n);
- }
- System.out.println("Traverse start");
- for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
- Prefix p_result = new Prefix(node.key, node.keyBits);
- }
-
- }
- */
-
- //TODO once the Ptree is object oriented this can go
- /*
- private String getPrefixFromPtree(PtreeNode node){
- InetAddress address = null;
- try {
- address = InetAddress.getByAddress(node.key);
- } catch (UnknownHostException e1) {
- //Should never happen is the reverse conversion has already been done
- log.error("Malformed IP address");
- return "";
- }
- return address.toString() + "/" + node.rib.masklen;
- }
- */
-
private void retrieveRib(){
String url = "http://" + bgpdRestIp + "/wm/bgp/" + routerId;
String response = RestClient.get(url);
@@ -469,21 +377,8 @@
continue;
}
- //PtreeNode node = ptree.acquire(p.getAddress(), p.getPrefixLength());
RibEntry rib = new RibEntry(router_id, nexthop);
-
- /*
- if (node.rib != null) {
- node.rib = null;
- ptree.delReference(node);
- }
-
- node.rib = rib;
- */
-
- //ptree.put(p, rib);
-
- //addPrefixFlows(p, rib);
+
try {
ribUpdates.put(new RibUpdate(Operation.UPDATE, p, rib));
} catch (InterruptedException e) {
@@ -497,8 +392,8 @@
try {
ribUpdates.put(update);
} catch (InterruptedException e) {
- // TODO Auto-generated catch block
- log.debug(" ", e);
+ log.debug("Interrupted while putting on ribUpdates queue", e);
+ Thread.currentThread().interrupt();
}
}
@@ -507,140 +402,114 @@
log.debug("Processing prefix add {}", prefix);
- //PtreeNode node = ptree.acquire(prefix.getAddress(), prefix.getPrefixLength());
RibEntry rib = ptree.put(prefix, update.getRibEntry());
- //if (node.rib != null) {
if (rib != null && !rib.equals(update.getRibEntry())) {
//There was an existing nexthop for this prefix. This update supersedes that,
//so we need to remove the old flows for this prefix from the switches
- deletePrefixFlows(prefix);
-
- //Then remove the old nexthop from the Ptree
- //node.rib = null;
- //ptree.delReference(node);
+ _processDeletePrefix(prefix, rib);
}
- //Put the new nexthop in the Ptree
- //node.rib = update.getRibEntry();
-
- //Push flows for the new <prefix, nexthop>
- addPrefixFlows(prefix, update.getRibEntry());
+ if (update.getRibEntry().getNextHop().equals(
+ InetAddresses.forString("0.0.0.0"))) {
+ //Route originated by SDN domain
+ //We don't handle these at the moment
+ log.debug("Own route {} to {}", prefix,
+ update.getRibEntry().getNextHop().getHostAddress());
+ return;
+ }
+
+ _processRibAdd(update);
}
- public synchronized void processRibDelete(RibUpdate update) {
+ private void _processRibAdd(RibUpdate update) {
Prefix prefix = update.getPrefix();
+ RibEntry rib = update.getRibEntry();
- //PtreeNode node = ptree.lookup(prefix.getAddress(), prefix.getPrefixLength());
+ InetAddress dstIpAddress = rib.getNextHop();
- /*
- * Remove the flows from the switches before the rib is lost
- * Theory: we could get a delete for a prefix not in the Ptree.
- * This would result in a null node being returned. We could get a delete for
- * a node that's not actually there, but is a aggregate node. This would result
- * in a non-null node with a null rib. Only a non-null node with a non-null
- * rib is an actual prefix in the Ptree.
- */
-
- /*
- if (node != null && node.rib != null) {
- if (update.getRibEntry().equals(node.rib)) {
- node.rib = null;
- ptree.delReference(node);
-
- deletePrefixFlows(update.getPrefix());
+ //See if we know the MAC address of the next hop
+ byte[] nextHopMacAddress = proxyArp.getMacAddress(rib.getNextHop());
+
+ //Find the attachment point (egress interface) of the next hop
+ Interface egressInterface = null;
+ if (bgpPeers.containsKey(dstIpAddress)) {
+ //Route to a peer
+ log.debug("Route to peer {}", dstIpAddress);
+ BgpPeer peer = bgpPeers.get(dstIpAddress);
+ egressInterface = interfaces.get(peer.getInterfaceName());
+ }
+ else {
+ //Route to non-peer
+ log.debug("Route to non-peer {}", dstIpAddress);
+ egressInterface = interfacePtrie.match(
+ new Prefix(dstIpAddress.getAddress(), 32));
+ if (egressInterface == null) {
+ log.warn("No outgoing interface found for {}", dstIpAddress.getHostAddress());
+ return;
}
}
- */
- if (ptree.remove(prefix, update.getRibEntry())) {
- /*
- * Only delete flows if an entry was actually removed from the trie.
- * If no entry was removed, the <prefix, nexthop> wasn't there so
- * it's probably already been removed and we don't need to do anything
- */
- deletePrefixFlows(prefix);
+ if (nextHopMacAddress == null) {
+ prefixesWaitingOnArp.put(dstIpAddress,
+ new RibUpdate(Operation.UPDATE, prefix, rib));
+ proxyArp.sendArpRequest(dstIpAddress, this, true);
+ return;
+ }
+ else {
+ if (!bgpPeers.containsKey(dstIpAddress)) {
+ //If the prefix is for a non-peer we need to ensure there's a path,
+ //and push one if there isn't.
+ Path path = pushedPaths.get(dstIpAddress);
+ if (path == null) {
+ path = new Path(egressInterface, dstIpAddress);
+ setUpDataPath(path, MACAddress.valueOf(nextHopMacAddress));
+ pushedPaths.put(dstIpAddress, path);
+ }
+
+ path.incrementUsers();
+ prefixToPath.put(prefix, path);
+ }
+
+ //For all prefixes we need to add the first-hop mac-rewriting flows
+ addPrefixFlows(prefix, egressInterface, nextHopMacAddress);
}
}
- //TODO compatibility layer, used by beginRouting()
- /*public void prefixAdded(PtreeNode node) {
- Prefix prefix = null;
- try {
- prefix = new Prefix(node.key, node.rib.masklen);
- } catch (IllegalArgumentException e) {
- log.error(" ", e);
- }
+ private void addPrefixFlows(Prefix prefix, Interface egressInterface, byte[] nextHopMacAddress) {
+ log.debug("Adding flows for prefix {} added, next hop mac {}",
+ prefix, HexString.toHexString(nextHopMacAddress));
- addPrefixFlows(prefix, node.rib);
- }*/
-
- private void addPrefixFlows(Prefix prefix, RibEntry rib) {
- if (!topologyReady){
- return;
- }
-
- //TODO before we do anything, we have to check that the RIB entry is still in the
- //Ptree because it could have been removed while we were waiting for ARP.
- //I think we'll have to make prefixAdded and prefixDelete atomic as well
- //to protect against the prefix getting deleted while where trying to add it
-
- log.debug("New prefix {} added, next hop {}",
- prefix, rib.getNextHop().getHostAddress());
-
- //TODO this is wrong, we shouldn't be dealing with BGP peers here.
- //We need to figure out where the device is attached and what its
- //mac address is by learning.
- //The next hop is not necessarily the peer, and the peer's attachment
- //point is not necessarily the next hop's attachment point.
- BgpPeer peer = bgpPeers.get(rib.getNextHop());
-
- if (peer == null){
- //TODO local router isn't in peers list so this will get thrown
- //Need to work out what to do about local prefixes with next hop 0.0.0.0.
-
- //The other scenario is this is a route server route. In that
- //case the next hop is not in our configuration
- log.error("Couldn't find next hop router in router {} in config",
- rib.getNextHop().getHostAddress());
- return; //just quit out here? This is probably a configuration error
- }
-
- //Get MAC address for peer from the ARP module
- //TODO separate out the 'ask for MAC' bit to another method
- byte[] peerMacAddress = proxyArp.getMacAddress(peer.getIpAddress());
- if (peerMacAddress == null) {
- //A RibUpdate is still a nice way to package them up
- prefixesWaitingOnArp.put(peer.getIpAddress(),
- new RibUpdate(Operation.UPDATE, prefix, rib));
- proxyArp.sendArpRequest(peer.getIpAddress(), this, true);
- return;
- }
-
- Interface peerInterface = interfaces.get(peer.getInterfaceName());
-
- //Add a flow to rewrite mac for this prefix to all border switches
+ //Add a flow to rewrite mac for this prefix to all other border switches
for (Interface srcInterface : interfaces.values()) {
- if (srcInterface == peerInterface) {
+ if (srcInterface == egressInterface) {
//Don't push a flow for the switch where this peer is attached
continue;
}
-
- DataPath shortestPath = topoRouteService.getShortestPath(
- srcInterface.getSwitchPort(),
- peerInterface.getSwitchPort());
+
+
+ DataPath shortestPath;
+ if (topoRouteTopology == null) {
+ shortestPath = topoRouteService.getShortestPath(
+ srcInterface.getSwitchPort(),
+ egressInterface.getSwitchPort());
+ }
+ else {
+ shortestPath = topoRouteService.getTopoShortestPath(
+ topoRouteTopology, srcInterface.getSwitchPort(),
+ egressInterface.getSwitchPort());
+ }
if (shortestPath == null){
log.debug("Shortest path between {} and {} not found",
srcInterface.getSwitchPort(),
- peerInterface.getSwitchPort());
+ egressInterface.getSwitchPort());
return; // just quit here?
}
//Set up the flow mod
- OFFlowMod fm =
- (OFFlowMod) floodlightProvider.getOFMessageFactory()
- .getMessage(OFType.FLOW_MOD);
+ OFFlowMod fm = (OFFlowMod) floodlightProvider.getOFMessageFactory()
+ .getMessage(OFType.FLOW_MOD);
fm.setIdleTimeout((short)0)
.setHardTimeout((short)0)
@@ -656,30 +525,16 @@
match.setDataLayerType(Ethernet.TYPE_IPv4);
match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
- /*
- InetAddress address = null;
- try {
- address = InetAddress.getByAddress(prefix.getAddress());
- } catch (UnknownHostException e1) {
- //Should never happen is the reverse conversion has already been done
- log.error("Malformed IP address");
- return;
- }*/
-
- //match.setFromCIDR(address.getHostAddress() + "/" +
- // prefix.getPrefixLength(), OFMatch.STR_NW_DST);
match.setFromCIDR(prefix.toString(), OFMatch.STR_NW_DST);
fm.setMatch(match);
//Set up MAC rewrite action
OFActionDataLayerDestination macRewriteAction = new OFActionDataLayerDestination();
- //TODO the peer's mac address is not necessarily the next hop's...
- macRewriteAction.setDataLayerAddress(peerMacAddress);
+ macRewriteAction.setDataLayerAddress(nextHopMacAddress);
//Set up output action
OFActionOutput outputAction = new OFActionOutput();
outputAction.setMaxLength((short)0xffff);
-
Port outputPort = shortestPath.flowEntries().get(0).outPort();
outputAction.setPort(outputPort.value());
@@ -697,7 +552,6 @@
continue;
}
- //TODO if prefix Added/Deleted are synchronized this shouldn't have to be
pushedFlows.put(prefix, new PushedFlowMod(sw.getId(), fm));
List<OFMessage> msglist = new ArrayList<OFMessage>();
@@ -711,20 +565,43 @@
}
}
- //TODO test next-hop changes
- //TODO check delete/add synchronization
+ public synchronized void processRibDelete(RibUpdate update) {
+ Prefix prefix = update.getPrefix();
- private void deletePrefixFlows(Prefix prefix) {
- if (!topologyReady) {
- return;
+ if (ptree.remove(prefix, update.getRibEntry())) {
+ /*
+ * Only delete flows if an entry was actually removed from the trie.
+ * If no entry was removed, the <prefix, nexthop> wasn't there so
+ * it's probably already been removed and we don't need to do anything
+ */
+ _processDeletePrefix(prefix, update.getRibEntry());
}
+ }
+
+ private void _processDeletePrefix(Prefix prefix, RibEntry ribEntry) {
+ deletePrefixFlows(prefix);
- log.debug("In deletePrefixFlows for {}", prefix);
-
- /*for (Map.Entry<Prefix, PushedFlowMod> entry : pushedFlows.entries()) {
- log.debug("Pushed flow: {} => {}", entry.getKey(), entry.getValue());
- }*/
-
+ log.debug("Deleting {} to {}", prefix, ribEntry.getNextHop());
+ log.debug("is peer {}", bgpPeers.containsKey(ribEntry.getNextHop()));
+ if (!bgpPeers.containsKey(ribEntry.getNextHop())) {
+ log.debug("Getting path for route with non-peer nexthop");
+ //Path path = prefixToPath.get(prefix);
+ Path path = prefixToPath.remove(prefix);
+
+ if (path == null) {
+ log.error("No path found for non-peer path");
+ }
+
+ path.decrementUsers();
+ log.debug("users {}, permanent {}", path.getUsers(), path.isPermanent());
+ if (path.getUsers() <= 0 && !path.isPermanent()) {
+ deletePath(path);
+ pushedPaths.remove(path.getDstIpAddress());
+ }
+ }
+ }
+
+ private void deletePrefixFlows(Prefix prefix) {
Collection<PushedFlowMod> pushedFlowMods
= pushedFlows.removeAll(prefix);
@@ -736,29 +613,45 @@
HexString.toHexString(((OFActionDataLayerDestination)pfm.getFlowMod().getActions().get(0))
.getDataLayerAddress())});
- OFFlowMod fm = pfm.getFlowMod();
-
- fm.setCommand(OFFlowMod.OFPFC_DELETE)
- .setOutPort(OFPort.OFPP_NONE)
- .setLengthU(OFFlowMod.MINIMUM_LENGTH);
-
- fm.getActions().clear();
-
- IOFSwitch sw = floodlightProvider.getSwitches().get(pfm.getDpid());
- if (sw == null) {
- log.warn("Switch not found when pushing delete flow mod");
- continue;
- }
-
- try {
- sw.write(fm, null);
- sw.flush();
- } catch (IOException e) {
- log.error("Failure writing flow mod", e);
- }
+ sendDeleteFlowMod(pfm.getFlowMod(), pfm.getDpid());
}
}
+ private void deletePath(Path path) {
+ for (PushedFlowMod pfm : path.getFlowMods()) {
+ log.debug("Pushing a DELETE flow mod to {}, dst MAC {}",
+ new Object[] {HexString.toHexString(pfm.getDpid()),
+ HexString.toHexString(pfm.getFlowMod().getMatch().getDataLayerDestination())
+ });
+
+ sendDeleteFlowMod(pfm.getFlowMod(), pfm.getDpid());
+ }
+ }
+
+ private void sendDeleteFlowMod(OFFlowMod addFlowMod, long dpid) {
+ addFlowMod.setCommand(OFFlowMod.OFPFC_DELETE_STRICT)
+ .setOutPort(OFPort.OFPP_NONE)
+ .setLengthU(OFFlowMod.MINIMUM_LENGTH);
+
+ addFlowMod.getActions().clear();
+
+ IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
+ if (sw == null) {
+ log.warn("Switch not found when pushing delete flow mod");
+ return;
+ }
+
+ try {
+ sw.write(addFlowMod, null);
+ sw.flush();
+ } catch (IOException e) {
+ log.error("Failure writing flow mod", e);
+ }
+ }
+
+ //TODO test next-hop changes
+ //TODO check delete/add synchronization
+
/*
* On startup we need to calculate a full mesh of paths between all gateway
* switches
@@ -773,32 +666,56 @@
for (BgpPeer peer : bgpPeers.values()) {
Interface peerInterface = interfaces.get(peer.getInterfaceName());
+ //We know there's not already a Path here pushed, because this is
+ //called before all other routing
+ Path path = new Path(peerInterface, peer.getIpAddress());
+ path.setPermanent();
+
//See if we know the MAC address of the peer. If not we can't
//do anything until we learn it
byte[] mac = proxyArp.getMacAddress(peer.getIpAddress());
if (mac == null) {
log.debug("Don't know MAC for {}", peer.getIpAddress().getHostAddress());
//Put in the pending paths list first
- pathsWaitingOnArp.put(peer.getIpAddress(),
- new PathUpdate(peerInterface, peer.getIpAddress()));
+ pathsWaitingOnArp.put(peer.getIpAddress(), path);
proxyArp.sendArpRequest(peer.getIpAddress(), this, true);
continue;
}
//If we know the MAC, lets go ahead and push the paths to this peer
- calculateAndPushPath(peerInterface, MACAddress.valueOf(mac));
+ setUpDataPath(path, MACAddress.valueOf(mac));
}
}
- private void calculateAndPushPath(Interface dstInterface, MACAddress dstMacAddress) {
+ private void setUpDataPath(Path path, MACAddress dstMacAddress) {
+ calculateAndPushPath(path, dstMacAddress);
+ }
+
+ private void calculateAndPushPath(Path path, MACAddress dstMacAddress) {
+ Interface dstInterface = path.getDstInterface();
+
+ log.debug("Setting up path to {}, {}", path.getDstIpAddress().getHostAddress(),
+ dstMacAddress);
+
+ List<PushedFlowMod> pushedFlows = new ArrayList<PushedFlowMod>();
+
for (Interface srcInterface : interfaces.values()) {
if (dstInterface.equals(srcInterface.getName())){
continue;
}
- DataPath shortestPath = topoRouteService.getShortestPath(
- srcInterface.getSwitchPort(), dstInterface.getSwitchPort());
+ DataPath shortestPath;
+ if (topoRouteTopology == null) {
+ log.debug("Using database topo");
+ shortestPath = topoRouteService.getShortestPath(
+ srcInterface.getSwitchPort(), dstInterface.getSwitchPort());
+ }
+ else {
+ log.debug("Using prepared topo");
+ shortestPath = topoRouteService.getTopoShortestPath(topoRouteTopology,
+ srcInterface.getSwitchPort(), dstInterface.getSwitchPort());
+ }
if (shortestPath == null){
log.debug("Shortest path between {} and {} not found",
@@ -806,11 +723,15 @@
return; // just quit here?
}
- installPath(shortestPath.flowEntries(), dstMacAddress);
+ pushedFlows.addAll(installPath(shortestPath.flowEntries(), dstMacAddress));
}
+
+ path.setFlowMods(pushedFlows);
}
- private void installPath(List<FlowEntry> flowEntries, MACAddress dstMacAddress){
+ private List<PushedFlowMod> installPath(List<FlowEntry> flowEntries, MACAddress dstMacAddress){
+ List<PushedFlowMod> flowMods = new ArrayList<PushedFlowMod>();
+
//Set up the flow mod
OFFlowMod fm =
(OFFlowMod) floodlightProvider.getOFMessageFactory()
@@ -835,7 +756,6 @@
FlowEntry flowEntry = flowEntries.get(i);
OFMatch match = new OFMatch();
- //TODO Again using MAC address from configuration
match.setDataLayerDestination(dstMacAddress.toBytes());
match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
((OFActionOutput) fm.getActions().get(0)).setPort(flowEntry.outPort().value());
@@ -849,6 +769,8 @@
continue;
}
+ flowMods.add(new PushedFlowMod(sw.getId(), fm));
+
List<OFMessage> msglist = new ArrayList<OFMessage>();
msglist.add(fm);
try {
@@ -864,6 +786,8 @@
log.error("Failure cloning flow mod", e1);
}
}
+
+ return flowMods;
}
private void setupBgpPaths(){
@@ -908,7 +832,6 @@
//Common match fields
forwardMatchSrc.setDataLayerType(Ethernet.TYPE_IPv4);
- //forwardMatch.setWildcards(forwardMatch.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
forwardMatchSrc.setNetworkProtocol(IPv4.PROTOCOL_TCP);
forwardMatchSrc.setTransportDestination(BGP_PORT);
forwardMatchSrc.setWildcards(forwardMatchSrc.getWildcards() & ~OFMatch.OFPFW_IN_PORT
@@ -1024,30 +947,39 @@
log.debug("Received ARP response: {} => {}", ipAddress.getHostAddress(),
MACAddress.valueOf(macAddress).toString());
- Set<PathUpdate> pathsToPush = pathsWaitingOnArp.removeAll(ipAddress);
-
- for (PathUpdate update : pathsToPush) {
- log.debug("Pushing path to {} at {} on {}", new Object[] {
- update.getDstIpAddress().getHostAddress(),
- MACAddress.valueOf(macAddress),
- update.getDstInterface().getSwitchPort()});
- calculateAndPushPath(update.getDstInterface(),
- MACAddress.valueOf(macAddress));
- }
-
- Set<RibUpdate> prefixesToPush = prefixesWaitingOnArp.removeAll(ipAddress);
-
/*
* We synchronize on this to prevent changes to the ptree while we're pushing
* flows to the switches. If the ptree changes, the ptree and switches
* could get out of sync.
*/
synchronized (this) {
+ Path path = pathsWaitingOnArp.remove(ipAddress);
+
+ if (path != null) {
+ log.debug("Pushing path to {} at {} on {}", new Object[] {
+ path.getDstIpAddress().getHostAddress(),
+ MACAddress.valueOf(macAddress),
+ path.getDstInterface().getSwitchPort()});
+ //These paths should always be to BGP peers. Paths to non-peers are
+ //handled once the first prefix is ready to push
+ if (pushedPaths.containsKey(path.getDstInterface())) {
+ //A path already got pushed to this endpoint while we were waiting
+ //for ARP. We'll copy over the permanent attribute if it is set on this path.
+ if (path.isPermanent()) {
+ pushedPaths.get(path.getDstInterface()).setPermanent();
+ }
+ }
+ else {
+ setUpDataPath(path, MACAddress.valueOf(macAddress));
+ pushedPaths.put(path.getDstIpAddress(), path);
+ }
+ }
+
+ Set<RibUpdate> prefixesToPush = prefixesWaitingOnArp.removeAll(ipAddress);
+
for (RibUpdate update : prefixesToPush) {
//These will always be adds
- //addPrefixFlows(update.getPrefix(), update.getRibEntry());
- //processRibAdd(update);
RibEntry rib = ptree.lookup(update.getPrefix());
if (rib != null && rib.equals(update.getRibEntry())) {
log.debug("Pushing prefix {} next hop {}", update.getPrefix(),
@@ -1056,7 +988,7 @@
//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.
- addPrefixFlows(update.getPrefix(), rib);
+ _processRibAdd(update);
} else {
log.debug("Received ARP response, but {},{} is no longer in ptree",
update.getPrefix(), update.getRibEntry());
@@ -1065,37 +997,59 @@
}
}
+ private void setupArpFlows() {
+ OFMatch match = new OFMatch();
+ match.setDataLayerType(Ethernet.TYPE_ARP);
+ match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
+
+ OFFlowMod fm = new OFFlowMod();
+ fm.setMatch(match);
+
+ OFActionOutput action = new OFActionOutput();
+ action.setPort(OFPort.OFPP_CONTROLLER.getValue());
+ action.setMaxLength((short)0xffff);
+ List<OFAction> actions = new ArrayList<OFAction>(1);
+ actions.add(action);
+ fm.setActions(actions);
+
+ fm.setIdleTimeout((short)0)
+ .setHardTimeout((short)0)
+ .setBufferId(OFPacketOut.BUFFER_ID_NONE)
+ .setCookie(0)
+ .setCommand(OFFlowMod.OFPFC_ADD)
+ .setPriority(ARP_PRIORITY)
+ .setLengthU(OFFlowMod.MINIMUM_LENGTH + OFActionOutput.MINIMUM_LENGTH);
+
+ for (String strdpid : switches){
+ IOFSwitch sw = floodlightProvider.getSwitches().get(HexString.toLong(strdpid));
+ if (sw == null) {
+ log.debug("Couldn't find switch to push ARP flow");
+ }
+ else {
+ try {
+ sw.write(fm, null);
+ } catch (IOException e) {
+ log.warn("Failure writing ARP flow to switch", e);
+ }
+ }
+ }
+ }
+
private void beginRouting(){
log.debug("Topology is now ready, beginning routing function");
+ topoRouteTopology = topoRouteService.prepareShortestPathTopo();
+
+ setupArpFlows();
+
setupBgpPaths();
setupFullMesh();
-
- //Traverse ptree and create flows for all routes
- /*
- for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)){
- if (node.rib != null){
- prefixAdded(node);
- }
- }
- */
-
- /*
- synchronized (ptree) {
- Iterator<IPatriciaTrie.Entry> it = ptree.iterator();
- while (it.hasNext()) {
- IPatriciaTrie.Entry entry = it.next();
- addPrefixFlows(entry.getPrefix(), entry.getRib());
- }
- }
- */
-
+
bgpUpdatesExecutor.execute(new Runnable() {
@Override
public void run() {
doUpdatesThread();
}
});
-
}
private void checkSwitchesConnected(){
@@ -1131,8 +1085,6 @@
}
private void checkStatus(){
- log.debug("In checkStatus, swC {}, toRe {}", switchesConnected, topologyReady);
-
if (!switchesConnected){
checkSwitchesConnected();
}
@@ -1144,19 +1096,7 @@
beginRouting();
}
}
-
- @Override
- public void startUp(FloodlightModuleContext context) {
- restApi.addRestletRoutable(new BgpRouteWebRoutable());
- floodlightProvider.addOFSwitchListener(this);
- topology.addListener(this);
-
- floodlightProvider.addOFMessageListener(OFType.PACKET_IN, proxyArp);
-
- //Retrieve the RIB from BGPd during startup
- retrieveRib();
- }
-
+
private void doUpdatesThread() {
boolean interrupted = false;
try {
@@ -1172,8 +1112,10 @@
break;
}
} catch (InterruptedException e) {
- log.debug("interrupted", e);
+ log.debug("Interrupted while taking from updates queue", e);
interrupted = true;
+ } catch (Exception e) {
+ log.debug("exception", e);
}
}
} finally {
@@ -1184,7 +1126,11 @@
}
@Override
- public void topologyChanged() {
+ public void topologyChanged() {
+ if (topologyReady) {
+ return;
+ }
+
boolean refreshNeeded = false;
for (LDUpdate ldu : topology.getLastLinkUpdates()){
if (!ldu.getOperation().equals(ILinkDiscovery.UpdateOperation.LINK_UPDATED)){
@@ -1202,27 +1148,33 @@
}
}
- if (refreshNeeded){
+ if (refreshNeeded && !topologyReady){
topologyChangeDetectorTask.reschedule(TOPO_DETECTION_WAIT, TimeUnit.SECONDS);
}
}
- //TODO determine whether we need to listen for switch joins
@Override
public void addedSwitch(IOFSwitch sw) {
- //checkStatus();
+ if (!topologyReady) {
+ sw.clearAllFlowMods();
+ }
}
@Override
public void removedSwitch(IOFSwitch sw) {
- // TODO Auto-generated method stub
+ // TODO Auto-generated method stub
+
}
@Override
- public void switchPortChanged(Long switchId) {}
+ public void switchPortChanged(Long switchId) {
+ // TODO Auto-generated method stub
+
+ }
@Override
public String getName() {
- return "BgpRoute";
+ // TODO Auto-generated method stub
+ return null;
}
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRouteResource.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRouteResource.java
index c9b3265..f058843 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRouteResource.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRouteResource.java
@@ -60,7 +60,7 @@
}
else {
//Ptree ptree = bgpRoute.getPtree();
- IPatriciaTrie ptree = bgpRoute.getPtree();
+ IPatriciaTrie<RibEntry> ptree = bgpRoute.getPtree();
output += "{\n \"rib\": [\n";
boolean printed = false;
@@ -78,16 +78,16 @@
}*/
synchronized(ptree) {
- Iterator<IPatriciaTrie.Entry> it = ptree.iterator();
+ Iterator<IPatriciaTrie.Entry<RibEntry>> it = ptree.iterator();
while (it.hasNext()) {
- IPatriciaTrie.Entry entry = it.next();
+ IPatriciaTrie.Entry<RibEntry> entry = it.next();
if (printed == true) {
output += ",\n";
}
output += " {\"prefix\": \"" + entry.getPrefix() +"\", ";
- output += "\"nexthop\": \"" + entry.getRib().getNextHop().getHostAddress() +"\"}";
+ output += "\"nexthop\": \"" + entry.getValue().getNextHop().getHostAddress() +"\"}";
//output += ",\n";
printed = true;
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Configuration.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Configuration.java
index 4b623e4..1d90edc 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Configuration.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Configuration.java
@@ -3,12 +3,15 @@
import java.util.Collections;
import java.util.List;
+import net.floodlightcontroller.util.MACAddress;
+
import org.codehaus.jackson.annotate.JsonProperty;
import org.openflow.util.HexString;
public class Configuration {
private long bgpdAttachmentDpid;
private short bgpdAttachmentPort;
+ private MACAddress bgpdMacAddress;
private List<String> switches;
private List<Interface> interfaces;
private List<BgpPeer> peers;
@@ -34,6 +37,15 @@
public void setBgpdAttachmentPort(short bgpdAttachmentPort) {
this.bgpdAttachmentPort = bgpdAttachmentPort;
}
+
+ public MACAddress getBgpdMacAddress() {
+ return bgpdMacAddress;
+ }
+
+ @JsonProperty("bgpdMacAddress")
+ public void setBgpdMacAddress(String strMacAddress) {
+ this.bgpdMacAddress = MACAddress.valueOf(strMacAddress);
+ }
public List<String> getSwitches() {
return Collections.unmodifiableList(switches);
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/IBgpRouteService.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/IBgpRouteService.java
index ba912ce..954976c 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/IBgpRouteService.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/IBgpRouteService.java
@@ -7,7 +7,7 @@
//public RibEntry lookupRib(byte[] dest);
//public Ptree getPtree();
- public IPatriciaTrie getPtree();
+ public IPatriciaTrie<RibEntry> getPtree();
public String getBGPdRestIp();
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/IPatriciaTrie.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/IPatriciaTrie.java
index 7fd7382..1fb0716 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/IPatriciaTrie.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/IPatriciaTrie.java
@@ -2,19 +2,19 @@
import java.util.Iterator;
-public interface IPatriciaTrie {
- public RibEntry put(Prefix p, RibEntry r);
+public interface IPatriciaTrie<V> {
+ public V put(Prefix prefix, V value);
- public RibEntry lookup(Prefix p);
+ public V lookup(Prefix prefix);
- public RibEntry match(Prefix p);
+ public V match(Prefix prefix);
- public boolean remove(Prefix p, RibEntry r);
+ public boolean remove(Prefix prefix, V value);
- public Iterator<Entry> iterator();
+ public Iterator<Entry<V>> iterator();
- interface Entry {
+ interface Entry<V> {
public Prefix getPrefix();
- public RibEntry getRib();
+ public V getValue();
}
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Path.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Path.java
new file mode 100644
index 0000000..5cf4b09
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Path.java
@@ -0,0 +1,61 @@
+package net.onrc.onos.ofcontroller.bgproute;
+
+import java.net.InetAddress;
+import java.util.Collections;
+import java.util.List;
+
+/*
+ * A path is always assumed to be from all other interfaces (external-facing
+ * switchports) to the destination interface.
+ */
+
+public class Path {
+
+ private Interface dstInterface;
+ private InetAddress dstIpAddress;
+ private int numUsers = 0;
+
+ private List<PushedFlowMod> flowMods = null;
+ private boolean permanent = false;
+
+ public Path(Interface dstInterface, InetAddress dstIpAddress) {
+ this.dstInterface = dstInterface;
+ this.dstIpAddress = dstIpAddress;
+ }
+
+ public Interface getDstInterface() {
+ return dstInterface;
+ }
+
+ public InetAddress getDstIpAddress() {
+ return dstIpAddress;
+ }
+
+ public void incrementUsers() {
+ numUsers++;
+ }
+
+ public void decrementUsers() {
+ numUsers--;
+ }
+
+ public int getUsers() {
+ return numUsers;
+ }
+
+ public List<PushedFlowMod> getFlowMods() {
+ return Collections.unmodifiableList(flowMods);
+ }
+
+ public void setFlowMods(List<PushedFlowMod> flowMods) {
+ this.flowMods = flowMods;
+ }
+
+ public boolean isPermanent() {
+ return permanent;
+ }
+
+ public void setPermanent() {
+ permanent = true;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/PathUpdate.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/PathUpdate.java
deleted file mode 100644
index 1d2a47b..0000000
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/PathUpdate.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package net.onrc.onos.ofcontroller.bgproute;
-
-import java.net.InetAddress;
-
-/*
- * A path is always assumed to be from all other interfaces (external-facing
- * switchports) to the destination interface.
- */
-
-public class PathUpdate {
-
- private Interface dstInterface;
- private InetAddress dstIpAddress;
-
- public PathUpdate(Interface dstInterface, InetAddress dstIpAddress) {
- this.dstInterface = dstInterface;
- this.dstIpAddress = dstIpAddress;
- }
-
- public Interface getDstInterface() {
- return dstInterface;
- }
-
- public InetAddress getDstIpAddress() {
- return dstIpAddress;
- }
-}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/PatriciaTrie.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/PatriciaTrie.java
index 86bc8cf..89dfb30 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/PatriciaTrie.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/PatriciaTrie.java
@@ -3,7 +3,7 @@
import java.util.Iterator;
import java.util.NoSuchElementException;
-public class PatriciaTrie implements IPatriciaTrie{
+public class PatriciaTrie<V> implements IPatriciaTrie<V> {
private final byte maskBits[] = {(byte)0x00, (byte)0x80, (byte)0xc0, (byte)0xe0, (byte)0xf0,
(byte)0xf8, (byte)0xfc, (byte)0xfe, (byte)0xff};
@@ -15,14 +15,15 @@
this.maxPrefixLength = maxPrefixLength;
}
- public synchronized RibEntry put(Prefix p, RibEntry r) {
- if (p.getPrefixLength() > maxPrefixLength) {
+ @Override
+ public synchronized V put(Prefix prefix, V value) {
+ if (prefix.getPrefixLength() > maxPrefixLength) {
throw new IllegalArgumentException(String.format(
"Prefix length %d is greater than max prefix length %d",
- p.getPrefixLength(), maxPrefixLength));
+ prefix.getPrefixLength(), maxPrefixLength));
}
- if (p == null || r == null) {
+ if (prefix == null || value == null) {
throw new NullPointerException();
}
@@ -30,23 +31,23 @@
Node match = null;
while (node != null
- && node.prefix.getPrefixLength() <= p.getPrefixLength()
- && key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), p.getAddress(), p.getPrefixLength()) == true) {
- if (node.prefix.getPrefixLength() == p.getPrefixLength()) {
+ && node.prefix.getPrefixLength() <= prefix.getPrefixLength()
+ && key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), prefix.getAddress(), prefix.getPrefixLength()) == true) {
+ if (node.prefix.getPrefixLength() == prefix.getPrefixLength()) {
/*
* Prefix is already in tree. This may be an aggregate node, in which case
* we are inserting a new prefix, or it could be an actual node, in which
* case we are inserting a new nexthop for the prefix and should return
* the old nexthop.
*/
- RibEntry oldRib = node.rib;
- node.rib = r;
- return oldRib;
+ V oldValue = node.value;
+ node.value = value;
+ return oldValue;
}
match = node;
- if (bit_check(p.getAddress(), node.prefix.getPrefixLength()) == true) {
+ if (bit_check(prefix.getAddress(), node.prefix.getPrefixLength()) == true) {
node = node.right;
} else {
node = node.left;
@@ -56,7 +57,9 @@
Node add = null;
if (node == null) {
- add = new Node(p, r);
+ //add = new Node(p, r);
+ add = new Node(prefix);
+ add.value = value;
if (match != null) {
node_link(match, add);
@@ -64,7 +67,7 @@
top = add;
}
} else {
- add = node_common(node, p.getAddress(), p.getPrefixLength());
+ add = node_common(node, prefix.getAddress(), prefix.getPrefixLength());
if (add == null) {
//I think this is -ENOMEM?
//return null;
@@ -77,14 +80,16 @@
}
node_link(add, node);
- if (add.prefix.getPrefixLength() != p.getPrefixLength()) {
+ if (add.prefix.getPrefixLength() != prefix.getPrefixLength()) {
match = add;
- add = new Node(p, r);
+ //add = new Node(p, r);
+ add = new Node(prefix);
+ add.value = value;
node_link(match, add);
}
else {
- add.rib = r;
+ add.value = value;
}
}
@@ -94,10 +99,9 @@
}
/*exact match*/
- public synchronized RibEntry lookup(Prefix p) {
- //TODO
-
- if (p.getPrefixLength() > maxPrefixLength) {
+ @Override
+ public synchronized V lookup(Prefix prefix) {
+ if (prefix.getPrefixLength() > maxPrefixLength) {
return null;
}
@@ -120,28 +124,36 @@
}
*/
- Node node = findNode(p);
+ Node node = findNode(prefix);
- return node == null ? null : node.rib;
+ return node == null ? null : node.value;
}
/*closest containing prefix*/
- public synchronized RibEntry match(Prefix p) {
+ @Override
+ public synchronized V match(Prefix prefix) {
//TODO
- return null;
+ if (prefix.getPrefixLength() > maxPrefixLength) {
+ return null;
+ }
+
+ Node closestNode = findClosestNode(prefix);
+
+ return closestNode == null ? null : closestNode.value;
}
- public synchronized boolean remove(Prefix p, RibEntry r) {
+ @Override
+ public synchronized boolean remove(Prefix prefix, V value) {
Node child;
Node parent;
- if (p == null || r == null) {
+ if (prefix == null || value == null) {
return false;
}
- Node node = findNode(p);
+ Node node = findNode(prefix);
- if (node == null || node.rib == null || !node.rib.equals(r)) {
+ if (node == null || node.isAggregate() || !node.value.equals(value)) {
//Given <prefix, nexthop> mapping is not in the tree
return false;
}
@@ -151,7 +163,7 @@
//In the future, maybe we should re-evaluate what the aggregate prefix should be?
//It shouldn't necessarily stay the same.
//More complicated if the above prefix is also aggregate.
- node.rib = null;
+ node.value = null;
return true;
}
@@ -192,22 +204,23 @@
return true;
}
- public Iterator<Entry> iterator() {
+ @Override
+ public Iterator<Entry<V>> iterator() {
return new PatriciaTrieIterator(top);
}
- private Node findNode(Prefix p) {
+ private Node findNode(Prefix prefix) {
Node node = top;
while (node != null
- && node.prefix.getPrefixLength() <= p.getPrefixLength()
- && key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), p.getAddress(), p.getPrefixLength()) == true) {
- if (node.prefix.getPrefixLength() == p.getPrefixLength()) {
+ && node.prefix.getPrefixLength() <= prefix.getPrefixLength()
+ && key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), prefix.getAddress(), prefix.getPrefixLength()) == true) {
+ if (node.prefix.getPrefixLength() == prefix.getPrefixLength()) {
//return addReference(node);
return node;
}
- if (bit_check(p.getAddress(), node.prefix.getPrefixLength()) == true) {
+ if (bit_check(prefix.getAddress(), node.prefix.getPrefixLength()) == true) {
node = node.right;
} else {
node = node.left;
@@ -217,6 +230,27 @@
return null;
}
+ private Node findClosestNode(Prefix prefix) {
+ Node node = top;
+ Node match = null;
+
+ while (node != null
+ && node.prefix.getPrefixLength() <= prefix.getPrefixLength()
+ && key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), prefix.getAddress(), prefix.getPrefixLength()) == true) {
+ if (!node.isAggregate()) {
+ match = node;
+ }
+
+ if (bit_check(prefix.getAddress(), node.prefix.getPrefixLength()) == true) {
+ node = node.right;
+ } else {
+ node = node.left;
+ }
+ }
+
+ return match;
+ }
+
/*
* Receives a 1-based bit index
* Returns a 1-based byte index
@@ -325,7 +359,8 @@
if (boundary != 0)
newPrefix[j] = (byte)(node.prefix.getAddress()[j] & maskBits[common_len % 8]);
- return new Node(new Prefix(newPrefix, common_len), null);
+ //return new Node(new Prefix(newPrefix, common_len), null);
+ return new Node(new Prefix(newPrefix, common_len));
//return add;
}
@@ -334,26 +369,33 @@
public Node left = null;
public Node right = null;
- public Prefix prefix;
- public RibEntry rib;
+ public final Prefix prefix;
+ public V value;
- public Node(Prefix p, RibEntry r) {
+ //public Node(Prefix p, RibEntry r) {
+ // this.prefix = p;
+ // this.rib = r;
+ //}
+ public Node(Prefix p) {
this.prefix = p;
- this.rib = r;
}
- public Entry getEntry() {
- return new PatriciaTrieEntry(prefix, rib);
+ public boolean isAggregate() {
+ return value == null;
+ }
+
+ public Entry<V> getEntry() {
+ return new PatriciaTrieEntry(prefix, value);
}
}
- private class PatriciaTrieEntry implements Entry {
+ private class PatriciaTrieEntry implements Entry<V> {
private Prefix prefix;
- private RibEntry rib;
+ private V value;
- public PatriciaTrieEntry(Prefix prefix, RibEntry rib) {
+ public PatriciaTrieEntry(Prefix prefix, V value) {
this.prefix = prefix;
- this.rib = rib;
+ this.value = value;
}
@Override
@@ -362,12 +404,12 @@
}
@Override
- public RibEntry getRib() {
- return rib;
+ public V getValue() {
+ return value;
}
}
- private class PatriciaTrieIterator implements Iterator<Entry> {
+ private class PatriciaTrieIterator implements Iterator<Entry<V>> {
private Node current;
private boolean started = false;
@@ -376,7 +418,7 @@
current = start;
//If the start is an aggregate node fast forward to find the next valid node
- if (current != null && current.rib == null) {
+ if (current != null && current.isAggregate()) {
current = findNext(current);
}
}
@@ -395,7 +437,7 @@
}
@Override
- public Entry next() {
+ public Entry<V> next() {
if (current == null) {
throw new NoSuchElementException();
}
@@ -452,9 +494,9 @@
return null;
}
- //If the node doesn't have a rib, it's not an actual node, it's an artifically
+ //If the node doesn't have a value, it's not an actual node, it's an artifically
//inserted aggregate node. We don't want to return these to the user.
- if (next.rib == null) {
+ if (next.isAggregate()) {
return findNext(next);
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Prefix.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Prefix.java
index 21dd45c..05ce0a4 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Prefix.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Prefix.java
@@ -4,6 +4,8 @@
import java.net.UnknownHostException;
import java.util.Arrays;
+import com.google.common.net.InetAddresses;
+
public class Prefix {
private final int MAX_BYTES = 4;
@@ -31,11 +33,7 @@
public Prefix(String strAddress, int prefixLength) {
byte[] addr = null;
- try {
- addr = InetAddress.getByName(strAddress).getAddress();
- } catch (UnknownHostException e) {
- throw new IllegalArgumentException("Invalid IP inetAddress argument");
- }
+ addr = InetAddresses.forString(strAddress).getAddress();
if (addr == null || addr.length != MAX_BYTES ||
prefixLength < 0 || prefixLength > MAX_BYTES * Byte.SIZE) {
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/PushedFlowMod.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/PushedFlowMod.java
new file mode 100644
index 0000000..56f4c04
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/PushedFlowMod.java
@@ -0,0 +1,25 @@
+package net.onrc.onos.ofcontroller.bgproute;
+
+import org.openflow.protocol.OFFlowMod;
+
+public class PushedFlowMod {
+ private long dpid;
+ private OFFlowMod flowMod;
+
+ public PushedFlowMod(long dpid, OFFlowMod flowMod) {
+ this.dpid = dpid;
+ try {
+ this.flowMod = flowMod.clone();
+ } catch (CloneNotSupportedException e) {
+ this.flowMod = flowMod;
+ }
+ }
+
+ public long getDpid() {
+ return dpid;
+ }
+
+ public OFFlowMod getFlowMod() {
+ return flowMod;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/RibEntry.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/RibEntry.java
index c27f962..8087471 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/RibEntry.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/RibEntry.java
@@ -1,7 +1,8 @@
package net.onrc.onos.ofcontroller.bgproute;
import java.net.InetAddress;
-import java.net.UnknownHostException;
+
+import com.google.common.net.InetAddresses;
public class RibEntry {
private InetAddress routerId;
@@ -13,12 +14,8 @@
}
public RibEntry(String routerId, String nextHop) {
- try {
- this.routerId = InetAddress.getByName(routerId);
- this.nextHop = InetAddress.getByName(nextHop);
- } catch (UnknownHostException e) {
- throw new IllegalArgumentException("Invalid address format");
- }
+ this.routerId = InetAddresses.forString(routerId);
+ this.nextHop = InetAddresses.forString(nextHop);
}
public InetAddress getNextHop() {
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
index a072882..bca9ef7 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
@@ -97,11 +97,6 @@
private static String measurementFlowIdStr = "0x186a0"; // 100000
private long modifiedMeasurementFlowTime = 0;
//
- private LinkedList<FlowPath> measurementStoredPaths = new LinkedList<FlowPath>();
- private long measurementStartTimeProcessingPaths = 0;
- private long measurementEndTimeProcessingPaths = 0;
- Map<Long, ?> measurementShortestPathTopo = null;
- private String measurementPerFlowStr = new String();
/** The logger. */
private static Logger log = LoggerFactory.getLogger(FlowManager.class);
@@ -2305,207 +2300,4 @@
//
return (installRemoteFlowEntry(flowPath, flowEntry));
}
-
- /**
- * Store a path flow for measurement purpose.
- *
- * NOTE: The Flow Path argument does NOT contain flow entries.
- * The Shortest Path is computed, and the corresponding Flow Entries
- * are stored in the Flow Path.
- *
- * @param flowPath the Flow Path with the endpoints and the match
- * conditions to store.
- * @return the stored shortest-path flow on success, otherwise null.
- */
- @Override
- public synchronized FlowPath measurementStorePathFlow(FlowPath flowPath) {
- //
- // Prepare the Shortest Path computation if the first Flow Path
- //
- if (measurementStoredPaths.isEmpty())
- measurementShortestPathTopo = topoRouteService.prepareShortestPathTopo();
-
- //
- // Compute the Shortest Path
- //
- DataPath dataPath =
- topoRouteService.getTopoShortestPath(measurementShortestPathTopo,
- flowPath.dataPath().srcPort(),
- flowPath.dataPath().dstPort());
- if (dataPath == null) {
- // We need the DataPath to populate the Network MAP
- dataPath = new DataPath();
- dataPath.setSrcPort(flowPath.dataPath().srcPort());
- dataPath.setDstPort(flowPath.dataPath().dstPort());
- }
- dataPath.applyFlowPathFlags(flowPath.flowPathFlags());
-
- //
- // Set the incoming port matching and the outgoing port output
- // actions for each flow entry.
- //
- for (FlowEntry flowEntry : dataPath.flowEntries()) {
- // Set the incoming port matching
- FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
- flowEntry.setFlowEntryMatch(flowEntryMatch);
- flowEntryMatch.enableInPort(flowEntry.inPort());
-
- // Set the outgoing port output action
- FlowEntryActions flowEntryActions = flowEntry.flowEntryActions();
- FlowEntryAction flowEntryAction = new FlowEntryAction();
- flowEntryAction.setActionOutput(flowEntry.outPort());
- flowEntryActions.addAction(flowEntryAction);
- }
-
- //
- // Prepare the computed Flow Path
- //
- FlowPath computedFlowPath = new FlowPath();
- computedFlowPath.setFlowId(new FlowId(flowPath.flowId().value()));
- computedFlowPath.setInstallerId(new CallerId(flowPath.installerId().value()));
- computedFlowPath.setFlowPathFlags(new FlowPathFlags(flowPath.flowPathFlags().flags()));
- computedFlowPath.setDataPath(dataPath);
- computedFlowPath.setFlowEntryMatch(new FlowEntryMatch(flowPath.flowEntryMatch()));
-
- //
- // Add the computed Flow Path to the internal storage
- //
- measurementStoredPaths.add(computedFlowPath);
-
- log.debug("Measurement storing path {}",
- computedFlowPath.flowId().toString());
-
- return (computedFlowPath);
- }
-
- /**
- * Install path flows for measurement purpose.
- *
- * @param numThreads the number of threads to use to install the path
- * flows.
- * @return true on success, otherwise false.
- */
- @Override
- public boolean measurementInstallPaths(Integer numThreads) {
- // Create a copy of the Flow Paths to install
- final ConcurrentLinkedQueue<FlowPath> measurementProcessingPaths =
- new ConcurrentLinkedQueue<FlowPath>(measurementStoredPaths);
-
- /**
- * A Thread-wrapper class for executing the threads and collecting
- * the measurement data.
- */
- class MyThread extends Thread {
- public long[] execTime = new long[2000];
- public int samples = 0;
- public int threadId = -1;
- @Override
- public void run() {
- while (true) {
- FlowPath flowPath = measurementProcessingPaths.poll();
- if (flowPath == null)
- return;
- // Install the Flow Path
- FlowId flowId = new FlowId();
- String dataPathSummaryStr =
- flowPath.dataPath().dataPathSummary();
- long startTime = System.nanoTime();
- addFlow(flowPath, flowId, dataPathSummaryStr);
- long endTime = System.nanoTime();
- execTime[samples] = endTime - startTime;
- samples++;
- }
- }
- };
-
- List<MyThread> threads = new LinkedList<MyThread>();
-
- log.debug("Measurement Installing {} flows",
- measurementProcessingPaths.size());
-
- //
- // Create the threads to install the Flow Paths
- //
- for (int i = 0; i < numThreads; i++) {
- MyThread thread = new MyThread();
- thread.threadId = i;
- threads.add(thread);
- }
-
- //
- // Start processing
- //
- measurementEndTimeProcessingPaths = 0;
- measurementStartTimeProcessingPaths = System.nanoTime();
- for (Thread thread : threads) {
- thread.start();
- }
-
- // Wait for all threads to complete
- for (Thread thread : threads) {
- try {
- thread.join();
- } catch (InterruptedException e) {
- log.debug("Exception waiting for a thread to install a Flow Path: ", e);
- }
- }
-
- // Record the end of processing
- measurementEndTimeProcessingPaths = System.nanoTime();
-
- //
- // Prepare the string with measurement data per each Flow Path
- // installation.
- // The string is multiple lines: one line per Flow Path installation:
- // ThreadAndTimePerFlow <ThreadId> <TotalThreads> <Time(ns)>
- //
- measurementPerFlowStr = new String();
- String eol = System.getProperty("line.separator");
- for (MyThread thread : threads) {
- for (int i = 0; i < thread.samples; i++) {
- measurementPerFlowStr += "ThreadAndTimePerFlow " + thread.threadId + " " + numThreads + " " + thread.execTime[i] + eol;
- }
- }
-
- return true;
- }
-
- /**
- * Get the measurement time that took to install the path flows.
- *
- * @return the measurement time (in nanoseconds) it took to install
- * the path flows.
- */
- @Override
- public Long measurementGetInstallPathsTimeNsec() {
- return new Long(measurementEndTimeProcessingPaths -
- measurementStartTimeProcessingPaths);
- }
-
- /**
- * Get the measurement install time per Flow.
- *
- * @return a multi-line string with the following format per line:
- * ThreadAndTimePerFlow <ThreadId> <TotalThreads> <Time(ns)>
- */
- @Override
- public String measurementGetPerFlowInstallTime() {
- return new String(measurementPerFlowStr);
- }
-
- /**
- * Clear the path flows stored for measurement purpose.
- *
- * @return true on success, otherwise false.
- */
- @Override
- public boolean measurementClearAllPaths() {
- measurementStoredPaths.clear();
- topoRouteService.dropShortestPathTopo(measurementShortestPathTopo);
- measurementStartTimeProcessingPaths = 0;
- measurementEndTimeProcessingPaths = 0;
- measurementPerFlowStr = new String();
-
- return true;
- }
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IFlowService.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IFlowService.java
index ba9cd1b..0fbb23c 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IFlowService.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IFlowService.java
@@ -115,47 +115,4 @@
* @return the added shortest-path flow on success, otherwise null.
*/
public FlowPath addAndMaintainShortestPathFlow(FlowPath flowPath);
-
- /**
- * Store a path flow for measurement purpose.
- *
- * NOTE: The Flow Path argument does NOT contain flow entries.
- *
- * @param flowPath the Flow Path with the endpoints and the match
- * conditions to store.
- * @return the stored shortest-path flow on success, otherwise null.
- */
- public FlowPath measurementStorePathFlow(FlowPath flowPath);
-
- /**
- * Install path flows for measurement purpose.
- *
- * @param numThreads the number of threads to use to install the path
- * flows.
- * @return true on success, otherwise false.
- */
- public boolean measurementInstallPaths(Integer numThreads);
-
- /**
- * Get the measurement time that took to install the path flows.
- *
- * @return the measurement time (in nanoseconds) it took to install
- * the path flows.
- */
- public Long measurementGetInstallPathsTimeNsec();
-
- /**
- * Get the measurement install time per Flow.
- *
- * @return a multi-line string with the following format per line:
- * ThreadAndTimePerFlow <ThreadId> <TotalThreads> <Time(ns)>
- */
- public String measurementGetPerFlowInstallTime();
-
- /**
- * Clear the path flows stored for measurement purpose.
- *
- * @return true on success, otherwise false.
- */
- public boolean measurementClearAllPaths();
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/FlowWebRoutable.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/FlowWebRoutable.java
index 954c84d..e1c6da9 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/FlowWebRoutable.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/FlowWebRoutable.java
@@ -22,11 +22,6 @@
router.attach("/getall-by-endpoints/{src-dpid}/{src-port}/{dst-dpid}/{dst-port}/json", GetAllFlowsByEndpointsResource.class);
router.attach("/getall/json", GetAllFlowsResource.class);
router.attach("/getsummary/{flow-id}/{max-flows}/json", GetSummaryFlowsResource.class);
- router.attach("/measurement-store-path/json", MeasurementStorePathFlowResource.class);
- router.attach("/measurement-install-paths/{num-threads}/json", MeasurementInstallPathsFlowResource.class);
- router.attach("/measurement-get-install-paths-time-nsec/json", MeasurementGetInstallPathsTimeNsecFlowResource.class);
- router.attach("/measurement-get-per-flow-install-time/json", MeasurementGetPerFlowInstallTimeFlowResource.class);
- router.attach("/measurement-clear-all-paths/json", MeasurementClearAllPathsFlowResource.class);
return router;
}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementClearAllPathsFlowResource.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementClearAllPathsFlowResource.java
deleted file mode 100644
index 07d9fb2..0000000
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementClearAllPathsFlowResource.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package net.onrc.onos.ofcontroller.flowmanager.web;
-
-import net.onrc.onos.ofcontroller.flowmanager.IFlowService;
-
-import org.restlet.resource.Get;
-import org.restlet.resource.ServerResource;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class MeasurementClearAllPathsFlowResource extends ServerResource {
- protected static Logger log = LoggerFactory.getLogger(MeasurementClearAllPathsFlowResource.class);
-
- @Get("json")
- public Boolean retrieve() {
- Boolean result = false;
-
- IFlowService flowService =
- (IFlowService)getContext().getAttributes().
- get(IFlowService.class.getCanonicalName());
-
- if (flowService == null) {
- log.debug("ONOS Flow Service not found");
- return result;
- }
-
- // Extract the arguments
- log.debug("Measurement Clear All Paths");
-
- // Process the request
- result = flowService.measurementClearAllPaths();
- return result;
- }
-}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementGetInstallPathsTimeNsecFlowResource.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementGetInstallPathsTimeNsecFlowResource.java
deleted file mode 100644
index 467afca..0000000
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementGetInstallPathsTimeNsecFlowResource.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package net.onrc.onos.ofcontroller.flowmanager.web;
-
-import net.onrc.onos.ofcontroller.flowmanager.IFlowService;
-
-import org.restlet.resource.Get;
-import org.restlet.resource.ServerResource;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class MeasurementGetInstallPathsTimeNsecFlowResource extends ServerResource {
- protected static Logger log = LoggerFactory.getLogger(MeasurementGetInstallPathsTimeNsecFlowResource.class);
-
- @Get("json")
- public Long retrieve() {
- Long result = null;
-
- IFlowService flowService =
- (IFlowService)getContext().getAttributes().
- get(IFlowService.class.getCanonicalName());
-
- if (flowService == null) {
- log.debug("ONOS Flow Service not found");
- return result;
- }
-
- // Extract the arguments
-
- // Process the request
- result = flowService.measurementGetInstallPathsTimeNsec();
-
- log.debug("Measurement Get Install Paths Time (nsec): " + result);
-
- return result;
- }
-}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementGetPerFlowInstallTimeFlowResource.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementGetPerFlowInstallTimeFlowResource.java
deleted file mode 100644
index 92d84ab..0000000
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementGetPerFlowInstallTimeFlowResource.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package net.onrc.onos.ofcontroller.flowmanager.web;
-
-import net.onrc.onos.ofcontroller.flowmanager.IFlowService;
-
-import org.restlet.resource.Get;
-import org.restlet.resource.ServerResource;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class MeasurementGetPerFlowInstallTimeFlowResource extends ServerResource {
- protected static Logger log = LoggerFactory.getLogger(MeasurementGetPerFlowInstallTimeFlowResource.class);
-
- @Get("json")
- public String retrieve() {
- String result = null;
-
- IFlowService flowService =
- (IFlowService)getContext().getAttributes().
- get(IFlowService.class.getCanonicalName());
-
- if (flowService == null) {
- log.debug("ONOS Flow Service not found");
- return result;
- }
-
- // Extract the arguments
-
- // Process the request
- result = flowService.measurementGetPerFlowInstallTime();
-
- log.debug("Measurement Get Install Paths Time (nsec): " + result);
-
- return result;
- }
-}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementInstallPathsFlowResource.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementInstallPathsFlowResource.java
deleted file mode 100644
index 074dfb4..0000000
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementInstallPathsFlowResource.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package net.onrc.onos.ofcontroller.flowmanager.web;
-
-import net.onrc.onos.ofcontroller.flowmanager.IFlowService;
-
-import org.restlet.resource.Get;
-import org.restlet.resource.ServerResource;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class MeasurementInstallPathsFlowResource extends ServerResource {
- protected static Logger log = LoggerFactory.getLogger(MeasurementInstallPathsFlowResource.class);
-
- @Get("json")
- public Boolean retrieve() {
- Boolean result = false;
-
- IFlowService flowService =
- (IFlowService)getContext().getAttributes().
- get(IFlowService.class.getCanonicalName());
-
- if (flowService == null) {
- log.debug("ONOS Flow Service not found");
- return result;
- }
-
- // Extract the arguments
- String numThreadsStr = (String) getRequestAttributes().get("num-threads");
- Integer numThreads = new Integer(numThreadsStr);
- log.debug("Measurement Install Paths Number of Threads " + numThreadsStr);
-
- // Process the request
- result = flowService.measurementInstallPaths(numThreads);
- return result;
- }
-}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementStorePathFlowResource.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementStorePathFlowResource.java
deleted file mode 100644
index 0f23663..0000000
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementStorePathFlowResource.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package net.onrc.onos.ofcontroller.flowmanager.web;
-
-import java.io.IOException;
-
-import net.onrc.onos.ofcontroller.flowmanager.IFlowService;
-import net.onrc.onos.ofcontroller.util.FlowId;
-import net.onrc.onos.ofcontroller.util.FlowPath;
-
-import org.codehaus.jackson.JsonGenerationException;
-import org.codehaus.jackson.map.JsonMappingException;
-import org.codehaus.jackson.map.ObjectMapper;
-import org.restlet.resource.Post;
-import org.restlet.resource.ServerResource;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class MeasurementStorePathFlowResource extends ServerResource {
-
- protected static Logger log = LoggerFactory.getLogger(MeasurementStorePathFlowResource.class);
-
- @Post("json")
- public FlowId store(String flowJson) {
- FlowId result = new FlowId();
-
- IFlowService flowService =
- (IFlowService)getContext().getAttributes().
- get(IFlowService.class.getCanonicalName());
-
- if (flowService == null) {
- log.debug("ONOS Flow Service not found");
- return result;
- }
-
- //
- // Extract the arguments
- // NOTE: The "flow" is specified in JSON format.
- //
- ObjectMapper mapper = new ObjectMapper();
- String flowPathStr = flowJson;
- FlowPath flowPath = null;
- log.debug("Measurement Store Flow Path: " + flowPathStr);
- try {
- flowPath = mapper.readValue(flowPathStr, FlowPath.class);
- } catch (JsonGenerationException e) {
- e.printStackTrace();
- } catch (JsonMappingException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- // Process the request
- if (flowPath != null) {
- FlowPath addedFlowPath =
- flowService.measurementStorePathFlow(flowPath);
- if (addedFlowPath == null)
- result = new FlowId(); // Error: Return empty Flow Id
- else
- result = addedFlowPath.flowId();
- }
-
- return result;
- }
-}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
index 521ba0f..493d58e 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
@@ -17,11 +17,13 @@
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.IOFMessageListener;
import net.floodlightcontroller.core.IOFSwitch;
-import net.floodlightcontroller.devicemanager.IDeviceService;
import net.floodlightcontroller.packet.ARP;
import net.floodlightcontroller.packet.Ethernet;
import net.floodlightcontroller.topology.ITopologyService;
import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.ofcontroller.bgproute.IPatriciaTrie;
+import net.onrc.onos.ofcontroller.bgproute.Interface;
+import net.onrc.onos.ofcontroller.bgproute.Prefix;
import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFPacketIn;
@@ -39,6 +41,7 @@
import com.google.common.collect.SetMultimap;
//TODO have L2 and also L3 mode, where it takes into account interface addresses
+//TODO REST API to inspect ARP table
public class ProxyArpManager implements IProxyArpService, IOFMessageListener {
private static Logger log = LoggerFactory.getLogger(ProxyArpManager.class);
@@ -48,12 +51,19 @@
protected IFloodlightProviderService floodlightProvider;
protected ITopologyService topology;
- protected IDeviceService devices;
protected Map<InetAddress, ArpTableEntry> arpTable;
protected SetMultimap<InetAddress, ArpRequest> arpRequests;
+ public enum Mode {L2_MODE, L3_MODE}
+
+ private Mode mode;
+ private IPatriciaTrie<Interface> interfacePtrie = null;
+ private Collection<Interface> interfaces = null;
+ private MACAddress routerMacAddress = null;
+ //private SwitchPort bgpdAttachmentPoint = null;
+
private class ArpRequest {
private IArpRequester requester;
private boolean retry;
@@ -88,16 +98,28 @@
}
public ProxyArpManager(IFloodlightProviderService floodlightProvider,
- ITopologyService topology, IDeviceService devices){
+ ITopologyService topology){
this.floodlightProvider = floodlightProvider;
this.topology = topology;
- this.devices = devices;
arpTable = new HashMap<InetAddress, ArpTableEntry>();
arpRequests = Multimaps.synchronizedSetMultimap(
HashMultimap.<InetAddress, ArpRequest>create());
+ mode = Mode.L2_MODE;
+ }
+
+ public void setL3Mode(IPatriciaTrie<Interface> interfacePtrie,
+ Collection<Interface> interfaces, MACAddress routerMacAddress) {
+ this.interfacePtrie = interfacePtrie;
+ this.interfaces = interfaces;
+ this.routerMacAddress = routerMacAddress;
+
+ mode = Mode.L3_MODE;
+ }
+
+ public void startUp() {
Timer arpTimer = new Timer();
arpTimer.scheduleAtFixedRate(new TimerTask() {
@Override
@@ -203,43 +225,77 @@
}
protected void handleArpRequest(IOFSwitch sw, OFPacketIn pi, ARP arp) {
- log.debug("ARP request received for {}",
+ log.trace("ARP request received for {}",
bytesToStringAddr(arp.getTargetProtocolAddress()));
+
+ InetAddress target;
+ InetAddress source;
+ try {
+ target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
+ source = InetAddress.getByAddress(arp.getSenderProtocolAddress());
+ } catch (UnknownHostException e) {
+ log.debug("Invalid address in ARP request", e);
+ return;
+ }
+
+ if (mode == Mode.L3_MODE) {
+
+ //if (originatedOutsideNetwork(source)) {
+ if (originatedOutsideNetwork(sw.getId(), pi.getInPort())) {
+ //If the request came from outside our network, we only care if
+ //it was a request for one of our interfaces.
+ if (isInterfaceAddress(target)) {
+ log.trace("ARP request for our interface. Sending reply {} => {}",
+ target.getHostAddress(), routerMacAddress.toString());
+ sendArpReply(arp, sw.getId(), pi.getInPort(), routerMacAddress.toBytes());
+ }
+ return;
+ }
+
+ /*
+ Interface intf = interfacePtrie.match(new Prefix(target.getAddress(), 32));
+ //if (intf != null && target.equals(intf.getIpAddress())) {
+ if (intf != null) {
+ if (target.equals(intf.getIpAddress())) {
+ //ARP request for one of our interfaces, we can reply straight away
+ sendArpReply(arp, sw.getId(), pi.getInPort(), routerMacAddress.toBytes());
+ }
+ // If we didn't enter the above if block, then we found a matching
+ // interface for the target IP but the request wasn't for us.
+ // This is someone else ARPing for a different host in the subnet.
+ // We shouldn't do anything in this case - if we let processing continue
+ // we'll end up erroneously re-broadcasting an ARP for someone else.
+ return;
+ }
+ */
+ }
byte[] mac = lookupArpTable(arp.getTargetProtocolAddress());
if (mac == null){
//Mac address is not in our arp table.
- //TODO check what the DeviceManager thinks
-
//Record where the request came from so we know where to send the reply
- InetAddress target;
- try {
- target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
- } catch (UnknownHostException e) {
- log.debug("Invalid address in ARP request", e);
- //return Command.CONTINUE; //Continue or stop?
- return;
- }
-
- //Should we just broadcast all received requests here? Or rate limit
- //if we know we just sent an request?
arpRequests.put(target, new ArpRequest(
new HostArpRequester(this, arp, sw.getId(), pi.getInPort()), false));
//Flood the request out edge ports
- broadcastArpRequestOutEdge(pi.getPacketData(), sw.getId(), pi.getInPort());
+ //broadcastArpRequestOutEdge(pi.getPacketData(), sw.getId(), pi.getInPort());
+ sendArpRequestToSwitches(target, pi.getPacketData(), sw.getId(), pi.getInPort());
}
else {
//We know the address, so send a reply
- log.debug("Sending reply of {}", MACAddress.valueOf(mac).toString());
+ log.trace("Sending reply: {} => {} to host at {}/{}", new Object [] {
+ bytesToStringAddr(arp.getTargetProtocolAddress()),
+ MACAddress.valueOf(mac).toString(),
+ HexString.toHexString(sw.getId()), pi.getInPort()});
+
sendArpReply(arp, sw.getId(), pi.getInPort(), mac);
}
}
protected void handleArpReply(IOFSwitch sw, OFPacketIn pi, ARP arp){
- log.debug("ARP reply recieved for {}, is {}, on {}/{}", new Object[] {
+ log.trace("ARP reply recieved: {} => {}, on {}/{}", new Object[] {
bytesToStringAddr(arp.getSenderProtocolAddress()),
HexString.toHexString(arp.getSenderHardwareAddress()),
HexString.toHexString(sw.getId()), pi.getInPort()});
@@ -257,14 +313,21 @@
Set<ArpRequest> requests = arpRequests.get(addr);
//Synchronize on the Multimap while using an iterator for one of the sets
+ List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(requests.size());
synchronized (arpRequests) {
Iterator<ArpRequest> it = requests.iterator();
while (it.hasNext()) {
ArpRequest request = it.next();
it.remove();
- request.dispatchReply(addr, arp.getSenderHardwareAddress());
+ //request.dispatchReply(addr, arp.getSenderHardwareAddress());
+ requestsToSend.add(request);
}
}
+
+ //Don't hold an ARP lock while dispatching requests
+ for (ArpRequest request : requestsToSend) {
+ request.dispatchReply(addr, arp.getSenderHardwareAddress());
+ }
}
private synchronized byte[] lookupArpTable(byte[] ipAddress){
@@ -279,7 +342,7 @@
ArpTableEntry arpEntry = arpTable.get(addr);
if (arpEntry == null){
- log.debug("MAC for {} unknown", bytesToStringAddr(ipAddress));
+ //log.debug("MAC for {} unknown", bytesToStringAddr(ipAddress));
return null;
}
@@ -320,7 +383,7 @@
//TODO what should the sender IP address be? Probably not 0.0.0.0
byte[] zeroIpv4 = {0x0, 0x0, 0x0, 0x0};
byte[] zeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
- byte[] bgpdMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x01};
+ //byte[] bgpdMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x01};
byte[] broadcastMac = {(byte)0xff, (byte)0xff, (byte)0xff,
(byte)0xff, (byte)0xff, (byte)0xff};
@@ -331,19 +394,54 @@
.setHardwareAddressLength((byte)Ethernet.DATALAYER_ADDRESS_LENGTH)
.setProtocolAddressLength((byte)4) //can't find the constant anywhere
.setOpCode(ARP.OP_REQUEST)
- .setSenderHardwareAddress(bgpdMac)
- .setSenderProtocolAddress(zeroIpv4)
+ //.setSenderHardwareAddress(bgpdMac)
+ .setSenderHardwareAddress(routerMacAddress.toBytes())
+ //.setSenderProtocolAddress(zeroIpv4)
.setTargetHardwareAddress(zeroMac)
.setTargetProtocolAddress(ipAddress.getAddress());
-
+
+ byte[] senderIPAddress = zeroIpv4;
+ if (mode == Mode.L3_MODE) {
+ Interface intf = interfacePtrie.match(new Prefix(ipAddress.getAddress(), 32));
+ if (intf != null) {
+ senderIPAddress = intf.getIpAddress().getAddress();
+ }
+ }
+
+ arpRequest.setSenderProtocolAddress(senderIPAddress);
+
Ethernet eth = new Ethernet();
- //eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
- eth.setSourceMACAddress(bgpdMac)
+ eth.setSourceMACAddress(routerMacAddress.toBytes())
.setDestinationMACAddress(broadcastMac)
.setEtherType(Ethernet.TYPE_ARP)
.setPayload(arpRequest);
- broadcastArpRequestOutEdge(eth.serialize(), 0, OFPort.OFPP_NONE.getValue());
+ //broadcastArpRequestOutEdge(eth.serialize(), 0, OFPort.OFPP_NONE.getValue());
+ sendArpRequestToSwitches(ipAddress, eth.serialize());
+ }
+
+ private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest) {
+ sendArpRequestToSwitches(dstAddress, arpRequest, 0, OFPort.OFPP_NONE.getValue());
+ }
+ private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest,
+ long inSwitch, short inPort) {
+ if (mode == Mode.L2_MODE) {
+ //log.debug("mode is l2");
+ broadcastArpRequestOutEdge(arpRequest, inSwitch, inPort);
+ }
+ else if (mode == Mode.L3_MODE) {
+ //log.debug("mode is l3");
+ //TODO the case where it should be broadcast out all non-interface
+ //edge ports
+ Interface intf = interfacePtrie.match(new Prefix(dstAddress.getAddress(), 32));
+ if (intf != null) {
+ sendArpRequestOutPort(arpRequest, intf.getDpid(), intf.getPort());
+ }
+ else {
+ log.debug("No interface found to send ARP request for {}",
+ dstAddress.getHostAddress());
+ }
+ }
}
private void broadcastArpRequestOutEdge(byte[] arpRequest, long inSwitch, short inPort) {
@@ -394,7 +492,43 @@
}
}
+ private void sendArpRequestOutPort(byte[] arpRequest, long dpid, short port) {
+ log.debug("Sending ARP request out {}/{}", HexString.toHexString(dpid), port);
+
+ OFPacketOut po = new OFPacketOut();
+ po.setInPort(OFPort.OFPP_NONE)
+ .setBufferId(-1)
+ .setPacketData(arpRequest);
+
+ List<OFAction> actions = new ArrayList<OFAction>();
+ actions.add(new OFActionOutput(port));
+ po.setActions(actions);
+ short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
+ po.setActionsLength(actionsLength);
+ po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength
+ + arpRequest.length);
+
+ IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
+
+ if (sw == null) {
+ log.debug("Switch not found when sending ARP request");
+ return;
+ }
+
+ try {
+ sw.write(po, null);
+ sw.flush();
+ } catch (IOException e) {
+ log.error("Failure writing packet out to switch", e);
+ }
+ }
+
public void sendArpReply(ARP arpRequest, long dpid, short port, byte[] targetMac) {
+ log.trace("Sending reply {} => {} to {}", new Object[] {
+ bytesToStringAddr(arpRequest.getTargetProtocolAddress()),
+ HexString.toHexString(targetMac),
+ bytesToStringAddr(arpRequest.getSenderProtocolAddress())});
+
ARP arpReply = new ARP();
arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
.setProtocolType(ARP.PROTO_TYPE_IP)
@@ -430,6 +564,8 @@
IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
if (sw == null) {
+ log.error("Switch {} not found when sending ARP reply",
+ HexString.toHexString(dpid));
return;
}
@@ -467,6 +603,55 @@
arpRequests.put(ipAddress, new ArpRequest(requester, retry));
//storeRequester(ipAddress, requester, retry);
- sendArpRequestForAddress(ipAddress);
+ //Sanity check to make sure we don't send a request for our own address
+ if (!isInterfaceAddress(ipAddress)) {
+ sendArpRequestForAddress(ipAddress);
+ }
+ }
+
+ /*
+ * TODO These methods might be more suited to some kind of L3 information service
+ * that ProxyArpManager could query, rather than having the information
+ * embedded in ProxyArpManager. There may be many modules that need L3 information.
+ */
+
+ private boolean originatedOutsideNetwork(InetAddress source) {
+ Interface intf = interfacePtrie.match(new Prefix(source.getAddress(), 32));
+ if (intf != null) {
+ if (intf.getIpAddress().equals(source)) {
+ // This request must have been originated by us (the controller)
+ return false;
+ }
+ else {
+ // Source was in one of our interface subnets, but wasn't us.
+ // It must be external.
+ return true;
+ }
+ }
+ else {
+ // Source is not in one of our interface subnets. It's probably a host
+ // in our network as we should only receive ARPs broadcast by external
+ // hosts if they're in the same subnet.
+ return false;
+ }
+ }
+
+ private boolean originatedOutsideNetwork(long inDpid, short inPort) {
+ for (Interface intf : interfaces) {
+ if (intf.getDpid() == inDpid && intf.getPort() == inPort) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isInterfaceAddress(InetAddress address) {
+ Interface intf = interfacePtrie.match(new Prefix(address.getAddress(), 32));
+ return (intf != null && intf.getIpAddress().equals(address));
+ }
+
+ private boolean inInterfaceSubnet(InetAddress address) {
+ Interface intf = interfacePtrie.match(new Prefix(address.getAddress(), 32));
+ return (intf != null && !intf.getIpAddress().equals(address));
}
}
diff --git a/src/test/java/net/onrc/onos/ofcontroller/bgproute/PatriciaTrieTest.java b/src/test/java/net/onrc/onos/ofcontroller/bgproute/PatriciaTrieTest.java
index 1af0416..571d37d 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/bgproute/PatriciaTrieTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/bgproute/PatriciaTrieTest.java
@@ -4,7 +4,6 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import java.util.HashMap;
import java.util.Iterator;
@@ -12,18 +11,17 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
public class PatriciaTrieTest {
- IPatriciaTrie ptrie;
+ IPatriciaTrie<RibEntry> ptrie;
Prefix[] prefixes;
Map<Prefix, RibEntry> mappings;
@Before
public void setUp() throws Exception {
- ptrie = new PatriciaTrie(32);
+ ptrie = new PatriciaTrie<RibEntry>(32);
mappings = new HashMap<Prefix, RibEntry>();
prefixes = new Prefix[] {
@@ -50,7 +48,7 @@
@Test
public void testPut() {
- IPatriciaTrie ptrie = new PatriciaTrie(32);
+ IPatriciaTrie<RibEntry> ptrie = new PatriciaTrie<RibEntry>(32);
Prefix p1 = new Prefix("192.168.240.0", 20);
RibEntry r1 = new RibEntry("192.168.10.101", "192.168.60.2");
@@ -107,10 +105,27 @@
assertTrue(retval.equals(r2));
}
- @Ignore
+ //@Ignore
@Test
public void testMatch() {
- fail("Not yet implemented");
+ Prefix p1 = new Prefix("192.168.10.30", 32);
+ Prefix p2 = new Prefix("192.168.10.30", 31);
+ Prefix p3 = new Prefix("192.168.8.241", 32);
+ Prefix p4 = new Prefix("1.0.0.0", 32);
+ Prefix p5 = new Prefix("192.168.8.0", 22);
+ Prefix p6 = new Prefix("192.168.8.0", 21);
+
+ assertTrue(ptrie.match(p1).equals(mappings.get(prefixes[0])));
+ assertTrue(ptrie.match(p2).equals(mappings.get(prefixes[0])));
+ assertTrue(ptrie.match(p3).equals(mappings.get(prefixes[1])));
+ assertNull(ptrie.match(p4));
+ assertTrue(ptrie.match(p5).equals(mappings.get(prefixes[2])));
+ //System.out.println(ptrie.match(p6).getNextHop().getHostAddress());
+ assertTrue(ptrie.match(p6).equals(mappings.get(prefixes[8])));
+
+
+ //TODO more extensive tests
+ //fail("Not yet implemented");
}
@Test
@@ -169,19 +184,19 @@
public void testIterator() {
int[] order = new int[] {7, 5, 3, 8, 2, 1, 0, 4, 6};
- Iterator<IPatriciaTrie.Entry> it = ptrie.iterator();
+ Iterator<IPatriciaTrie.Entry<RibEntry>> it = ptrie.iterator();
int i = 0;
assertTrue(it.hasNext());
while (it.hasNext()) {
- IPatriciaTrie.Entry entry = it.next();
+ IPatriciaTrie.Entry<RibEntry> entry = it.next();
assertTrue(entry.getPrefix().equals(prefixes[order[i]]));
i++;
}
assertFalse(it.hasNext());
assertTrue(i == order.length);
- IPatriciaTrie pt = new PatriciaTrie(32);
- Iterator<IPatriciaTrie.Entry> it2 = pt.iterator();
+ IPatriciaTrie<RibEntry> pt = new PatriciaTrie<RibEntry>(32);
+ Iterator<IPatriciaTrie.Entry<RibEntry>> it2 = pt.iterator();
assertFalse(it2.hasNext());
it.next(); //throws NoSuchElementException
}
diff --git a/src/test/java/net/onrc/onos/ofcontroller/bgproute/PtreeTest.java b/src/test/java/net/onrc/onos/ofcontroller/bgproute/PtreeTest.java
index 6af9d30..1135407 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/bgproute/PtreeTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/bgproute/PtreeTest.java
@@ -25,14 +25,14 @@
private Logger log = LoggerFactory.getLogger(PtreeTest.class);
private Ptree ptree;
- private PatriciaTrie ooptrie;
+ private PatriciaTrie<RibEntry> ooptrie;
private Map<String, byte[]> byteAddresses;
@Before
public void setUp() throws Exception {
ptree = new Ptree(32);
- ooptrie = new PatriciaTrie(32);
+ ooptrie = new PatriciaTrie<RibEntry>(32);
String[] strPrefixes = {
"192.168.10.0/24",
@@ -176,6 +176,7 @@
fail("Not yet implemented");
}
+ @Ignore
@Test
public void testMisc() {
int bitIndex = -1;
@@ -197,10 +198,10 @@
@Test
public void testIteration() {
- Iterator<IPatriciaTrie.Entry> it = ooptrie.iterator();
+ Iterator<IPatriciaTrie.Entry<RibEntry>> it = ooptrie.iterator();
while (it.hasNext()) {
- IPatriciaTrie.Entry entry = it.next();
+ IPatriciaTrie.Entry<RibEntry> entry = it.next();
log.debug("PatriciaTrie prefix {} \t {}", entry.getPrefix(), entry.getPrefix().printAsBits());
}
diff --git a/src/test/java/net/onrc/onos/ofcontroller/flowmanager/FlowManagerTest.java b/src/test/java/net/onrc/onos/ofcontroller/flowmanager/FlowManagerTest.java
index 7bc0aac..83a5fab 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/flowmanager/FlowManagerTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/flowmanager/FlowManagerTest.java
@@ -574,234 +574,8 @@
assertEquals(paramFlow.flowEntryMatch().toString(), resultFlow.flowEntryMatch().toString());
}
- /**
- * Test method for {@link FlowManager#measurementStorePathFlow(FlowPath)}.
- * @throws Exception
- */
- @Test
- public final void testMeasurementStorePathFlowSuccessNormally() throws Exception {
- // instantiate required objects
- FlowPath paramFlow = createTestFlowPath(100, "installer id", 0, 1, 3, 2, 4);
- Map<Long, Object> shortestPathMap = new HashMap<Long, Object>();
- FlowManager fm = new FlowManager();
-
- // setup expectations
- expectInitWithContext();
- expect((Map<Long,Object>)topoRouteService.prepareShortestPathTopo()
- ).andReturn(shortestPathMap);
- expect(topoRouteService.getTopoShortestPath(
- shortestPathMap,
- paramFlow.dataPath().srcPort(),
- paramFlow.dataPath().dstPort())).andReturn(null);
-
- // start the test
- replayAll();
-
- fm.init(context);
- FlowPath resultFlowPath = fm.measurementStorePathFlow(paramFlow);
-
- // verify the test
- verifyAll();
- assertEquals(paramFlow.flowId().value(), resultFlowPath.flowId().value());
- assertEquals(paramFlow.installerId().toString(), resultFlowPath.installerId().toString());
- assertEquals(paramFlow.flowPathFlags().flags(), resultFlowPath.flowPathFlags().flags());
- assertEquals(paramFlow.dataPath().toString(), resultFlowPath.dataPath().toString());
- assertEquals(paramFlow.flowEntryMatch().toString(), resultFlowPath.flowEntryMatch().toString());
- }
-
- /**
- * Test method for {@link FlowManager#measurementInstallPaths(Integer)}.
- * @throws Exception
- */
- @Test
- public final void testMeasurementInstallPathsSuccessNormally() throws Exception {
- final String addFlow = "addFlow";
-
- // create mock objects
- FlowManager fm = createPartialMockAndInvokeDefaultConstructor(FlowManager.class, addFlow);
-
- // instantiate required objects
- FlowPath flow1 = createTestFlowPath(1, "installer id", 0, 1, 2, 3, 4);
- FlowPath flow2 = createTestFlowPath(2, "installer id", 0, 2, 3, 4, 5);
- FlowPath flow3 = createTestFlowPath(3, "installer id", 0, 3, 4, 5, 6);
- Map<Long, Object> shortestPathMap = new HashMap<Long, Object>();
-
- // setup expectations
- expectInitWithContext();
- expect((Map<Long,Object>)topoRouteService.prepareShortestPathTopo()
- ).andReturn(shortestPathMap);
-
- expect(topoRouteService.getTopoShortestPath(
- shortestPathMap,
- flow1.dataPath().srcPort(),
- flow1.dataPath().dstPort())).andReturn(null);
-
- expect(topoRouteService.getTopoShortestPath(
- shortestPathMap,
- flow2.dataPath().srcPort(),
- flow2.dataPath().dstPort())).andReturn(null);
-
- expect(topoRouteService.getTopoShortestPath(
- shortestPathMap,
- flow3.dataPath().srcPort(),
- flow3.dataPath().dstPort())).andReturn(null);
-
- expectPrivate(fm, addFlow,
- EasyMock.cmpEq(flow1),
- EasyMock.anyObject(FlowId.class),
- EasyMock.anyObject(String.class)).andReturn(true);
-
- expectPrivate(fm, addFlow,
- EasyMock.cmpEq(flow2),
- EasyMock.anyObject(FlowId.class),
- EasyMock.anyObject(String.class)).andReturn(true);
-
- expectPrivate(fm, addFlow,
- EasyMock.cmpEq(flow3),
- EasyMock.anyObject(FlowId.class),
- EasyMock.anyObject(String.class)).andReturn(true);
-
- // start the test
- replayAll();
-
- fm.init(context);
- fm.measurementStorePathFlow(flow1);
- fm.measurementStorePathFlow(flow2);
- fm.measurementStorePathFlow(flow3);
- Boolean result = fm.measurementInstallPaths(3);
-
- // verify the test
- verifyAll();
- assertTrue(result);
- }
-
- /**
- * Test method for {@link FlowManager#measurementGetInstallPathsTimeNsec()}.
- * @throws Exception
- */
- @Test
- public final void testMeasurementGetInstallPathsTimeNsecSuccessNormally() throws Exception {
- final String addFlow = "addFlow";
-
- // create mock objects
- FlowManager fm = createPartialMockAndInvokeDefaultConstructor(FlowManager.class, addFlow);
- mockStaticPartial(System.class, "nanoTime");
-
- // instantiate required objects
- FlowPath flow1 = createTestFlowPath(1, "installer id", 0, 1, 2, 3, 4);
- Map<Long, Object> shortestPathMap = new HashMap<Long, Object>();
-
- // setup expectations
- expectInitWithContext();
- expect(System.nanoTime()).andReturn(new Long(100000));
- expect(System.nanoTime()).andReturn(new Long(110000));
- expect((Map<Long,Object>)topoRouteService.prepareShortestPathTopo()
- ).andReturn(shortestPathMap);
- expect(topoRouteService.getTopoShortestPath(
- shortestPathMap,
- flow1.dataPath().srcPort(),
- flow1.dataPath().dstPort())).andReturn(null);
- expectPrivate(fm, addFlow,
- EasyMock.cmpEq(flow1),
- EasyMock.anyObject(FlowId.class),
- EasyMock.anyObject(String.class)).andReturn(true);
-
- // start the test
- replayAll();
-
- fm.init(context);
- fm.measurementStorePathFlow(flow1).toString();
- fm.measurementInstallPaths(1);
- Long result = fm.measurementGetInstallPathsTimeNsec();
-
- // verify the test
- verifyAll();
- assertEquals(new Long(10000), result);
- }
-
- /**
- * Test method for {@link FlowManager#measurementGetPerFlowInstallTime()}.
- * @throws Exception
- */
- @Test
- public final void testMeasurementGetPerFlowInstallTimeSuccessNormally() throws Exception {
- final String addFlow = "addFlow";
-
- // create mock objects
- FlowManager fm = createPartialMockAndInvokeDefaultConstructor(FlowManager.class, addFlow);
-
- // instantiate required objects
- FlowPath flow1 = createTestFlowPath(1, "installer id", 0, 1, 2, 3, 4);
- Map<Long, Object> shortestPathMap = new HashMap<Long, Object>();
-
- // setup expectations
- expectInitWithContext();
- expect((Map<Long,Object>)topoRouteService.prepareShortestPathTopo()
- ).andReturn(shortestPathMap);
-
- expect(topoRouteService.getTopoShortestPath(
- shortestPathMap,
- flow1.dataPath().srcPort(),
- flow1.dataPath().dstPort())).andReturn(null);
-
- expectPrivate(fm, addFlow,
- EasyMock.cmpEq(flow1),
- EasyMock.anyObject(FlowId.class),
- EasyMock.anyObject(String.class)).andReturn(true);
-
-
- // start the test
- replayAll();
-
- fm.init(context);
- fm.measurementStorePathFlow(flow1);
- fm.measurementInstallPaths(10);
- String result = fm.measurementGetPerFlowInstallTime();
-
- // verify the test
- verifyAll();
- assertTrue(result.startsWith("ThreadAndTimePerFlow"));
- }
-
- /**
- * Test method for {@link FlowManager#measurementClearAllPaths()}.
- * @throws Exception
- */
- @Test
- public final void testMeasurementClearAllPathsSuccessNormally() throws Exception {
- // instantiate required objects
- FlowPath paramFlow = createTestFlowPath(100, "installer id", 0, 1, 3, 2, 4);
- Map<Long, Object> shortestPathMap = new HashMap<Long, Object>();
- FlowManager fm = new FlowManager();
-
- // setup expectations
- expectInitWithContext();
- expect((Map<Long,Object>)topoRouteService.prepareShortestPathTopo()
- ).andReturn(shortestPathMap);
- expect(topoRouteService.getTopoShortestPath(
- shortestPathMap,
- paramFlow.dataPath().srcPort(),
- paramFlow.dataPath().dstPort())).andReturn(null);
- topoRouteService.dropShortestPathTopo(shortestPathMap);
-
- // start the test
- replayAll();
-
- fm.init(context);
- fm.measurementStorePathFlow(paramFlow);
- Boolean result = fm.measurementClearAllPaths();
-
- // verify the test
- verifyAll();
- assertTrue(result);
- assertEquals(new Long(0), fm.measurementGetInstallPathsTimeNsec());
- assertEquals("", fm.measurementGetPerFlowInstallTime());
- }
-
-
// INetMapStorage methods
-
/**
* Test method for {@link FlowManager#init(String)}.
* @throws Exception
diff --git a/web/measurement_clear_all_paths.py b/web/measurement_clear_all_paths.py
deleted file mode 100755
index 5bb73c5..0000000
--- a/web/measurement_clear_all_paths.py
+++ /dev/null
@@ -1,61 +0,0 @@
-#! /usr/bin/env python
-# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
-
-import pprint
-import os
-import sys
-import subprocess
-import json
-import argparse
-import io
-import time
-
-from flask import Flask, json, Response, render_template, make_response, request
-
-#
-# TODO: remove this! We don't use JSON argument here!
-# curl http://127.0.0.1:8080/wm/flow/delete/{"value":"0xf"}/json'
-#
-
-## Global Var ##
-ControllerIP="127.0.0.1"
-ControllerPort=8080
-
-DEBUG=0
-pp = pprint.PrettyPrinter(indent=4)
-
-app = Flask(__name__)
-
-## Worker Functions ##
-def log_error(txt):
- print '%s' % (txt)
-
-def debug(txt):
- if DEBUG:
- print '%s' % (txt)
-
-# @app.route("/wm/flow/measurement-clear-all-paths/json")
-def measurement_clear_all_paths():
- command = "curl -s \"http://%s:%s/wm/flow/measurement-clear-all-paths/json\"" % (ControllerIP, ControllerPort)
- debug("measurement_clear_all_paths %s" % command)
- result = os.popen(command).read()
- debug("result %s" % result)
- # parsedResult = json.loads(result)
- # debug("parsed %s" % parsedResult)
-
-if __name__ == "__main__":
- usage_msg = "Clear the paths that have been stored for measurement purpose\n"
- usage_msg = usage_msg + "Usage: %s\n" % (sys.argv[0])
- usage_msg = usage_msg + "\n"
-
- # app.debug = False;
-
- # Usage info
- if len(sys.argv) > 1 and (sys.argv[1] == "-h" or sys.argv[1] == "--help"):
- print(usage_msg)
- exit(0)
-
- # Check arguments
-
- # Do the work
- measurement_clear_all_paths()
diff --git a/web/measurement_get_install_paths_time_nsec.py b/web/measurement_get_install_paths_time_nsec.py
deleted file mode 100755
index d64dc49..0000000
--- a/web/measurement_get_install_paths_time_nsec.py
+++ /dev/null
@@ -1,61 +0,0 @@
-#! /usr/bin/env python
-# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
-
-import pprint
-import os
-import sys
-import subprocess
-import json
-import argparse
-import io
-import time
-
-from flask import Flask, json, Response, render_template, make_response, request
-
-#
-# TODO: remove this! We don't use JSON argument here!
-# curl http://127.0.0.1:8080/wm/flow/delete/{"value":"0xf"}/json'
-#
-
-## Global Var ##
-ControllerIP="127.0.0.1"
-ControllerPort=8080
-
-DEBUG=0
-pp = pprint.PrettyPrinter(indent=4)
-
-app = Flask(__name__)
-
-## Worker Functions ##
-def log_error(txt):
- print '%s' % (txt)
-
-def debug(txt):
- if DEBUG:
- print '%s' % (txt)
-
-# @app.route("/wm/flow/measurement-get-install-paths-time-nsec/json")
-def measurement_get_install_paths_time_nsec():
- command = "curl -s \"http://%s:%s/wm/flow/measurement-get-install-paths-time-nsec/json\"" % (ControllerIP, ControllerPort)
- debug("measurement_get_install_paths_time_nsec %s" % command)
- result = os.popen(command).read()
- print '%s nsec' % (result)
- # parsedResult = json.loads(result)
- # debug("parsed %s" % parsedResult)
-
-if __name__ == "__main__":
- usage_msg = "Get the measured time to install the stored flow paths\n"
- usage_msg = usage_msg + "Usage: %s\n" % (sys.argv[0])
- usage_msg = usage_msg + "\n"
-
- # app.debug = False;
-
- # Usage info
- if len(sys.argv) > 1 and (sys.argv[1] == "-h" or sys.argv[1] == "--help"):
- print(usage_msg)
- exit(0)
-
- # Check arguments
-
- # Do the work
- measurement_get_install_paths_time_nsec()
diff --git a/web/measurement_get_per_flow_install_time.py b/web/measurement_get_per_flow_install_time.py
deleted file mode 100755
index bf2bcc7..0000000
--- a/web/measurement_get_per_flow_install_time.py
+++ /dev/null
@@ -1,61 +0,0 @@
-#! /usr/bin/env python
-# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
-
-import pprint
-import os
-import sys
-import subprocess
-import json
-import argparse
-import io
-import time
-
-from flask import Flask, json, Response, render_template, make_response, request
-
-#
-# TODO: remove this! We don't use JSON argument here!
-# curl http://127.0.0.1:8080/wm/flow/delete/{"value":"0xf"}/json'
-#
-
-## Global Var ##
-ControllerIP="127.0.0.1"
-ControllerPort=8080
-
-DEBUG=0
-pp = pprint.PrettyPrinter(indent=4)
-
-app = Flask(__name__)
-
-## Worker Functions ##
-def log_error(txt):
- print '%s' % (txt)
-
-def debug(txt):
- if DEBUG:
- print '%s' % (txt)
-
-# @app.route("/wm/flow/measurement-get-per-flow-install-time/json")
-def measurement_get_per_flow_install_time():
- command = "curl -s \"http://%s:%s/wm/flow/measurement-get-per-flow-install-time/json\"" % (ControllerIP, ControllerPort)
- debug("measurement_get_per_flow_install_time %s" % command)
- result = os.popen(command).read()
- print '%s' % (result)
- # parsedResult = json.loads(result)
- # debug("parsed %s" % parsedResult)
-
-if __name__ == "__main__":
- usage_msg = "Get the measured time per flow to install each stored flow path\n"
- usage_msg = usage_msg + "Usage: %s\n" % (sys.argv[0])
- usage_msg = usage_msg + "\n"
-
- # app.debug = False;
-
- # Usage info
- if len(sys.argv) > 1 and (sys.argv[1] == "-h" or sys.argv[1] == "--help"):
- print(usage_msg)
- exit(0)
-
- # Check arguments
-
- # Do the work
- measurement_get_per_flow_install_time()
diff --git a/web/measurement_install_paths.py b/web/measurement_install_paths.py
deleted file mode 100755
index d99070e..0000000
--- a/web/measurement_install_paths.py
+++ /dev/null
@@ -1,67 +0,0 @@
-#! /usr/bin/env python
-# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
-
-import pprint
-import os
-import sys
-import subprocess
-import json
-import argparse
-import io
-import time
-
-from flask import Flask, json, Response, render_template, make_response, request
-
-#
-# TODO: remove this! We don't use JSON argument here!
-# curl http://127.0.0.1:8080/wm/flow/delete/{"value":"0xf"}/json'
-#
-
-## Global Var ##
-ControllerIP="127.0.0.1"
-ControllerPort=8080
-
-DEBUG=0
-pp = pprint.PrettyPrinter(indent=4)
-
-app = Flask(__name__)
-
-## Worker Functions ##
-def log_error(txt):
- print '%s' % (txt)
-
-def debug(txt):
- if DEBUG:
- print '%s' % (txt)
-
-# @app.route("/wm/flow/measurement-install-paths/<num-threads>/json")
-def measurement_install_paths(num_threads):
- command = "curl -s \"http://%s:%s/wm/flow/measurement-install-paths/%s/json\"" % (ControllerIP, ControllerPort, num_threads)
- debug("measurement_install_paths %s" % command)
- result = os.popen(command).read()
- debug("result %s" % result)
- # parsedResult = json.loads(result)
- # debug("parsed %s" % parsedResult)
-
-if __name__ == "__main__":
- usage_msg = "Install flow paths and start measurements\n"
- usage_msg = usage_msg + "Usage: %s <num-threads>\n" % (sys.argv[0])
- usage_msg = usage_msg + "\n"
- usage_msg = usage_msg + " Arguments:\n"
- usage_msg = usage_msg + " <num-threads> Number of threads to use to install the flows\n"
-
- # app.debug = False;
-
- # Usage info
- if len(sys.argv) > 1 and (sys.argv[1] == "-h" or sys.argv[1] == "--help"):
- print(usage_msg)
- exit(0)
-
- # Check arguments
- if len(sys.argv) < 2:
- log_error(usage_msg)
- exit(1)
- num_threads = int(sys.argv[1], 0)
-
- # Do the work
- measurement_install_paths(num_threads)
diff --git a/web/measurement_process.py b/web/measurement_process.py
deleted file mode 100755
index 3187299..0000000
--- a/web/measurement_process.py
+++ /dev/null
@@ -1,59 +0,0 @@
-#! /usr/bin/env python
-# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
-
-import functools
-import math
-import sys
-
-## {{{ http://code.activestate.com/recipes/511478/ (r1)
-
-def percentile(N, percent, key=lambda x:x):
- """
- Find the percentile of a list of values.
-
- @parameter N - is a list of values. Note N MUST BE already sorted.
- @parameter percent - a float value from 0.0 to 1.0.
- @parameter key - optional key function to compute value from each element of N.
-
- @return - the percentile of the values
- """
- if not N:
- return None
- k = (len(N)-1) * percent
- f = math.floor(k)
- c = math.ceil(k)
- if f == c:
- return key(N[int(k)])
- d0 = key(N[int(f)]) * (c-k)
- d1 = key(N[int(c)]) * (k-f)
- return d0+d1
-
-# median is 50th percentile.
-# median = functools.partial(percentile, percent=0.5)
-## end of http://code.activestate.com/recipes/511478/ }}}
-
-if __name__ == "__main__":
-
- dict = {}
-
- #
- # Read the data from the stdin, and store it in a dictionary.
- # The dictionary uses lists as values.
- #
- data = sys.stdin.readlines()
- for line in data:
- words = line.split()
- thread_n = int(words[0])
- msec = float(words[1])
- dict.setdefault(thread_n, []).append(msec)
-
- #
- # Compute and print the values: median (50-th), 10-th, and 90-th
- # percentile:
- # <key> <median> <10-percentile> <90-percentile>
- #
- for key, val_list in sorted(dict.items()):
- val_10 = percentile(sorted(val_list), 0.1)
- val_50 = percentile(sorted(val_list), 0.5)
- val_90 = percentile(sorted(val_list), 0.9)
- print "%s %s %s %s" % (str(key), str(val_50), str(val_10), str(val_90))
diff --git a/web/measurement_run.py b/web/measurement_run.py
deleted file mode 100755
index 80d0517..0000000
--- a/web/measurement_run.py
+++ /dev/null
@@ -1,104 +0,0 @@
-#! /usr/bin/env python
-# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
-
-import os
-import string
-import subprocess
-import time
-
-# flow_n = 252
-# threads_n = [1, 2, 3, 4, 5, 10, 20, 30, 40, 50, 100]
-# iterations_n = 10
-
-flow_n = 1
-threads_n = [1]
-iterations_n = 10
-# iterations_n = 100
-
-# flow_n = 42
-# flow_n = 420
-# flow_n = 1008
-
-def run_command(cmd):
- """
- - Run an external command, and return a tuple: stdout as the
- first argument, and stderr as the second argument.
- - Returns None if error.
- """
- try:
- pr = subprocess.Popen(cmd, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
- ret_tuple = pr.communicate();
- if pr.returncode:
- print "%s failed with error code: %s" % (cmd, str(pr.returncode))
- return ret_tuple
- except OSError:
- print "OS Error running %s" % cmd
-
-def run_install_paths(flowdef_filename):
- # Prepare the flows to measure
- cmd = "web/measurement_store_flow.py -f " + flowdef_filename
- os.system(cmd)
-
-def run_measurement(thread_n):
- # Install the Flow Paths
- cmd = ["web/measurement_install_paths.py", str(thread_n)]
- run_command(cmd)
-
- # Get the measurement data and print it
- cmd = "web/measurement_get_install_paths_time_nsec.py"
- r = run_command(cmd) # Tuple: [<stdout>, <stderr>]
- res = r[0].split() # Tuple: [<num>, nsec]
- nsec_str = res[0]
- msec = float(nsec_str) / (1000 * 1000)
-
- # Get the measurement data and print it
- cmd = "web/measurement_get_per_flow_install_time.py"
- r = run_command(cmd) # Tuple: [<stdout>, <stderr>]
- res = r[0]
- print res
-
- # Keep checking until all Flow Paths are installed
- while True:
- # time.sleep(3)
- cmd = ["web/get_flow.py", "all"]
- r = run_command(cmd)
- if string.count(r[0], "FlowPath") != flow_n:
- continue
- if string.find(r[0], "NOT") == -1:
- break
-
- # Remove the installed Flow Paths
- cmd = ["web/delete_flow.py", "all"]
- run_command(cmd)
-
- # Keep checking until all Flows are removed
- while True:
- # time.sleep(3)
- cmd = ["web/get_flow.py", "all"]
- r = run_command(cmd)
- if r[0] == "":
- break
-
- return msec
-
-
-if __name__ == "__main__":
-
- # Initial cleanup
- cmd = "web/measurement_clear_all_paths.py"
- run_command(cmd)
-
- # Install the Flow Paths to measure
- flowdef_filename = "web/flowdef_8node_" + str(flow_n) + ".txt"
- run_install_paths(flowdef_filename)
-
- # Do the work
- for thread_n in threads_n:
- for n in range(iterations_n):
- msec = run_measurement(thread_n)
- # Format: <number of threads> <time in ms>
- print "%d %f" % (thread_n, msec / flow_n)
-
- # Cleanup on exit
- cmd = "web/measurement_clear_all_paths.py"
- run_command(cmd)
diff --git a/web/measurement_store_flow.py b/web/measurement_store_flow.py
deleted file mode 100755
index 0e39465..0000000
--- a/web/measurement_store_flow.py
+++ /dev/null
@@ -1,471 +0,0 @@
-#! /usr/bin/env python
-# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
-
-import copy
-import pprint
-import os
-import sys
-import subprocess
-import json
-import argparse
-import io
-import time
-
-from flask import Flask, json, Response, render_template, make_response, request
-
-## Global Var ##
-ControllerIP = "127.0.0.1"
-ControllerPort = 8080
-ReadFromFile = ""
-
-DEBUG=0
-pp = pprint.PrettyPrinter(indent=4)
-
-app = Flask(__name__)
-
-## Worker Functions ##
-def log_error(txt):
- print '%s' % (txt)
-
-def debug(txt):
- if DEBUG:
- print '%s' % (txt)
-
-def measurement_store_path_flow(flow_path):
- flow_path_json = json.dumps(flow_path)
-
- try:
- command = "curl -s -H 'Content-Type: application/json' -d '%s' http://%s:%s/wm/flow/measurement-store-path/json" % (flow_path_json, ControllerIP, ControllerPort)
- debug("measurement_store_path_flow %s" % command)
- result = os.popen(command).read()
- debug("result %s" % result)
- # parsedResult = json.loads(result)
- # debug("parsed %s" % parsedResult)
- except:
- log_error("Controller IF has issue")
- exit(1)
-
-def extract_flow_args(my_args):
- # Check the arguments
- if len(my_args) < 6:
- log_error(usage_msg)
- exit(1)
-
- # Extract the mandatory arguments
- my_flow_id = my_args[0]
- my_installer_id = my_args[1]
- my_src_dpid = my_args[2]
- my_src_port = my_args[3]
- my_dst_dpid = my_args[4]
- my_dst_port = my_args[5]
-
- #
- # Extract the "flowPathFlags", "match" and "action" arguments
- #
- flowPathFlags = 0L
- match = {}
- matchInPortEnabled = True # NOTE: Enabled by default
- actions = []
- actionOutputEnabled = True # NOTE: Enabled by default
- idx = 6
- while idx < len(my_args):
- action = {}
- arg1 = my_args[idx]
- idx = idx + 1
- # Extract the second argument
- if idx >= len(my_args):
- error_arg = "ERROR: Missing or invalid '" + arg1 + "' argument"
- log_error(error_arg)
- log_error(usage_msg)
- exit(1)
- arg2 = my_args[idx]
- idx = idx + 1
-
- if arg1 == "flowPathFlags":
- if "DISCARD_FIRST_HOP_ENTRY" in arg2:
- flowPathFlags = flowPathFlags + 0x1
- if "KEEP_ONLY_FIRST_HOP_ENTRY" in arg2:
- flowPathFlags = flowPathFlags + 0x2
- elif arg1 == "matchInPort":
- # Mark whether ACTION_OUTPUT action is enabled
- matchInPortEnabled = arg2 in ['True', 'true']
- # inPort = {}
- # inPort['value'] = int(arg2, 0)
- # match['inPort'] = inPort
- ## match['matchInPort'] = True
- elif arg1 == "matchSrcMac":
- srcMac = {}
- srcMac['value'] = arg2
- match['srcMac'] = srcMac
- # match['matchSrcMac'] = True
- elif arg1 == "matchDstMac":
- dstMac = {}
- dstMac['value'] = arg2
- match['dstMac'] = dstMac
- # match['matchDstMac'] = True
- elif arg1 == "matchEthernetFrameType":
- match['ethernetFrameType'] = int(arg2, 0)
- # match['matchEthernetFrameType'] = True
- elif arg1 == "matchVlanId":
- match['vlanId'] = int(arg2, 0)
- # match['matchVlanId'] = True
- elif arg1 == "matchVlanPriority":
- match['vlanPriority'] = int(arg2, 0)
- # match['matchVlanPriority'] = True
- elif arg1 == "matchSrcIPv4Net":
- srcIPv4Net = {}
- srcIPv4Net['value'] = arg2
- match['srcIPv4Net'] = srcIPv4Net
- # match['matchSrcIPv4Net'] = True
- elif arg1 == "matchDstIPv4Net":
- dstIPv4Net = {}
- dstIPv4Net['value'] = arg2
- match['dstIPv4Net'] = dstIPv4Net
- # match['matchDstIPv4Net'] = True
- elif arg1 == "matchIpProto":
- match['ipProto'] = int(arg2, 0)
- # match['matchIpProto'] = True
- elif arg1 == "matchIpToS":
- match['ipToS'] = int(arg2, 0)
- # match['matchIpToS'] = True
- elif arg1 == "matchSrcTcpUdpPort":
- match['srcTcpUdpPort'] = int(arg2, 0)
- # match['matchSrcTcpUdpPort'] = True
- elif arg1 == "matchDstTcpUdpPort":
- match['dstTcpUdpPort'] = int(arg2, 0)
- # match['matchDstTcpUdpPort'] = True
- elif arg1 == "actionOutput":
- # Mark whether ACTION_OUTPUT action is enabled
- actionOutputEnabled = arg2 in ['True', 'true']
- # If ACTION_OUTPUT is explicitly enabled, add an entry with a fake
- # port number. We need this entry to preserve the action ordering.
- if actionOutputEnabled == True:
- actionOutput = {}
- outPort = {}
- outPort['value'] = 0xffff
- actionOutput['port'] = outPort
- actionOutput['maxLen'] = 0
- action['actionOutput'] = actionOutput
- # action['actionType'] = 'ACTION_OUTPUT'
- actions.append(action)
- #
- elif arg1 == "actionSetVlanId":
- vlanId = {}
- vlanId['vlanId'] = int(arg2, 0)
- action['actionSetVlanId'] = vlanId
- # action['actionType'] = 'ACTION_SET_VLAN_VID'
- actions.append(copy.deepcopy(action))
- elif arg1 == "actionSetVlanPriority":
- vlanPriority = {}
- vlanPriority['vlanPriority'] = int(arg2, 0)
- action['actionSetVlanPriority'] = vlanPriority
- # action['actionType'] = 'ACTION_SET_VLAN_PCP'
- actions.append(copy.deepcopy(action))
- elif arg1 == "actionStripVlan":
- stripVlan = {}
- stripVlan['stripVlan'] = arg2 in ['True', 'true']
- action['actionStripVlan'] = stripVlan
- # action['actionType'] = 'ACTION_STRIP_VLAN'
- actions.append(copy.deepcopy(action))
- elif arg1 == "actionSetEthernetSrcAddr":
- ethernetSrcAddr = {}
- ethernetSrcAddr['value'] = arg2
- setEthernetSrcAddr = {}
- setEthernetSrcAddr['addr'] = ethernetSrcAddr
- action['actionSetEthernetSrcAddr'] = setEthernetSrcAddr
- # action['actionType'] = 'ACTION_SET_DL_SRC'
- actions.append(copy.deepcopy(action))
- elif arg1 == "actionSetEthernetDstAddr":
- ethernetDstAddr = {}
- ethernetDstAddr['value'] = arg2
- setEthernetDstAddr = {}
- setEthernetDstAddr['addr'] = ethernetDstAddr
- action['actionSetEthernetDstAddr'] = setEthernetDstAddr
- # action['actionType'] = 'ACTION_SET_DL_DST'
- actions.append(copy.deepcopy(action))
- elif arg1 == "actionSetIPv4SrcAddr":
- IPv4SrcAddr = {}
- IPv4SrcAddr['value'] = arg2
- setIPv4SrcAddr = {}
- setIPv4SrcAddr['addr'] = IPv4SrcAddr
- action['actionSetIPv4SrcAddr'] = setIPv4SrcAddr
- # action['actionType'] = 'ACTION_SET_NW_SRC'
- actions.append(copy.deepcopy(action))
- elif arg1 == "actionSetIPv4DstAddr":
- IPv4DstAddr = {}
- IPv4DstAddr['value'] = arg2
- setIPv4DstAddr = {}
- setIPv4DstAddr['addr'] = IPv4DstAddr
- action['actionSetIPv4DstAddr'] = setIPv4DstAddr
- # action['actionType'] = 'ACTION_SET_NW_DST'
- actions.append(copy.deepcopy(action))
- elif arg1 == "actionSetIpToS":
- ipToS = {}
- ipToS['ipToS'] = int(arg2, 0)
- action['actionSetIpToS'] = ipToS
- # action['actionType'] = 'ACTION_SET_NW_TOS'
- actions.append(copy.deepcopy(action))
- elif arg1 == "actionSetTcpUdpSrcPort":
- tcpUdpSrcPort = {}
- tcpUdpSrcPort['port'] = int(arg2, 0)
- action['actionSetTcpUdpSrcPort'] = tcpUdpSrcPort
- # action['actionType'] = 'ACTION_SET_TP_SRC'
- actions.append(copy.deepcopy(action))
- elif arg1 == "actionSetTcpUdpDstPort":
- tcpUdpDstPort = {}
- tcpUdpDstPort['port'] = int(arg2, 0)
- action['actionSetTcpUdpDstPort'] = tcpUdpDstPort
- # action['actionType'] = 'ACTION_SET_TP_DST'
- actions.append(copy.deepcopy(action))
- elif arg1 == "actionEnqueue":
- # TODO: Implement ACTION_ENQUEUE
- actionEnqueue = {}
- # actionEnqueue['queueId'] = int(arg2, 0)
- # enqueuePort = {}
- # enqueuePort['value'] = int(arg3, 0)
- # actionEnqueue['port'] = enqueuePort
- # action['actionEnqueue'] = actionEnqueue
- # # action['actionType'] = 'ACTION_ENQUEUE'
- # actions.append(copy.deepcopy(action))
- #
- else:
- log_error("ERROR: Unknown argument '%s'" % (arg1))
- log_error(usage_msg)
- exit(1)
-
- return {
- 'my_flow_id' : my_flow_id,
- 'my_installer_id' : my_installer_id,
- 'my_src_dpid' : my_src_dpid,
- 'my_src_port' : my_src_port,
- 'my_dst_dpid' : my_dst_dpid,
- 'my_dst_port' : my_dst_port,
- 'flowPathFlags' : flowPathFlags,
- 'match' : match,
- 'matchInPortEnabled' : matchInPortEnabled,
- 'actions' : actions,
- 'actionOutputEnabled' : actionOutputEnabled
- }
-
-def compute_flow_path(parsed_args, data_path):
-
- my_flow_id = parsed_args['my_flow_id']
- my_installer_id = parsed_args['my_installer_id']
- myFlowPathFlags = parsed_args['flowPathFlags']
- match = parsed_args['match']
- matchInPortEnabled = parsed_args['matchInPortEnabled']
- actions = parsed_args['actions']
- actionOutputEnabled = parsed_args['actionOutputEnabled']
- my_data_path = copy.deepcopy(data_path)
-
- flow_id = {}
- flow_id['value'] = my_flow_id
- installer_id = {}
- installer_id['value'] = my_installer_id
- flowPathFlags = {}
- flowPathFlags['flags'] = myFlowPathFlags
-
- flowEntryActions = {}
-
- flow_path = {}
- flow_path['flowId'] = flow_id
- flow_path['installerId'] = installer_id
- flow_path['flowPathFlags'] = flowPathFlags
-
- if (len(match) > 0):
- flow_path['flowEntryMatch'] = copy.deepcopy(match)
-
- #
- # Add the match conditions to each flow entry
- #
- if (len(match) > 0) or matchInPortEnabled:
- idx = 0
- while idx < len(my_data_path['flowEntries']):
- if matchInPortEnabled:
- inPort = my_data_path['flowEntries'][idx]['inPort']
- match['inPort'] = copy.deepcopy(inPort)
- # match['matchInPort'] = True
- my_data_path['flowEntries'][idx]['flowEntryMatch'] = copy.deepcopy(match)
- idx = idx + 1
-
-
- if (len(actions) > 0):
- flowEntryActions['actions'] = copy.deepcopy(actions)
- flow_path['flowEntryActions'] = flowEntryActions
-
- #
- # Set the actions for each flow entry
- # NOTE: The actions from the command line are aplied
- # ONLY to the first flow entry.
- #
- # If ACTION_OUTPUT action is enabled, then apply it
- # to each flow entry.
- #
- if (len(actions) > 0) or actionOutputEnabled:
- idx = 0
- while idx < len(my_data_path['flowEntries']):
- if idx > 0:
- actions = [] # Reset the actions for all but first entry
- action = {}
- outPort = my_data_path['flowEntries'][idx]['outPort']
- actionOutput = {}
- actionOutput['port'] = copy.deepcopy(outPort)
- # actionOutput['maxLen'] = 0 # TODO: not used for now
- action['actionOutput'] = copy.deepcopy(actionOutput)
- # action['actionType'] = 'ACTION_OUTPUT'
- actions.append(copy.deepcopy(action))
- flowEntryActions = {}
- flowEntryActions['actions'] = copy.deepcopy(actions)
-
- my_data_path['flowEntries'][idx]['flowEntryActions'] = flowEntryActions
- idx = idx + 1
-
- flow_path['dataPath'] = my_data_path
- debug("Flow Path: %s" % flow_path)
- return flow_path
-
-def measurement_store_paths(parsed_args):
- idx = 0
- while idx < len(parsed_args):
- data_path = {}
- src_dpid = {}
- src_port = {}
- dst_dpid = {}
- dst_port = {}
- src_switch_port = {}
- dst_switch_port = {}
- flow_entries = []
-
- src_dpid['value'] = parsed_args[idx]['my_src_dpid']
- src_port['value'] = parsed_args[idx]['my_src_port']
- dst_dpid['value'] = parsed_args[idx]['my_dst_dpid']
- dst_port['value'] = parsed_args[idx]['my_dst_port']
- src_switch_port['dpid'] = src_dpid
- src_switch_port['port'] = src_port
- dst_switch_port['dpid'] = dst_dpid
- dst_switch_port['port'] = dst_port
-
- data_path['srcPort'] = copy.deepcopy(src_switch_port)
- data_path['dstPort'] = copy.deepcopy(dst_switch_port)
- data_path['flowEntries'] = copy.deepcopy(flow_entries)
-
- #
- # XXX: Explicitly disable the InPort matching, and
- # the Output action, because they get in the way
- # during the compute_flow_path() processing.
- #
- parsed_args[idx]['matchInPortEnabled'] = False
- parsed_args[idx]['actionOutputEnabled'] = False
-
- flow_path = compute_flow_path(parsed_args[idx], data_path)
- measurement_store_path_flow(flow_path)
-
- idx = idx + 1
-
-
-if __name__ == "__main__":
- usage_msg = "Store Flow Paths into ONOS for measurement purpose.\n"
- usage_msg = usage_msg + "\n"
- usage_msg = usage_msg + "Usage: %s [Flags] <flow-id> <installer-id> <src-dpid> <src-port> <dest-dpid> <dest-port> [Flow Path Flags] [Match Conditions] [Actions]\n" % (sys.argv[0])
- usage_msg = usage_msg + "\n"
- usage_msg = usage_msg + " Flags:\n"
- usage_msg = usage_msg + " -f <filename> Read the flow(s) to install from a file\n"
- usage_msg = usage_msg + " File format: one line per flow starting with <flow-id>\n"
- usage_msg = usage_msg + "\n"
- usage_msg = usage_msg + " Flow Path Flags:\n"
- usage_msg = usage_msg + " flowPathFlags <Flags> (flag names separated by ',')\n"
- usage_msg = usage_msg + "\n"
- usage_msg = usage_msg + " Known flags:\n"
- usage_msg = usage_msg + " DISCARD_FIRST_HOP_ENTRY : Discard the first-hop flow entry\n"
- usage_msg = usage_msg + " KEEP_ONLY_FIRST_HOP_ENTRY : Keep only the first-hop flow entry\n"
- usage_msg = usage_msg + "\n"
- usage_msg = usage_msg + " Match Conditions:\n"
- usage_msg = usage_msg + " matchInPort <True|False> (default to True)\n"
- usage_msg = usage_msg + " matchSrcMac <source MAC address>\n"
- usage_msg = usage_msg + " matchDstMac <destination MAC address>\n"
- usage_msg = usage_msg + " matchEthernetFrameType <Ethernet frame type>\n"
- usage_msg = usage_msg + " matchVlanId <VLAN ID>\n"
- usage_msg = usage_msg + " matchVlanPriority <VLAN priority>\n"
- usage_msg = usage_msg + " matchSrcIPv4Net <source IPv4 network address>\n"
- usage_msg = usage_msg + " matchDstIPv4Net <destination IPv4 network address>\n"
- usage_msg = usage_msg + " matchIpProto <IP protocol>\n"
- usage_msg = usage_msg + " matchIpToS <IP ToS (DSCP field, 6 bits)>\n"
- usage_msg = usage_msg + " matchSrcTcpUdpPort <source TCP/UDP port>\n"
- usage_msg = usage_msg + " matchDstTcpUdpPort <destination TCP/UDP port>\n"
- usage_msg = usage_msg + "\n"
- usage_msg = usage_msg + " Actions:\n"
- usage_msg = usage_msg + " actionOutput <True|False> (default to True)\n"
- usage_msg = usage_msg + " actionSetVlanId <VLAN ID>\n"
- usage_msg = usage_msg + " actionSetVlanPriority <VLAN priority>\n"
- usage_msg = usage_msg + " actionStripVlan <True|False>\n"
- usage_msg = usage_msg + " actionSetEthernetSrcAddr <source MAC address>\n"
- usage_msg = usage_msg + " actionSetEthernetDstAddr <destination MAC address>\n"
- usage_msg = usage_msg + " actionSetIPv4SrcAddr <source IPv4 address>\n"
- usage_msg = usage_msg + " actionSetIPv4DstAddr <destination IPv4 address>\n"
- usage_msg = usage_msg + " actionSetIpToS <IP ToS (DSCP field, 6 bits)>\n"
- usage_msg = usage_msg + " actionSetTcpUdpSrcPort <source TCP/UDP port>\n"
- usage_msg = usage_msg + " actionSetTcpUdpDstPort <destination TCP/UDP port>\n"
- usage_msg = usage_msg + " Actions (not implemented yet):\n"
- usage_msg = usage_msg + " actionEnqueue <dummy argument>\n"
-
- # app.debug = False;
-
- # Usage info
- if len(sys.argv) > 1 and (sys.argv[1] == "-h" or sys.argv[1] == "--help"):
- print(usage_msg)
- exit(0)
-
- #
- # Check the flags
- #
- start_argv_index = 1
- idx = 1
- while idx < len(sys.argv):
- arg1 = sys.argv[idx]
- idx = idx + 1
- if arg1 == "-f":
- if idx >= len(sys.argv):
- error_arg = "ERROR: Missing or invalid '" + arg1 + "' argument"
- log_error(error_arg)
- log_error(usage_msg)
- exit(1)
- ReadFromFile = sys.argv[idx]
- idx = idx + 1
- start_argv_index = idx
- else:
- break;
-
- #
- # Read the arguments from a file or from the remaining command line options
- #
- my_lines = []
- if len(ReadFromFile) > 0:
- f = open(ReadFromFile, "rt")
- my_line = f.readline()
- while my_line:
- if len(my_line.rstrip()) > 0 and my_line[0] != "#":
- my_token_line = my_line.rstrip().split()
- my_lines.append(my_token_line)
- my_line = f.readline()
- else:
- my_lines.append(copy.deepcopy(sys.argv[start_argv_index:]))
-
- #
- # Initialization
- #
- last_data_paths = []
- parsed_args = []
- idx = 0
- while idx < len(my_lines):
- last_data_path = []
- last_data_paths.append(copy.deepcopy(last_data_path))
- #
- # Parse the flow arguments
- #
- my_args = my_lines[idx]
- parsed_args.append(copy.deepcopy(extract_flow_args(my_args)))
-
- idx = idx + 1
-
- #
- measurement_store_paths(parsed_args)