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

Change-Id: Ib1aba443708a13499f21c08b39b777c876595fac
diff --git a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java
index d119a5a..127c7c9 100644
--- a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java
+++ b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java
@@ -103,7 +103,6 @@
             .register(NodeState.class);
     private static final String DEFAULT_BRIDGE = "br-int";
     private static final String VPORT_PREFIX = "tap";
-    private static final String GWPORT_PREFIX = "qr-";
     private static final String DEFAULT_TUNNEL = "vxlan";
     private static final Map<String, String> DEFAULT_TUNNEL_OPTIONS = new HashMap<String, String>() {
         {
@@ -379,13 +378,14 @@
      * @param node cordvtn node
      */
     private void postInit(CordVtnNode node) {
-        log.info("Initializing {}", node.hostname());
         disconnect(node);
 
         ruleInstaller.init(node.intBrId(), getTunnelPort(node.intBrId()));
         hostService.getConnectedHosts(node.intBrId())
                 .stream()
                 .forEach(vmHandler::connected);
+
+        log.info("Finished initializing {}", node.hostname());
     }
 
     /**
@@ -646,13 +646,16 @@
     private Set<Host> getHostsWithOpenstackNetwork(OpenstackNetwork vNet) {
         checkNotNull(vNet);
 
-        return openstackService.ports(vNet.id()).stream()
+        Set<Host> hosts = openstackService.ports(vNet.id()).stream()
                 .filter(port -> port.deviceOwner().contains("compute"))
                 .map(port -> hostService.getHostsByMac(port.macAddress())
                         .stream()
                         .findFirst()
                         .orElse(null))
                 .collect(Collectors.toSet());
+
+        hosts.remove(null);
+        return hosts;
     }
 
     /**
@@ -687,16 +690,15 @@
     }
 
     /**
-     * Returns if the host is gateway interface.
-     * This codes should be removed after adding proxy arp for the gateway.
+     * Returns if the host is VM or not.
      *
      * @param host host
-     * @return true if the host is gateway
+     * @return true if the host is a VM.
      */
-    private boolean isGateway(Host host) {
+    private boolean isVm(Host host) {
         Port port = deviceService.getPort(host.location().deviceId(),
                                           host.location().port());
-        return port.annotations().value("portName").contains(GWPORT_PREFIX);
+        return port.annotations().value("portName").contains(VPORT_PREFIX);
     }
 
     /**
@@ -892,8 +894,8 @@
 
         @Override
         public void connected(Host host) {
-            // TODO remove check gateway here after applying network config host provider
-            if (isGateway(host)) {
+            // TODO remove check VM here after applying network config host provider
+            if (!isVm(host)) {
                 return;
             }
 
@@ -924,7 +926,11 @@
                     checkNotNull(getRemoteIp(host.location().deviceId())).getIp4Address(),
                     vNet);
 
-            // TODO add new VM to related service group if exists
+            CordService service = getCordService(vNet);
+            // TODO check if the service needs an update on its group buckets after done CORD-433
+            if (service != null) {
+                ruleInstaller.updateServiceGroup(service);
+            }
         }
 
         @Override
@@ -943,7 +949,12 @@
             log.info("VM {} is vanished", host.id());
             ruleInstaller.removeBasicConnectionRules(host);
 
-            // TODO remove the VM from related service group if exists
+            CordService service = getCordService(vNet);
+            // TODO check if the service needs an update on its group buckets after done CORD-433
+            if (service != null) {
+                ruleInstaller.updateServiceGroup(service);
+            }
+
             hostNetMap.remove(host.id());
         }
     }
diff --git a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java
index 4e044e1..1984629 100644
--- a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java
+++ b/apps/cordvtn/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