Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
diff --git a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 4494709..9d8259e 100644
--- a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -2,6 +2,9 @@
 
     <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
         <command>
+            <action class="org.onlab.onos.cli.net.FlowsListCommand"/>
+        </command>
+        <command>
             <action class="org.onlab.onos.cli.net.DevicesListCommand"/>
         </command>
         <command>
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java
index fae1fe3..62e331f 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultFlowRule.java
@@ -15,19 +15,20 @@
     private final FlowId id;
     private final long created;
     private final long life;
-    private final long idle;
     private final long packets;
     private final long bytes;
+    private final FlowRuleState state;
 
 
     public DefaultFlowRule(DeviceId deviceId,
-            TrafficSelector selector, TrafficTreatment treatment, int priority) {
+            TrafficSelector selector, TrafficTreatment treatment,
+            int priority, FlowRuleState state) {
         this.deviceId = deviceId;
         this.priority = priority;
         this.selector = selector;
         this.treatment = treatment;
+        this.state = state;
         this.life = 0;
-        this.idle = 0;
         this.packets = 0;
         this.bytes = 0;
         this.id = FlowId.valueOf(this.hashCode());
@@ -35,22 +36,32 @@
     }
 
     public DefaultFlowRule(DeviceId deviceId, TrafficSelector selector,
-            TrafficTreatment treatment, int priority,
-            long life, long idle, long packets, long bytes, Integer flowId) {
+            TrafficTreatment treatment, int priority, FlowRuleState state,
+            long life, long packets, long bytes, Integer flowId) {
         this.deviceId = deviceId;
         this.priority = priority;
         this.selector = selector;
         this.treatment = treatment;
+        this.state = state;
 
         this.id = FlowId.valueOf(flowId);
 
         this.life = life;
-        this.idle = idle;
         this.packets = packets;
         this.bytes = bytes;
         this.created = System.currentTimeMillis();
     }
 
+    public DefaultFlowRule(DeviceId deviceId, TrafficSelector selector,
+            TrafficTreatment treatement, int priority) {
+        this(deviceId, selector, treatement, priority, FlowRuleState.CREATED);
+    }
+
+    public DefaultFlowRule(FlowRule rule, FlowRuleState state) {
+        this(rule.deviceId(), rule.selector(), rule.treatment(),
+                rule.priority(), state);
+    }
+
 
     @Override
     public FlowId id() {
@@ -83,11 +94,6 @@
     }
 
     @Override
-    public long idleMillis() {
-        return idle;
-    }
-
-    @Override
     public long packets() {
         return packets;
     }
@@ -98,6 +104,12 @@
     }
 
     @Override
+    public FlowRuleState state() {
+        return this.state;
+    }
+
+
+    @Override
     /*
      * The priority and statistics can change on a given treatment and selector
      *
@@ -116,19 +128,14 @@
      * @see java.lang.Object#equals(java.lang.Object)
      */
     public boolean equals(Object obj) {
-        if (obj instanceof FlowRule) {
-            DefaultFlowRule that = (DefaultFlowRule) obj;
-            if (!this.deviceId().equals(that.deviceId())) {
-                return false;
-            }
-            if (!this.treatment().equals(that.treatment())) {
-                return false;
-            }
-            if (!this.selector().equals(that.selector())) {
-                return false;
-            }
+        if (this == obj) {
             return true;
         }
+        if (obj instanceof FlowRule) {
+            FlowRule that = (FlowRule) obj;
+            return Objects.equals(deviceId, that.deviceId()) &&
+                    Objects.equals(id, that.id());
+        }
         return false;
     }
 
@@ -144,5 +151,4 @@
                 .toString();
     }
 
-
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowId.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowId.java
index 03140f7..c7c44cb 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowId.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowId.java
@@ -1,13 +1,15 @@
 package org.onlab.onos.net.flow;
 
+import com.google.common.base.Objects;
+
 /**
  * Representation of a Flow ID.
  */
 public final class FlowId {
 
-    private final int flowid;
+    private final long flowid;
 
-    private FlowId(int id) {
+    private FlowId(long id) {
         this.flowid = id;
     }
 
@@ -15,7 +17,24 @@
         return new FlowId(id);
     }
 
-    public int value() {
+    public long value() {
         return flowid;
     }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj.getClass()  == this.getClass()) {
+            FlowId that = (FlowId) obj;
+            return Objects.equal(this.flowid, that.flowid);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(this.flowid);
+    }
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRule.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRule.java
index f2bc1a0..e72beed 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRule.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRule.java
@@ -8,6 +8,42 @@
  */
 public interface FlowRule {
 
+
+    public enum FlowRuleState {
+        /**
+         * Indicates that this rule has been created.
+         */
+        CREATED,
+
+        /**
+         * Indicates that this rule has been submitted for addition.
+         * Not necessarily in  the flow table.
+         */
+        PENDING_ADD,
+
+        /**
+         * Rule has been added which means it is in the flow table.
+         */
+        ADDED,
+
+        /**
+         * Flow has been marked for removal, might still be in flow table.
+         */
+        PENDING_REMOVE,
+
+        /**
+         * Flow has been removed from flow table and can be purged.
+         */
+        REMOVED
+    }
+
+    /**
+     * Returns the flow rule state.
+     *
+     * @return flow rule state
+     */
+    FlowRuleState state();
+
     //TODO: build cookie value
     /**
      * Returns the ID of this flow.
@@ -54,13 +90,6 @@
     long lifeMillis();
 
     /**
-     * Returns the number of milliseconds this flow rule has been idle.
-     *
-     * @return number of millis
-     */
-    long idleMillis();
-
-    /**
      * Returns the number of packets this flow rule has matched.
      *
      * @return number of packets
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProviderService.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProviderService.java
index 737ac28..df988fe 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProviderService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleProviderService.java
@@ -1,5 +1,6 @@
 package org.onlab.onos.net.flow;
 
+import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.provider.ProviderService;
 
 /**
@@ -35,6 +36,6 @@
      *
      * @param flowRules collection of flow rules
      */
-    void pushFlowMetrics(Iterable<FlowRule> flowRules);
+    void pushFlowMetrics(DeviceId deviceId, Iterable<FlowRule> flowRules);
 
 }
diff --git a/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java b/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
index 41f7303..5bd6fed 100644
--- a/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
@@ -4,6 +4,7 @@
 import static org.slf4j.LoggerFactory.getLogger;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 
 import org.apache.felix.scr.annotations.Activate;
@@ -17,7 +18,9 @@
 import org.onlab.onos.net.Device;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.device.DeviceService;
+import org.onlab.onos.net.flow.DefaultFlowRule;
 import org.onlab.onos.net.flow.FlowRule;
+import org.onlab.onos.net.flow.FlowRule.FlowRuleState;
 import org.onlab.onos.net.flow.FlowRuleEvent;
 import org.onlab.onos.net.flow.FlowRuleListener;
 import org.onlab.onos.net.flow.FlowRuleProvider;
@@ -29,17 +32,19 @@
 import org.onlab.onos.net.provider.AbstractProviderService;
 import org.slf4j.Logger;
 
+import com.google.common.collect.Lists;
+
 @Component(immediate = true)
 @Service
 public class FlowRuleManager
-        extends AbstractProviderRegistry<FlowRuleProvider, FlowRuleProviderService>
-        implements FlowRuleService, FlowRuleProviderRegistry {
+extends AbstractProviderRegistry<FlowRuleProvider, FlowRuleProviderService>
+implements FlowRuleService, FlowRuleProviderRegistry {
 
     public static final String FLOW_RULE_NULL = "FlowRule cannot be null";
     private final Logger log = getLogger(getClass());
 
     private final AbstractListenerRegistry<FlowRuleEvent, FlowRuleListener>
-            listenerRegistry = new AbstractListenerRegistry<>();
+    listenerRegistry = new AbstractListenerRegistry<>();
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected FlowRuleStore store;
@@ -72,7 +77,7 @@
         List<FlowRule> entries = new ArrayList<FlowRule>();
 
         for (int i = 0; i < flowRules.length; i++) {
-            FlowRule f = flowRules[i];
+            FlowRule f = new DefaultFlowRule(flowRules[i], FlowRuleState.PENDING_ADD);
             final Device device = deviceService.getDevice(f.deviceId());
             final FlowRuleProvider frp = getProvider(device.providerId());
             entries.add(store.storeFlowRule(f));
@@ -85,7 +90,7 @@
     @Override
     public void removeFlowRules(FlowRule... flowRules) {
         for (int i = 0; i < flowRules.length; i++) {
-            FlowRule f = flowRules[i];
+            FlowRule f = new DefaultFlowRule(flowRules[i], FlowRuleState.PENDING_REMOVE);
             final Device device = deviceService.getDevice(f.deviceId());
             final FlowRuleProvider frp = getProvider(device.providerId());
             store.removeFlowRule(f);
@@ -111,8 +116,8 @@
     }
 
     private class InternalFlowRuleProviderService
-            extends AbstractProviderService<FlowRuleProvider>
-            implements FlowRuleProviderService {
+    extends AbstractProviderService<FlowRuleProvider>
+    implements FlowRuleProviderService {
 
         protected InternalFlowRuleProviderService(FlowRuleProvider provider) {
             super(provider);
@@ -160,8 +165,32 @@
         }
 
         @Override
-        public void pushFlowMetrics(Iterable<FlowRule> flowEntries) {
-            // TODO Auto-generated method stub
+        public void pushFlowMetrics(DeviceId deviceId, Iterable<FlowRule> flowEntries) {
+            List<FlowRule> storedRules = Lists.newLinkedList(store.getFlowEntries(deviceId));
+            List<FlowRule> switchRules = Lists.newLinkedList(flowEntries);
+            Iterator<FlowRule> switchRulesIterator = switchRules.iterator();
+            List<FlowRule> extraRules = Lists.newLinkedList();
+
+            while (switchRulesIterator.hasNext()) {
+                FlowRule rule = switchRulesIterator.next();
+                if (storedRules.remove(rule)) {
+                    // we both have the rule let's update some info then.
+                    log.info("rule {} is added. {}", rule.id(), rule.state());
+                    flowAdded(rule);
+                } else {
+                    // the device a rule the store does not have
+                    extraRules.add(rule);
+                }
+            }
+            for (FlowRule rule : storedRules) {
+                // there are rules in the store that aren't on the switch
+                flowMissing(rule);
+            }
+            if (extraRules.size() > 0) {
+                log.warn("Device {} has extra flow rules: {}", deviceId, extraRules);
+                // TODO do something with this.
+            }
+
 
         }
     }
diff --git a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowRuleBuilder.java b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowRuleBuilder.java
index ab01ab7..49ee9b9 100644
--- a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowRuleBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowRuleBuilder.java
@@ -10,6 +10,7 @@
 import org.onlab.onos.net.flow.DefaultTrafficSelector;
 import org.onlab.onos.net.flow.DefaultTrafficTreatment;
 import org.onlab.onos.net.flow.FlowRule;
+import org.onlab.onos.net.flow.FlowRule.FlowRuleState;
 import org.onlab.onos.net.flow.TrafficSelector;
 import org.onlab.onos.net.flow.TrafficTreatment;
 import org.onlab.onos.net.flow.criteria.Criteria;
@@ -69,14 +70,14 @@
         if (stat != null) {
             return new DefaultFlowRule(DeviceId.deviceId(Dpid.uri(dpid)),
                     buildSelector(), buildTreatment(), stat.getPriority(),
-                    stat.getDurationNsec() / 1000000, stat.getIdleTimeout(),
+                    FlowRuleState.ADDED, stat.getDurationNsec() / 1000000,
                     stat.getPacketCount().getValue(), stat.getByteCount().getValue(),
                     (int) (stat.getCookie().getValue() & 0xFFFFFFFF));
         } else {
             // TODO: revisit potentially.
             return new DefaultFlowRule(DeviceId.deviceId(Dpid.uri(dpid)),
                     buildSelector(), null, removed.getPriority(),
-                    removed.getDurationNsec() / 1000000, removed.getIdleTimeout(),
+                    FlowRuleState.REMOVED, removed.getDurationNsec() / 1000000,
                     removed.getPacketCount().getValue(), removed.getByteCount().getValue(),
                     (int) (removed.getCookie().getValue() & 0xFFFFFFFF));
         }
diff --git a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java
index b11eb0c..7cb4fdf 100644
--- a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java
+++ b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java
@@ -10,6 +10,7 @@
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.flow.FlowRule;
 import org.onlab.onos.net.flow.FlowRuleProvider;
 import org.onlab.onos.net.flow.FlowRuleProviderRegistry;
@@ -154,7 +155,7 @@
                 entries.add(new FlowRuleBuilder(dpid, reply).build());
             }
             log.debug("sending flowstats to core {}", entries);
-            providerService.pushFlowMetrics(entries);
+            providerService.pushFlowMetrics(DeviceId.deviceId(Dpid.uri(dpid)), entries);
         }
 
     }