CORD-417 Update group buckets when a VM is created or terminated

Change-Id: Ib1aba443708a13499f21c08b39b777c876595fac
diff --git a/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java b/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java
index 4e044e1..1984629 100644
--- a/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java
+++ b/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java
@@ -70,6 +70,7 @@
 import org.onosproject.openstackswitching.OpenstackSubnet;
 import org.slf4j.Logger;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
@@ -188,40 +189,6 @@
     }
 
     /**
-     * Populates service dependency rules.
-     *
-     * @param tService tenant cord service
-     * @param pService provider cord service
-     */
-    public void populateServiceDependencyRules(CordService tService, CordService pService) {
-        checkNotNull(tService);
-        checkNotNull(pService);
-
-        Ip4Prefix srcRange = tService.serviceIpRange().getIp4Prefix();
-        Ip4Prefix dstRange = pService.serviceIpRange().getIp4Prefix();
-        Ip4Address serviceIp = pService.serviceIp().getIp4Address();
-
-        Map<DeviceId, GroupId> outGroups = Maps.newHashMap();
-        Map<DeviceId, Set<PortNumber>> inPorts = Maps.newHashMap();
-
-        for (Device device : deviceService.getAvailableDevices(SWITCH)) {
-            GroupId groupId = createServiceGroup(device.id(), pService);
-            outGroups.put(device.id(), groupId);
-
-            Set<PortNumber> vms = tService.hosts().keySet()
-                    .stream()
-                    .filter(host -> host.location().deviceId().equals(device.id()))
-                    .map(host -> host.location().port())
-                    .collect(Collectors.toSet());
-            inPorts.put(device.id(), vms);
-        }
-
-        populateIndirectAccessRule(srcRange, serviceIp, outGroups);
-        populateDirectAccessRule(srcRange, dstRange);
-        populateInServiceRule(inPorts, outGroups);
-    }
-
-    /**
      * Removes basic rules related to a given flow information.
      *
      * @param host host to be removed
@@ -263,13 +230,46 @@
             if (dstIp != null && dstIp.equals(ip.toIpPrefix())) {
                 processFlowRule(false, flowRule);
             }
-
         }
 
         // TODO uninstall same network access rule in access table if no vm exists in the network
     }
 
     /**
+     * Populates service dependency rules.
+     *
+     * @param tService tenant cord service
+     * @param pService provider cord service
+     */
+    public void populateServiceDependencyRules(CordService tService, CordService pService) {
+        checkNotNull(tService);
+        checkNotNull(pService);
+
+        Ip4Prefix srcRange = tService.serviceIpRange().getIp4Prefix();
+        Ip4Prefix dstRange = pService.serviceIpRange().getIp4Prefix();
+        Ip4Address serviceIp = pService.serviceIp().getIp4Address();
+
+        Map<DeviceId, GroupId> outGroups = Maps.newHashMap();
+        Map<DeviceId, Set<PortNumber>> inPorts = Maps.newHashMap();
+
+        for (Device device : deviceService.getAvailableDevices(SWITCH)) {
+            GroupId groupId = createServiceGroup(device.id(), pService);
+            outGroups.put(device.id(), groupId);
+
+            Set<PortNumber> vms = tService.hosts().keySet()
+                    .stream()
+                    .filter(host -> host.location().deviceId().equals(device.id()))
+                    .map(host -> host.location().port())
+                    .collect(Collectors.toSet());
+            inPorts.put(device.id(), vms);
+        }
+
+        populateIndirectAccessRule(srcRange, serviceIp, outGroups);
+        populateDirectAccessRule(srcRange, dstRange);
+        populateInServiceRule(inPorts, outGroups);
+    }
+
+    /**
      * Removes service dependency rules.
      *
      * @param tService tenant cord service
@@ -324,6 +324,58 @@
     }
 
     /**
+     * Updates group buckets for a given service to all devices.
+     *
+     * @param service cord service
+     */
+    public void updateServiceGroup(CordService service) {
+        checkNotNull(service);
+
+        GroupKey groupKey = getGroupKey(service.id());
+
+        for (Device device : deviceService.getAvailableDevices(SWITCH)) {
+            DeviceId deviceId = device.id();
+            if (!mastershipService.isLocalMaster(deviceId)) {
+                continue;
+            }
+
+            Group group = groupService.getGroup(deviceId, groupKey);
+            if (group == null) {
+                log.debug("No group exists for service {} in {}", service.id(), deviceId);
+                continue;
+            }
+
+            List<GroupBucket> oldBuckets = group.buckets().buckets();
+            List<GroupBucket> newBuckets = getServiceGroupBuckets(
+                    deviceId, service.segmentationId(), service.hosts()).buckets();
+
+            if (oldBuckets.equals(newBuckets)) {
+                continue;
+            }
+
+            List<GroupBucket> bucketsToRemove = new ArrayList<>(oldBuckets);
+            bucketsToRemove.removeAll(newBuckets);
+            if (!bucketsToRemove.isEmpty()) {
+                groupService.removeBucketsFromGroup(
+                        deviceId,
+                        groupKey,
+                        new GroupBuckets(bucketsToRemove),
+                        groupKey, appId);
+            }
+
+            List<GroupBucket> bucketsToAdd = new ArrayList<>(newBuckets);
+            bucketsToAdd.removeAll(oldBuckets);
+            if (!bucketsToAdd.isEmpty()) {
+                groupService.addBucketsToGroup(
+                        deviceId,
+                        groupKey,
+                        new GroupBuckets(bucketsToAdd),
+                        groupKey, appId);
+            }
+        }
+    }
+
+    /**
      * Creates a new group for a given service.
      *
      * @param deviceId device id to create a group