ONOS-895: Group manager implementation

Change-Id: Ie183f722fa39012f8de056961715c325e2388e63
diff --git a/core/api/src/main/java/org/onosproject/core/DefaultGroupId.java b/core/api/src/main/java/org/onosproject/core/DefaultGroupId.java
index 11ca73c..58ae9a9 100644
--- a/core/api/src/main/java/org/onosproject/core/DefaultGroupId.java
+++ b/core/api/src/main/java/org/onosproject/core/DefaultGroupId.java
@@ -37,7 +37,7 @@
 
     @Override
     public int id() {
-        return 0;
+        return this.id;
     }
 
     @Override
diff --git a/core/api/src/main/java/org/onosproject/net/group/DefaultGroup.java b/core/api/src/main/java/org/onosproject/net/group/DefaultGroup.java
index 0e4cac1..eba2c6c 100644
--- a/core/api/src/main/java/org/onosproject/net/group/DefaultGroup.java
+++ b/core/api/src/main/java/org/onosproject/net/group/DefaultGroup.java
@@ -17,7 +17,10 @@
 
 import static org.slf4j.LoggerFactory.getLogger;
 
+import java.util.Objects;
+
 import org.onosproject.core.GroupId;
+import org.onosproject.net.DeviceId;
 import org.slf4j.Logger;
 
 /**
@@ -32,6 +35,7 @@
     private long life;
     private long packets;
     private long bytes;
+    private long referenceCount;
     private GroupId id;
 
     /**
@@ -47,6 +51,29 @@
         this.life = 0;
         this.packets = 0;
         this.bytes = 0;
+        this.referenceCount = 0;
+    }
+
+    /**
+     * Default group object constructor with the available information
+     * from data plane.
+     *
+     * @param id group identifier
+     * @param deviceId device identifier
+     * @param type type of the group
+     * @param buckets immutable list of group bucket
+     */
+    public DefaultGroup(GroupId id,
+                        DeviceId deviceId,
+                        GroupDescription.Type type,
+                        GroupBuckets buckets) {
+        super(deviceId, type, buckets);
+        this.id = id;
+        this.state = GroupState.PENDING_ADD;
+        this.life = 0;
+        this.packets = 0;
+        this.bytes = 0;
+        this.referenceCount = 0;
     }
 
     /**
@@ -139,4 +166,43 @@
         this.bytes = bytes;
     }
 
+    @Override
+    public void setReferenceCount(long referenceCount) {
+        this.referenceCount = referenceCount;
+    }
+
+    @Override
+    public long referenceCount() {
+        return referenceCount;
+    }
+
+    /*
+     * The deviceId, type and buckets are used for hash.
+     *
+     * (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public int hashCode() {
+        return super.hashCode() + Objects.hash(id);
+    }
+
+    /*
+     * The deviceId, groupId, type and buckets should be same.
+     *
+     * (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+       if (obj instanceof DefaultGroup) {
+            DefaultGroup that = (DefaultGroup) obj;
+            return super.equals(obj) &&
+                    Objects.equals(id, that.id);
+        }
+        return false;
+    }
 }
diff --git a/core/api/src/main/java/org/onosproject/net/group/DefaultGroupBucket.java b/core/api/src/main/java/org/onosproject/net/group/DefaultGroupBucket.java
index 3fab387..931cc71 100644
--- a/core/api/src/main/java/org/onosproject/net/group/DefaultGroupBucket.java
+++ b/core/api/src/main/java/org/onosproject/net/group/DefaultGroupBucket.java
@@ -18,6 +18,8 @@
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.util.Objects;
+
 import org.onosproject.core.GroupId;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.TrafficTreatment;
@@ -178,4 +180,36 @@
     public GroupId watchGroup() {
         return watchGroup;
     }
+
+    /*
+     * The type and treatment can change on a given bucket
+     *
+     * (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(type, treatment);
+    }
+
+    /*
+     * The priority and statistics can change on a given treatment and selector
+     *
+     * (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof DefaultGroupBucket) {
+            DefaultGroupBucket that = (DefaultGroupBucket) obj;
+            return Objects.equals(type, that.type) &&
+                   this.treatment.instructions().containsAll(that.treatment.instructions()) &&
+                   that.treatment.instructions().containsAll(this.treatment.instructions());
+        }
+        return false;
+    }
+
 }
diff --git a/core/api/src/main/java/org/onosproject/net/group/DefaultGroupDescription.java b/core/api/src/main/java/org/onosproject/net/group/DefaultGroupDescription.java
index 25af506..8d374c1 100644
--- a/core/api/src/main/java/org/onosproject/net/group/DefaultGroupDescription.java
+++ b/core/api/src/main/java/org/onosproject/net/group/DefaultGroupDescription.java
@@ -17,6 +17,8 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.util.Objects;
+
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.DeviceId;
 
@@ -49,8 +51,8 @@
         this.type = checkNotNull(type);
         this.deviceId = checkNotNull(deviceId);
         this.buckets = checkNotNull(buckets);
-        this.appCookie = checkNotNull(appCookie);
-        this.appId = checkNotNull(appId);
+        this.appCookie = appCookie;
+        this.appId = appId;
     }
 
     /**
@@ -61,11 +63,27 @@
      *
      */
     public DefaultGroupDescription(GroupDescription groupDesc) {
-        this.type = checkNotNull(groupDesc.type());
-        this.deviceId = checkNotNull(groupDesc.deviceId());
-        this.buckets = checkNotNull(groupDesc.buckets());
-        this.appCookie = checkNotNull(groupDesc.appCookie());
-        this.appId = checkNotNull(groupDesc.appId());
+        this.type = groupDesc.type();
+        this.deviceId = groupDesc.deviceId();
+        this.buckets = groupDesc.buckets();
+        this.appCookie = groupDesc.appCookie();
+        this.appId = groupDesc.appId();
+    }
+
+    /**
+     * Constructor to be used by group subsystem internal components.
+     * Creates group description object from the information retrieved
+     * from data plane.
+     *
+     * @param deviceId device identifier
+     * @param type type of the group
+     * @param buckets immutable list of group bucket
+     *
+     */
+    public DefaultGroupDescription(DeviceId deviceId,
+                                   GroupDescription.Type type,
+                                   GroupBuckets buckets) {
+        this(deviceId, type, buckets, null, null);
     }
 
     /**
@@ -118,4 +136,36 @@
         return this.buckets;
     }
 
+    @Override
+    /*
+     * The deviceId, type and buckets are used for hash.
+     *
+     * (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public int hashCode() {
+        return Objects.hash(deviceId, type, buckets);
+    }
+
+    @Override
+    /*
+     * The deviceId, type and buckets should be same.
+     *
+     * (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+       if (obj instanceof DefaultGroupDescription) {
+            DefaultGroupDescription that = (DefaultGroupDescription) obj;
+            return Objects.equals(deviceId, that.deviceId) &&
+                    Objects.equals(type, that.type) &&
+                    Objects.equals(buckets, that.buckets);
+
+        }
+        return false;
+    }
+
 }
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onosproject/net/group/Group.java b/core/api/src/main/java/org/onosproject/net/group/Group.java
index f7fa507..b7872de 100644
--- a/core/api/src/main/java/org/onosproject/net/group/Group.java
+++ b/core/api/src/main/java/org/onosproject/net/group/Group.java
@@ -26,6 +26,10 @@
      */
     public enum GroupState {
         /**
+         * Group create request is queued as group AUDIT is in progress.
+         */
+        WAITING_AUDIT_COMPLETE,
+        /**
          * Group create request is processed by ONOS and not yet
          * received the confirmation from data plane.
          */
@@ -81,4 +85,11 @@
      * @return number of bytes
      */
     long bytes();
+
+    /**
+     * Returns the number of flow rules or other groups reference this group.
+     *
+     * @return number of flow rules or other groups pointing to this group
+     */
+    long referenceCount();
 }
diff --git a/core/api/src/main/java/org/onosproject/net/group/GroupBuckets.java b/core/api/src/main/java/org/onosproject/net/group/GroupBuckets.java
index 10f4eca..5ca8f30 100644
--- a/core/api/src/main/java/org/onosproject/net/group/GroupBuckets.java
+++ b/core/api/src/main/java/org/onosproject/net/group/GroupBuckets.java
@@ -45,4 +45,25 @@
         return buckets;
     }
 
+    @Override
+    public int hashCode() {
+        int result = 17;
+        int combinedHash = 0;
+        for (GroupBucket bucket:buckets) {
+            combinedHash = combinedHash + bucket.hashCode();
+        }
+        result = 31 * result + combinedHash;
+
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof GroupBuckets) {
+            return (this.buckets.containsAll(((GroupBuckets) obj).buckets) &&
+                    ((GroupBuckets) obj).buckets.containsAll(this.buckets));
+        }
+        return false;
+    }
+
 }
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onosproject/net/group/GroupOperation.java b/core/api/src/main/java/org/onosproject/net/group/GroupOperation.java
index 44d7e88..5a66aca 100644
--- a/core/api/src/main/java/org/onosproject/net/group/GroupOperation.java
+++ b/core/api/src/main/java/org/onosproject/net/group/GroupOperation.java
@@ -17,6 +17,8 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.util.Objects;
+
 import org.onosproject.core.GroupId;
 
 /**
@@ -142,4 +144,37 @@
     public GroupBuckets buckets() {
         return this.buckets;
     }
+
+    @Override
+    /*
+     * The deviceId, type and buckets are used for hash.
+     *
+     * (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public int hashCode() {
+        return (buckets != null) ? Objects.hash(groupId, opType, buckets) :
+            Objects.hash(groupId, opType);
+    }
+
+    @Override
+    /*
+     * The deviceId, type and buckets should be same.
+     *
+     * (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+       if (obj instanceof GroupOperation) {
+           GroupOperation that = (GroupOperation) obj;
+           return Objects.equals(groupId, that.groupId) &&
+                   Objects.equals(opType, that.opType) &&
+                   Objects.equals(buckets, that.buckets);
+
+        }
+        return false;
+    }
 }
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onosproject/net/group/GroupProviderRegistry.java b/core/api/src/main/java/org/onosproject/net/group/GroupProviderRegistry.java
new file mode 100644
index 0000000..d45789d
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/group/GroupProviderRegistry.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.group;
+
+import org.onosproject.net.provider.ProviderRegistry;
+
+/**
+ * Abstraction for a group provider registry.
+ */
+public interface GroupProviderRegistry
+        extends ProviderRegistry<GroupProvider, GroupProviderService> {
+}
diff --git a/core/api/src/main/java/org/onosproject/net/group/GroupService.java b/core/api/src/main/java/org/onosproject/net/group/GroupService.java
index 1fd9984..8502aea 100644
--- a/core/api/src/main/java/org/onosproject/net/group/GroupService.java
+++ b/core/api/src/main/java/org/onosproject/net/group/GroupService.java
@@ -16,7 +16,6 @@
 package org.onosproject.net.group;
 
 import org.onosproject.core.ApplicationId;
-import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 
 /**
@@ -27,7 +26,7 @@
  * specified in a group.
  * "group" can also be used for grouping common actions of different flows,
  * so that in some scenarios only one group entry required to be modified
- * for all the referencing flow entries instead of modifying all of them
+ * for all the referencing flow entries instead of modifying all of them.
  *
  * This implements semantics of a distributed authoritative group store
  * where the master copy of the groups lies with the controller and
@@ -60,7 +59,7 @@
      * NOTE1: The presence of group object in the system does not
      * guarantee that the "group" is actually created in device.
      * GROUP_ADDED notification would confirm the creation of
-     * this group in data plane
+     * this group in data plane.
      *
      * @param deviceId device identifier
      * @param appCookie application cookie to be used for lookup
@@ -73,7 +72,7 @@
      * Appends buckets to existing group. The caller can optionally
      * associate a new cookie during this updation. GROUP_UPDATED or
      * GROUP_UPDATE_FAILED notifications would be provided along with
-     * cookie depending on the result of the operation on the device
+     * cookie depending on the result of the operation on the device.
      *
      * @param deviceId device identifier
      * @param oldCookie cookie to be used to retrieve the existing group
@@ -91,7 +90,7 @@
      * Removes buckets from existing group. The caller can optionally
      * associate a new cookie during this updation. GROUP_UPDATED or
      * GROUP_UPDATE_FAILED notifications would be provided along with
-     * cookie depending on the result of the operation on the device
+     * cookie depending on the result of the operation on the device.
      *
      * @param deviceId device identifier
      * @param oldCookie cookie to be used to retrieve the existing group
@@ -99,7 +98,7 @@
      * @param newCookie immutable cookie to be used post update operation
      * @param appId Application Id
      */
-    void removeBucketsFromGroup(Device deviceId,
+    void removeBucketsFromGroup(DeviceId deviceId,
                                 GroupKey oldCookie,
                                 GroupBuckets buckets,
                                 GroupKey newCookie,
@@ -109,13 +108,13 @@
      * Deletes a group associated to an application cookie.
      * GROUP_DELETED or GROUP_DELETE_FAILED notifications would be
      * provided along with cookie depending on the result of the
-     * operation on the device
+     * operation on the device.
      *
      * @param deviceId device identifier
      * @param appCookie application cookie to be used for lookup
      * @param appId Application Id
      */
-    void removeGroup(Device deviceId, GroupKey appCookie, ApplicationId appId);
+    void removeGroup(DeviceId deviceId, GroupKey appCookie, ApplicationId appId);
 
     /**
      * Retrieves all groups created by an application in the specified device
@@ -125,7 +124,7 @@
      * @param appId application id
      * @return collection of immutable group objects created by the application
      */
-    Iterable<Group> getGroups(Device deviceId, ApplicationId appId);
+    Iterable<Group> getGroups(DeviceId deviceId, ApplicationId appId);
 
     /**
      * Adds the specified group listener.
diff --git a/core/api/src/main/java/org/onosproject/net/group/GroupStore.java b/core/api/src/main/java/org/onosproject/net/group/GroupStore.java
index 22914f9..2fc7030 100644
--- a/core/api/src/main/java/org/onosproject/net/group/GroupStore.java
+++ b/core/api/src/main/java/org/onosproject/net/group/GroupStore.java
@@ -73,12 +73,14 @@
      * @param deviceId the device ID
      * @param oldAppCookie the current group key
      * @param type update type
-     * @param newGroupDesc group description with updates
+     * @param newBuckets group buckets for updates
+     * @param newAppCookie optional new group key
      */
     void updateGroupDescription(DeviceId deviceId,
                                 GroupKey oldAppCookie,
                                 UpdateType type,
-                                GroupDescription newGroupDesc);
+                                GroupBuckets newBuckets,
+                                GroupKey newAppCookie);
 
     /**
      * Triggers deleting the existing group entry.
@@ -102,4 +104,43 @@
      * @param group group entry
      */
     void removeGroupEntry(Group group);
+
+    /**
+     * A group entry that is present in switch but not in the store.
+     *
+     * @param group group entry
+     */
+    void addOrUpdateExtraneousGroupEntry(Group group);
+
+    /**
+     * Remove the group entry from extraneous database.
+     *
+     * @param group group entry
+     */
+    void removeExtraneousGroupEntry(Group group);
+
+    /**
+     * Returns the extraneous groups associated with a device.
+     *
+     * @param deviceId the device ID
+     *
+     * @return the extraneous group entries
+     */
+    Iterable<Group> getExtraneousGroups(DeviceId deviceId);
+
+    /**
+     * Indicates the first group audit is completed.
+     *
+     * @param deviceId the device ID
+     */
+    void deviceInitialAuditCompleted(DeviceId deviceId);
+
+    /**
+     * Retrieves the initial group audit status for a device.
+     *
+     * @param deviceId the device ID
+     *
+     * @return initial group audit status
+     */
+    boolean deviceInitialAuditStatus(DeviceId deviceId);
 }
diff --git a/core/api/src/main/java/org/onosproject/net/group/StoredGroupEntry.java b/core/api/src/main/java/org/onosproject/net/group/StoredGroupEntry.java
index b3557b4..297663f 100644
--- a/core/api/src/main/java/org/onosproject/net/group/StoredGroupEntry.java
+++ b/core/api/src/main/java/org/onosproject/net/group/StoredGroupEntry.java
@@ -49,4 +49,10 @@
      */
     void setBytes(long bytes);
 
+    /**
+     * Sets number of flow rules or groups referencing this group entry.
+     *
+     * @param referenceCount reference count
+     */
+    void setReferenceCount(long referenceCount);
 }