Group event indicating failover of previously used live port

Change-Id: I32749b38d5e4fab93fa97bbf6587bd0dc91db88c
diff --git a/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java b/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java
index 83100f4..159b7dd 100644
--- a/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java
+++ b/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java
@@ -18,7 +18,10 @@
 
 import static org.slf4j.LoggerFactory.getLogger;
 
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.concurrent.atomic.AtomicLong;
@@ -31,9 +34,12 @@
 import org.onosproject.core.DefaultGroupId;
 import org.onosproject.core.GroupId;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.group.DefaultGroup;
 import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBucket;
 import org.onosproject.net.group.GroupBuckets;
 import org.onosproject.net.group.GroupDescription;
 import org.onosproject.net.group.GroupOperation;
@@ -42,6 +48,7 @@
 import org.onosproject.net.group.GroupProvider;
 import org.onosproject.net.group.GroupProviderRegistry;
 import org.onosproject.net.group.GroupProviderService;
+import org.onosproject.net.group.GroupService;
 import org.onosproject.net.group.StoredGroupBucketEntry;
 import org.onosproject.net.provider.AbstractProvider;
 import org.onosproject.net.provider.ProviderId;
@@ -62,6 +69,7 @@
 import org.projectfloodlight.openflow.protocol.OFGroupStatsReply;
 import org.projectfloodlight.openflow.protocol.OFGroupType;
 import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
 import org.projectfloodlight.openflow.protocol.OFPortStatus;
 import org.projectfloodlight.openflow.protocol.OFStatsReply;
 import org.projectfloodlight.openflow.protocol.OFStatsType;
@@ -88,6 +96,12 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected DriverService driverService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected GroupService groupService;
+
     private GroupProviderService providerService;
 
     static final int POLL_INTERVAL = 10;
@@ -389,6 +403,7 @@
 
         @Override
         public void portChanged(Dpid dpid, OFPortStatus status) {
+            providerService.notifyOfFailovers(checkFailoverGroups(dpid, status));
         }
 
         @Override
@@ -396,4 +411,58 @@
         }
     }
 
+    /**
+     * Builds a list of failover Groups whose primary live bucket failed over
+     * (i.e. bucket in use has changed).
+     *
+     * @param dpid    DPID of switch whose port's status changed
+     * @param status  new status of port
+     * @return        list of groups whose primary live bucket failed over
+     */
+    private List<Group> checkFailoverGroups(Dpid dpid, OFPortStatus status) {
+        List<Group> groupList = new ArrayList<>();
+        OFPortDesc desc = status.getDesc();
+        PortNumber portNumber = PortNumber.portNumber(desc.getPortNo().getPortNumber());
+        DeviceId id = DeviceId.deviceId(Dpid.uri(dpid));
+        if (desc.isEnabled()) {
+            return groupList;
+        }
+        Iterator<Group> iterator = groupService.getGroups(id).iterator();
+        while (iterator.hasNext()) {
+            Group group = iterator.next();
+            if (group.type() == GroupDescription.Type.FAILOVER &&
+                    checkFailoverGroup(group, id, portNumber)) {
+                groupList.add(group);
+            }
+        }
+    return groupList;
+    }
+
+    /**
+     * Checks whether the first live port in the failover group's bucket
+     * has failed over.
+     *
+     * @param group       failover group to be checked for failover
+     * @param id          device ID of switch whose port's status changed
+     * @param portNumber  port number of port that was disabled
+     * @return            whether the failover group experienced failover
+     */
+    private boolean checkFailoverGroup(Group group, DeviceId id,
+                                       PortNumber portNumber) {
+        boolean portReached = false;
+        boolean portEnabled = false;
+        Iterator<GroupBucket> bIterator = group.buckets().buckets().iterator();
+        GroupBucket bucket;
+        while (bIterator.hasNext() && !portReached) {
+            bucket = bIterator.next();
+            if (deviceService.getPort(id, bucket.watchPort()).isEnabled()) {
+                portEnabled = true;
+            }
+            if (bucket.watchPort().equals(portNumber)) {
+                portReached = true;
+            }
+        }
+        return portReached && !portEnabled;
+    }
+
 }