Merge "Added note about the URL" into dev/ramcloud-new-datamodel
diff --git a/src/main/java/net/onrc/onos/intent/FlowEntry.java b/src/main/java/net/onrc/onos/intent/FlowEntry.java
index 374e5d6..7aa3d76 100644
--- a/src/main/java/net/onrc/onos/intent/FlowEntry.java
+++ b/src/main/java/net/onrc/onos/intent/FlowEntry.java
@@ -51,7 +51,7 @@
 	public net.onrc.onos.ofcontroller.util.FlowEntry getFlowEntry() {
 		net.onrc.onos.ofcontroller.util.FlowEntry entry = new net.onrc.onos.ofcontroller.util.FlowEntry();
 		entry.setDpid(new Dpid(sw));
-		entry.setFlowEntryId(new FlowEntryId(0)); // all zero for now
+		entry.setFlowEntryId(new FlowEntryId(hashCode())); // naive, but useful for now
 		entry.setFlowEntryMatch(match.getFlowEntryMatch());
 		FlowEntryActions flowEntryActions = new FlowEntryActions();
 		for(Action action : actions) {
@@ -71,6 +71,19 @@
 		return entry;
 	}
 	
-	//TODO: implement hash for cookie
-	//TODO: implement equals (don't include operator!)
-}
\ No newline at end of file
+	
+	public int hashCode() {
+	    return match.hashCode();
+	}
+	
+	public boolean equals(Object o) {
+	    if(!(o instanceof FlowEntry)) {
+		return false;
+	    }
+	    FlowEntry other = (FlowEntry) o;
+	    // Note: we should not consider the operator for this comparison
+	    return this.match.equals(other.match) 
+		&& this.actions.containsAll(other.actions)
+		&& other.actions.containsAll(this.actions);
+	}
+}
diff --git a/src/main/java/net/onrc/onos/intent/ForwardAction.java b/src/main/java/net/onrc/onos/intent/ForwardAction.java
index 6ca514d..9344d8c 100644
--- a/src/main/java/net/onrc/onos/intent/ForwardAction.java
+++ b/src/main/java/net/onrc/onos/intent/ForwardAction.java
@@ -25,5 +25,16 @@
 	    action.setActionOutput(new net.onrc.onos.ofcontroller.util.Port((short) dstPort));
 	    return action;
 	}
+
+	public int hashCode() {
+	    return (int) dstPort;
+	}
 	
-}
\ No newline at end of file
+	public boolean equals(Object o) {
+	    if(!(o instanceof ForwardAction)) {
+		return false;
+	    }
+  	    ForwardAction action = (ForwardAction) o;
+	    return this.dstPort == action.dstPort;
+	}
+}
diff --git a/src/main/java/net/onrc/onos/intent/Intent.java b/src/main/java/net/onrc/onos/intent/Intent.java
index b6a51cb..c6a960f 100644
--- a/src/main/java/net/onrc/onos/intent/Intent.java
+++ b/src/main/java/net/onrc/onos/intent/Intent.java
@@ -54,6 +54,9 @@
 	public IntentState setState(IntentState newState) {
 		logs.add(String.format("setState, oldState:%s, newState:%s, time:%d",
 				state, newState, System.nanoTime())); // for measurement
+		if (logs.size() > 20) { // TODO this size should be configurable
+			logs.removeFirst();
+		}
 		IntentState oldState = state;
 		state = newState;
 		return oldState;
diff --git a/src/main/java/net/onrc/onos/intent/Match.java b/src/main/java/net/onrc/onos/intent/Match.java
index 5adb598..886fb64 100644
--- a/src/main/java/net/onrc/onos/intent/Match.java
+++ b/src/main/java/net/onrc/onos/intent/Match.java
@@ -1,5 +1,7 @@
 package net.onrc.onos.intent;
 
+import java.util.Arrays;
+
 import net.floodlightcontroller.util.MACAddress;
 //import net.onrc.onos.ofcontroller.networkgraph.Port;
 //import net.onrc.onos.ofcontroller.networkgraph.Switch;
@@ -30,9 +32,9 @@
 		if(obj instanceof Match) {
 			Match other = (Match) obj;
 			return this.sw == other.sw &&
-					this.srcMac.equals(other.srcMac) &&
-					this.dstMac.equals(other.dstMac) &&
-					this.srcPort == other.srcPort;
+			       this.srcMac.equals(other.srcMac) &&
+			       this.dstMac.equals(other.dstMac) &&
+			       this.srcPort == other.srcPort;
 		}
 		else {
 			return false;
@@ -51,4 +53,14 @@
 	public String toString() {
 		return "Sw:" + sw + " (" + srcPort + "," + srcMac + "," + dstMac + ")";
 	}
+	
+	@Override
+	public int hashCode() {
+	    long[] nums = new long[4];
+	    nums[0] = sw;
+	    nums[1] = srcPort;
+	    nums[2] = srcMac.toLong();
+	    nums[3] = dstMac.toLong();
+	    return Arrays.hashCode(nums);
+	}
 }
diff --git a/src/main/java/net/onrc/onos/intent/persist/PersistIntent.java b/src/main/java/net/onrc/onos/intent/persist/PersistIntent.java
index 515b0bf..380a0f2 100755
--- a/src/main/java/net/onrc/onos/intent/persist/PersistIntent.java
+++ b/src/main/java/net/onrc/onos/intent/persist/PersistIntent.java
@@ -9,6 +9,8 @@
 import edu.stanford.ramcloud.JRamCloud;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
 import java.util.concurrent.atomic.AtomicLong;
 import net.onrc.onos.datagrid.web.IntentResource;
 import net.onrc.onos.datastore.RCTable;
@@ -29,7 +31,8 @@
     private long range = 10000L;
     private final IControllerRegistryService controllerRegistry;
     NetworkGraph graph = null;
-    private final String intentJournal = "G:IntentJournal";
+    private final static String intentJournal = "G:IntentJournal";
+    private final static int valueStoreLimit = 1024 * 1024;
     private RCTable table;
     private Kryo kryo = new Kryo();
     private ByteArrayOutputStream stream;
@@ -61,6 +64,7 @@
     }
     
     private long getNextBlock() {
+        // XXX This method is not thread safe, may lose allocated IdBlock 
         idBlock = controllerRegistry.allocateUniqueIdBlock(range);
         nextId = new AtomicLong(idBlock.getStart());
         rangeEnd = idBlock.getEnd();
@@ -73,15 +77,40 @@
         // TODO call controllerRegistry.isClusterLeader()
         if (leader) {
             try {
+                // reserve key 10 entries for multi-write if size over 1MB
+                key *= 10;
                 kryo.writeObject(output, operations);
                 output.close();
+                ByteBuffer keyBytes = ByteBuffer.allocate(8).putLong(key);
                 byte[] buffer = stream.toByteArray();
-                table.create(String.valueOf(key).getBytes(), buffer);
+                int total = buffer.length;
+                if ((total >= valueStoreLimit )) {
+                    int writeCount = total / valueStoreLimit;
+                    int remainder = total % valueStoreLimit;
+                    int upperIndex = 0;
+                    for (int i = 0; i < writeCount; i++, key++) {
+                        keyBytes.clear();
+                        keyBytes.putLong(key);
+                        keyBytes.flip();
+                        upperIndex = (i * valueStoreLimit + valueStoreLimit) - 1;
+                        log.debug("writing using indexes {}:{}", (i*valueStoreLimit) ,upperIndex);
+                        table.create(keyBytes.array(), Arrays.copyOfRange(buffer, i * valueStoreLimit, upperIndex));
+                    }
+                    if (remainder > 0) {
+                        keyBytes.clear();
+                        keyBytes.putLong(key);
+                        keyBytes.flip();
+                        log.debug("writing using indexes {}:{}" ,upperIndex ,total);
+                        table.create(keyBytes.array(), Arrays.copyOfRange(buffer, upperIndex + 1, total - 1));
+                    }
+                } else {
+                    keyBytes.flip();
+                    table.create(keyBytes.array(), buffer);
+                }
                 log.debug("key is {} value length is {}", key, buffer.length);
                 stream.reset();
                 stream.close();
                 log.debug("persist operations to ramcloud size of operations: {}", operations.size());
-                if (buffer.length > 921600 ) log.error("oversize key {} value length is {}", key, buffer.length);
                 ret = true;
             } catch (JRamCloud.ObjectExistsException ex) {
                 log.warn("Failed to store intent journal with key " + key);
diff --git a/src/main/java/net/onrc/onos/intent/runtime/PlanCalcRuntime.java b/src/main/java/net/onrc/onos/intent/runtime/PlanCalcRuntime.java
index b12711a..5691c53 100644
--- a/src/main/java/net/onrc/onos/intent/runtime/PlanCalcRuntime.java
+++ b/src/main/java/net/onrc/onos/intent/runtime/PlanCalcRuntime.java
@@ -5,13 +5,11 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import net.floodlightcontroller.util.MACAddress;
 import net.onrc.onos.intent.FlowEntry;
 import net.onrc.onos.intent.Intent;
@@ -23,6 +21,9 @@
 import net.onrc.onos.ofcontroller.networkgraph.LinkEvent;
 //import net.onrc.onos.ofcontroller.networkgraph.NetworkGraph;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 /**
  *
  * @author Brian O'Connor <bocon@onlab.us>
@@ -40,7 +41,7 @@
 
     public List<Set<FlowEntry>> computePlan(IntentOperationList intentOps) {
 	long start = System.nanoTime();
-	Set<Collection<FlowEntry>> flowEntries = computeFlowEntries(intentOps);
+	List<Collection<FlowEntry>> flowEntries = computeFlowEntries(intentOps);
 	long step1 = System.nanoTime();
 	List<Set<FlowEntry>> plan = buildPhases(flowEntries);
 	long step2 = System.nanoTime();
@@ -49,8 +50,8 @@
 	return plan;
     }
 
-    private Set<Collection<FlowEntry>> computeFlowEntries(IntentOperationList intentOps) {
-	Set<Collection<FlowEntry>> flowEntries = new HashSet<>();
+    private List<Collection<FlowEntry>> computeFlowEntries(IntentOperationList intentOps) {
+	List<Collection<FlowEntry>> flowEntries = new LinkedList<>();
 	for(IntentOperation i : intentOps) {
 	    if(!(i.intent instanceof PathIntent)) {
 		log.warn("Not a path intent: {}", i);
@@ -106,7 +107,7 @@
 	return flowEntries;
     }
 
-    private List<Set<FlowEntry>> buildPhases(Set<Collection<FlowEntry>> flowEntries) {
+    private List<Set<FlowEntry>> buildPhases(List<Collection<FlowEntry>> flowEntries) {
 	Map<FlowEntry, Integer> map = new HashMap<>();
 	List<Set<FlowEntry>> plan = new ArrayList<>();
 	for(Collection<FlowEntry> c : flowEntries) {
diff --git a/src/main/java/net/onrc/onos/intent/runtime/PlanInstallModule.java b/src/main/java/net/onrc/onos/intent/runtime/PlanInstallModule.java
index 4d4e437..b8bed85 100644
--- a/src/main/java/net/onrc/onos/intent/runtime/PlanInstallModule.java
+++ b/src/main/java/net/onrc/onos/intent/runtime/PlanInstallModule.java
@@ -2,6 +2,7 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -69,6 +70,9 @@
 		    IntentOperationList intents = intentQueue.take();
 		    //TODO: consider draining the remaining intent lists 
 		    //      and processing in one big batch
+//		    List<IntentOperationList> remaining = new LinkedList<>();
+//		    intentQueue.drainTo(remaining);
+		    
 		    processIntents(intents);
 		} catch (InterruptedException e) {
 		    log.warn("Error taking from intent queue: {}", e.getMessage());
@@ -77,12 +81,20 @@
 	}
 	
 	private void processIntents(IntentOperationList intents) {
+	    log("start_processIntents");
 	    log.debug("Processing OperationList {}", intents);
+	    log("begin_computePlan");
 	    List<Set<FlowEntry>> plan = planCalc.computePlan(intents);
+	    log("end_computePlan");
 	    log.debug("Plan: {}", plan);
+	    log("begin_installPlan");
 	    boolean success = planInstall.installPlan(plan);
+	    log("end_installPlan");
 	    
+	    log("begin_sendInstallNotif");
 	    sendNotifications(intents, true, success);
+	    log("end_sendInstallNotif");
+	    log("finish");
 	}
 	
 	private void sendNotifications(IntentOperationList intents, boolean installed, boolean success) {
@@ -116,8 +128,12 @@
 	
 	@Override
 	public void entryAdded(IntentOperationList value) {
+	    log("start_intentNotifRecv");
+	    log("begin_sendReceivedNotif");
 	    sendNotifications(value, false, false);
-	    
+	    log("end_sendReceivedNotif");
+	    log("finish");
+
 	    log.debug("Added OperationList {}", value);
 	    try {
 		intentQueue.put(value);
@@ -136,6 +152,11 @@
 	    // This channel is a queue, so this method is not needed
 	}
     }
+    
+    public static void log(String step) {
+	log.error("Time:{}, Step:{}", System.nanoTime(), step);
+    }
+    
     @Override
     public void startUp(FloodlightModuleContext context) {
 	// start subscriber
diff --git a/src/main/java/net/onrc/onos/intent/runtime/PlanInstallRuntime.java b/src/main/java/net/onrc/onos/intent/runtime/PlanInstallRuntime.java
index a524875..abd82ed 100644
--- a/src/main/java/net/onrc/onos/intent/runtime/PlanInstallRuntime.java
+++ b/src/main/java/net/onrc/onos/intent/runtime/PlanInstallRuntime.java
@@ -1,17 +1,21 @@
 package net.onrc.onos.intent.runtime;
 
+import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ExecutionException;
 
 import net.floodlightcontroller.core.IFloodlightProviderService;
 import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.core.internal.OFMessageFuture;
 import net.onrc.onos.intent.FlowEntry;
 import net.onrc.onos.ofcontroller.flowprogrammer.IFlowPusherService;
 //import net.onrc.onos.ofcontroller.networkgraph.NetworkGraph;
 import net.onrc.onos.ofcontroller.util.Pair;
 
+import org.openflow.protocol.OFBarrierReply;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -61,9 +65,23 @@
 	    
 	    // TODO: insert a barrier after each phase on each modifiedSwitch
 	    // TODO: wait for confirmation messages before proceeding
+	    List<Pair<IOFSwitch,OFMessageFuture<OFBarrierReply>>> barriers = new ArrayList<>();
+	    for(IOFSwitch sw : modifiedSwitches) {
+		barriers.add(new Pair<>(sw, pusher.barrierAsync(sw)));
+	    }
+	    for(Pair<IOFSwitch,OFMessageFuture<OFBarrierReply>> pair : barriers) {
+		IOFSwitch sw = pair.first;
+		OFMessageFuture<OFBarrierReply> future = pair.second;
+		try {
+		    future.get();
+		} catch (InterruptedException | ExecutionException e) {
+		    log.error("Barrier message not received for sw: {}", sw);
+		}
+	    }
 	}
 	long end = System.nanoTime();
 	log.error("MEASUREMENT: Install plan: {} ns", (end-start));
+	
 	// TODO: we assume that the plan installation succeeds for now
 	return true;
     }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/DeviceEvent.java b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/DeviceEvent.java
index 628b9d4..8bea7f8 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/DeviceEvent.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/DeviceEvent.java
@@ -30,8 +30,9 @@
 
 
     /**
-     * Default constructor.
+     * Default constructor for Serializer to use.
      */
+    @Deprecated
     public DeviceEvent() {
 	mac = null;
     }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/LinkEvent.java b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/LinkEvent.java
index 5fd3b82..03c1a43 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/LinkEvent.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/LinkEvent.java
@@ -15,8 +15,9 @@
     protected final SwitchPort dst;
 
     /**
-     * Default constructor.
+     * Default constructor for Serializer to use.
      */
+    @Deprecated
     public LinkEvent() {
 	src = null;
 	dst = null;
diff --git a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/PortEvent.java b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/PortEvent.java
index 4bd3ea1..801bc1c 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/PortEvent.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/PortEvent.java
@@ -14,8 +14,9 @@
 	public final Long number;
 
 	/**
-	 * Default constructor.
+	 * Default constructor for Serializer to use.
 	 */
+	@Deprecated
 	public SwitchPort() {
 	    dpid = null;
 	    number = null;
@@ -77,8 +78,9 @@
     // TODO Add Description
 
     /**
-     * Default constructor.
+     * Default constructor for Serializer to use.
      */
+    @Deprecated
     public PortEvent() {
 	id = null;
     }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/SwitchEvent.java b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/SwitchEvent.java
index 84b9360..f483e77 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/SwitchEvent.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/SwitchEvent.java
@@ -12,8 +12,9 @@
     protected final Long dpid;
 
     /**
-     * Default constructor.
+     * Default constructor for Serializer to use.
      */
+    @Deprecated
     public SwitchEvent() {
 	dpid = null;
     }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/web/NetworkGraphLinksResource.java b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/web/NetworkGraphLinksResource.java
index a244dc1..16da940 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/web/NetworkGraphLinksResource.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/web/NetworkGraphLinksResource.java
@@ -31,10 +31,13 @@
 		mapper.registerModule(module);
 		
 		try {
+			graph.acquireReadLock();
 			return mapper.writeValueAsString(graph.getLinks());
 		} catch (IOException e) {
 			log.error("Error writing link list to JSON", e);
 			return "";
+		} finally {
+		    graph.releaseReadLock();
 		}
 	}
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/web/NetworkGraphSwitchesResource.java b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/web/NetworkGraphSwitchesResource.java
index 92e94f6..3e2a57c 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/web/NetworkGraphSwitchesResource.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/web/NetworkGraphSwitchesResource.java
@@ -33,11 +33,13 @@
 		mapper.registerModule(module);
 		
 		try {
+			graph.acquireReadLock();
 			return mapper.writeValueAsString(graph.getSwitches());
 		} catch (IOException e) {
 			log.error("Error writing switch list to JSON", e);
 			return "";
+		} finally {
+			graph.releaseReadLock();
 		}
 	}
-
 }
diff --git a/web/get_network_graph.py b/web/get_network_graph.py
new file mode 100755
index 0000000..ed3292b
--- /dev/null
+++ b/web/get_network_graph.py
@@ -0,0 +1,114 @@
+#! /usr/bin/env python
+# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
+
+#
+# Get Network Graph Elements
+#
+
+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
+
+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/onos/ng/links/json ")
+# @app.route("/wm/onos/ng/switches/json ")
+# Sample output:
+
+def print_parsed_result(parsedResult):
+  print '%s' % (parsedResult),
+
+def get_network_switches():
+  try:
+    command = "curl -s \"http://%s:%s/wm/onos/ng/switches/json\"" % (ControllerIP, ControllerPort)
+    debug("get_network_switches %s" % command)
+
+    result = os.popen(command).read()
+    debug("result %s" % result)
+    if len(result) == 0:
+      print "No Switches found"
+      return;
+
+    # parsedResult = result
+    # parsedResult = json.loads(result)
+    parsedResult = json.dumps(json.loads(result), indent=4)
+    debug("parsed %s" % parsedResult)
+  except:
+    log_error("Controller IF has issue")
+    exit(1)
+
+  print_parsed_result(parsedResult)
+
+def get_network_links():
+  try:
+    command = "curl -s \"http://%s:%s/wm/onos/ng/links/json\"" % (ControllerIP, ControllerPort)
+    debug("get_network_links %s" % command)
+
+    result = os.popen(command).read()
+    debug("result %s" % result)
+    if len(result) == 0:
+      print "No Links found"
+      return;
+
+    # parsedResult = result
+    # parsedResult = json.loads(result)
+    parsedResult = json.dumps(json.loads(result), indent=4)
+    debug("parsed %s" % parsedResult)
+  except:
+    log_error("Controller IF has issue")
+    exit(1)
+
+  print_parsed_result(parsedResult)
+
+
+if __name__ == "__main__":
+  usage_msg1 = "Usage:\n"
+  usage_msg2 = "%s <elements_name> : Print network elements with name of <elements_name>\n" % (sys.argv[0])
+  usage_msg3 = "    Valid element names:\n"
+  usage_msg4 = "        all              : Print all network elements\n"
+  usage_msg5 = "        switches         : Print all switches and ports\n"
+  usage_msg6 = "        links            : Print all links\n"
+  usage_msg = usage_msg1 + usage_msg2 + usage_msg3 + usage_msg4 + usage_msg5
+  usage_msg = usage_msg + usage_msg6
+
+  # 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)
+
+  if (sys.argv[1] != "all" and sys.argv[1] != "switches" and sys.argv[1] != "links"):
+    log_error(usage_msg)
+    exit(1)
+
+  # Do the work
+  if (sys.argv[1] == "all" or sys.argv[1] == "switches"):
+    get_network_switches()
+  if (sys.argv[1] == "all" or sys.argv[1] == "links"):
+    get_network_links()