Fixed to not to create gateway group if it is already exists

Also changed some names shorter.

Change-Id: Iaa8aa5ac378fc168e79c9e238090ca817af42261
diff --git a/apps/scalablegateway/src/main/java/org/onosproject/scalablegateway/impl/ScalableGatewayManager.java b/apps/scalablegateway/src/main/java/org/onosproject/scalablegateway/impl/ScalableGatewayManager.java
index 37e601c..5e1d2b0 100644
--- a/apps/scalablegateway/src/main/java/org/onosproject/scalablegateway/impl/ScalableGatewayManager.java
+++ b/apps/scalablegateway/src/main/java/org/onosproject/scalablegateway/impl/ScalableGatewayManager.java
@@ -16,6 +16,7 @@
 
 package org.onosproject.scalablegateway.impl;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -25,6 +26,7 @@
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.util.KryoNamespace;
+import org.onlab.util.Tools;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 
@@ -43,13 +45,14 @@
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.group.Group;
-import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupKey;
 import org.onosproject.net.group.GroupService;
 import org.onosproject.scalablegateway.api.GatewayNode;
 import org.onosproject.scalablegateway.api.GatewayNodeConfig;
 import org.onosproject.scalablegateway.api.ScalableGatewayService;
 
 import java.util.List;
+import java.util.Objects;
 import java.util.Optional;
 
 import org.onosproject.store.serializers.KryoNamespaces;
@@ -60,7 +63,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.AnnotationKeys.PORT_NAME;
 
 /**
  * Manages gateway node for gateway scalability.
@@ -74,11 +77,6 @@
     private ApplicationId appId;
     private static final String APP_ID = "org.onosproject.scalablegateway";
     private static final String APP_NAME = "scalablegateway";
-    private static final String GATEWAYNODE_CAN_NOT_BE_NULL = "The gateway node can not be null";
-    private static final String PORT_CAN_NOT_BE_NULL = "The port can not be null";
-    private static final String FAIL_ADD_GATEWAY = "Adding process is failed as existing deivce id";
-    private static final String FAIL_REMOVE_GATEWAY = "Removing process is failed as unknown deivce id";
-    private static final String PORT_NAME = "portName";
     private static final String GATEWAYNODE_MAP_NAME = "gatewaynode-map";
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -106,7 +104,7 @@
     private SelectGroupHandler selectGroupHandler;
 
     private final NetworkConfigListener configListener = new InternalConfigListener();
-    private InternalDeviceListener internalDeviceListener = new InternalDeviceListener();
+    private final InternalDeviceListener internalDeviceListener = new InternalDeviceListener();
 
     private final ConfigFactory configFactory =
             new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, GatewayNodeConfig.class, APP_NAME) {
@@ -115,10 +113,10 @@
                     return new GatewayNodeConfig();
                 }
             };
-    private ConsistentMap<DeviceId, GatewayNode> gatewayNodeMap; // Map<GatewayNode`s Id, GatewayNode object>
+
+    private ConsistentMap<DeviceId, GatewayNode> gatewayNodeMap; // Map<GatewayNode Id, GatewayNode object>
     private static final KryoNamespace.Builder GATEWAYNODE_SERIALIZER = KryoNamespace.newBuilder()
             .register(KryoNamespaces.API)
-            .register(DeviceId.class)
             .register(GatewayNode.class);
 
     @Activate
@@ -141,8 +139,6 @@
 
     @Deactivate
     protected void deactivate() {
-        gatewayNodeMap.clear();
-
         deviceService.removeListener(internalDeviceListener);
         configService.removeListener(configListener);
 
@@ -151,32 +147,44 @@
 
     @Override
     public GatewayNode getGatewayNode(DeviceId deviceId) {
-        return checkNotNull(gatewayNodeMap.get(deviceId).value(), GATEWAYNODE_CAN_NOT_BE_NULL);
+        GatewayNode gatewayNode = gatewayNodeMap.get(deviceId).value();
+        if (gatewayNode == null) {
+            log.warn("Gateway with device ID {} does not exist");
+            return null;
+        }
+        return gatewayNode;
     }
 
     @Override
-    public PortNumber getGatewayExternalPort(DeviceId deviceId) {
-        GatewayNode gatewayNode = checkNotNull(gatewayNodeMap.get(deviceId).value(), GATEWAYNODE_CAN_NOT_BE_NULL);
-        String externalInterfaceName = gatewayNode.getGatewayExternalInterfaceName();
-        Optional<Port> port = deviceService.getPorts(deviceId)
-                .stream()
-                .filter(p -> p.annotations().value(PORT_NAME).equals(externalInterfaceName))
-                .findFirst();
-
-        if (!port.isPresent()) {
-            log.error("Cannot find port {} in gateway device {}", externalInterfaceName, deviceId);
+    public PortNumber getUplinkPort(DeviceId deviceId) {
+        GatewayNode gatewayNode = gatewayNodeMap.get(deviceId).value();
+        if (gatewayNode == null) {
+            log.warn("Gateway with device ID {} does not exist");
             return null;
         }
 
+        Optional<Port> port = deviceService.getPorts(deviceId).stream()
+                .filter(p -> Objects.equals(
+                        p.annotations().value(PORT_NAME),
+                        gatewayNode.getUplinkIntf()))
+                .findFirst();
+        if (!port.isPresent()) {
+            log.warn("Cannot find uplink interface from gateway {}", deviceId);
+            return null;
+        }
         return port.get().number();
     }
 
     @Override
-    public GroupId getGroupIdForGatewayLoadBalance(DeviceId srcDeviceId) {
-        GroupDescription description = selectGroupHandler.createSelectGroupInVxlan(srcDeviceId, getGatewayNodes());
-        groupService.addGroup(description);
-        Group group = groupService.getGroup(description.deviceId(), description.appCookie());
-        return group != null ? group.id() : null;
+    public synchronized GroupId getGatewayGroupId(DeviceId srcDeviceId) {
+        GroupKey groupKey = selectGroupHandler.getGroupKey(srcDeviceId);
+        Group group = groupService.getGroup(srcDeviceId, groupKey);
+        if (group == null) {
+            log.info("Created gateway group for {}", srcDeviceId);
+            return selectGroupHandler.createGatewayGroup(srcDeviceId, getGatewayNodes());
+        } else {
+            return group.id();
+        }
     }
 
     @Override
@@ -185,9 +193,8 @@
         gatewayNodeMap.values()
                 .stream()
                 .map(Versioned::value)
-                .forEach(gatewayNode -> gatewayNodeList.add(gatewayNode));
+                .forEach(gatewayNodeList::add);
         return gatewayNodeList;
-
     }
 
     @Override
@@ -198,7 +205,6 @@
                 .map(Versioned::value)
                 .forEach(gatewayNode -> deviceIdList.add(gatewayNode.getGatewayDeviceId()));
         return deviceIdList;
-
     }
 
     @Override
@@ -206,7 +212,7 @@
         Versioned<GatewayNode> existingNode = gatewayNodeMap.putIfAbsent(
                 gatewayNode.getGatewayDeviceId(), gatewayNode);
         if (existingNode == null) {
-            updateGatewayLoadBalance(gatewayNode, true);
+            updateGatewayGroup(gatewayNode, true);
             log.info("Added {} to gateway pool", gatewayNode);
             return true;
         } else {
@@ -218,16 +224,22 @@
     public boolean deleteGatewayNode(GatewayNode gatewayNode) {
         boolean result = gatewayNodeMap.remove(gatewayNode.getGatewayDeviceId(), gatewayNode);
         if (result) {
-            updateGatewayLoadBalance(gatewayNode, false);
+            updateGatewayGroup(gatewayNode, false);
+            log.info("Deleted gateway with device ID {}", gatewayNode.getGatewayDeviceId());
         }
         return result;
     }
 
-    private void updateGatewayLoadBalance(GatewayNode gatewayNode, boolean nodeInsertion) {
-        deviceService.getAvailableDevices().forEach(device ->
-                groupService.getGroups(device.id(), appId).forEach(group ->
-                        selectGroupHandler.updateBucketToSelectGroupInVxlan(device.id(), group.appCookie(),
-                                Lists.newArrayList(gatewayNode), nodeInsertion)));
+    private void updateGatewayGroup(GatewayNode gatewayNode, boolean isInsert) {
+        Tools.stream(deviceService.getAvailableDevices()).forEach(device -> {
+            Tools.stream(groupService.getGroups(device.id(), appId)).forEach(group -> {
+                selectGroupHandler.updateGatewayGroupBuckets(
+                        device.id(),
+                        ImmutableList.of(gatewayNode),
+                        isInsert);
+                log.trace("Updated gateway group on {}", device.id());
+            });
+        });
     }
 
     private class InternalConfigListener implements NetworkConfigListener {
@@ -257,7 +269,9 @@
         public void event(DeviceEvent deviceEvent) {
             if (deviceEvent.type() == DeviceEvent.Type.DEVICE_SUSPENDED ||
                     deviceEvent.type() == DeviceEvent.Type.DEVICE_REMOVED) {
-                deleteGatewayNode(getGatewayNode(deviceEvent.subject().id()));
+                DeviceId deviceId = deviceEvent.subject().id();
+                deleteGatewayNode(getGatewayNode(deviceId));
+                log.warn("Gateway with device ID {} is disconnected", deviceId);
             }
         }
     }
@@ -269,8 +283,7 @@
             return;
         }
 
-        config.gatewayNodes().forEach(gatewayNode -> addGatewayNode(gatewayNode));
-
+        config.gatewayNodes().forEach(this::addGatewayNode);
         log.info("ScalableGateway configured");
     }
 }
diff --git a/apps/scalablegateway/src/main/java/org/onosproject/scalablegateway/impl/SelectGroupHandler.java b/apps/scalablegateway/src/main/java/org/onosproject/scalablegateway/impl/SelectGroupHandler.java
index 717379b..ed0b9b6 100644
--- a/apps/scalablegateway/src/main/java/org/onosproject/scalablegateway/impl/SelectGroupHandler.java
+++ b/apps/scalablegateway/src/main/java/org/onosproject/scalablegateway/impl/SelectGroupHandler.java
@@ -19,6 +19,8 @@
 import com.google.common.collect.Lists;
 import org.onlab.packet.Ip4Address;
 import org.onosproject.core.ApplicationId;
+import org.onosproject.core.DefaultGroupId;
+import org.onosproject.core.GroupId;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
@@ -36,7 +38,6 @@
 import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
 import org.onosproject.net.group.DefaultGroupDescription;
 import org.onosproject.net.group.DefaultGroupKey;
-import org.onosproject.net.group.Group;
 import org.onosproject.net.group.GroupBucket;
 import org.onosproject.net.group.GroupBuckets;
 import org.onosproject.net.group.GroupDescription;
@@ -48,6 +49,7 @@
 
 import java.util.List;
 
+import static org.onosproject.net.AnnotationKeys.PORT_NAME;
 import static org.onosproject.net.group.DefaultGroupBucket.createSelectGroupBucket;
 
 /**
@@ -59,7 +61,6 @@
 
     private static final String TUNNEL_DESTINATION = "tunnelDst";
     private static final String PORTNAME_PREFIX_TUNNEL = "vxlan";
-    private static final String PORTNAME = "portName";
 
     private final GroupService groupService;
     private final DeviceService deviceService;
@@ -89,45 +90,61 @@
      * @param nodeList gateway node list for bucket action
      * @return created select type group description
      */
-    public GroupDescription createSelectGroupInVxlan(DeviceId srcDeviceId, List<GatewayNode> nodeList) {
+    public GroupId createGatewayGroup(DeviceId srcDeviceId, List<GatewayNode> nodeList) {
         List<GroupBucket> bucketList = generateBucketsForSelectGroup(srcDeviceId, nodeList);
-        GroupKey key = generateGroupKey(srcDeviceId, nodeList);
-        return new DefaultGroupDescription(srcDeviceId, GroupDescription.Type.SELECT,
-                new GroupBuckets(bucketList), key, null, appId);
+        GroupId groupId = getGroupId(srcDeviceId);
+        GroupDescription groupDescription = new DefaultGroupDescription(
+                srcDeviceId,
+                GroupDescription.Type.SELECT,
+                new GroupBuckets(bucketList),
+                getGroupKey(srcDeviceId),
+                groupId.id(),
+                appId);
+
+        groupService.addGroup(groupDescription);
+        return groupId;
     }
 
-    private GroupKey generateGroupKey(DeviceId srcDeviceId, List<GatewayNode> nodeList) {
-        String cookie = srcDeviceId.toString();
-        for (GatewayNode node : nodeList) {
-            cookie = cookie.concat(node.getGatewayDeviceId().toString());
-        }
-        return new DefaultGroupKey(cookie.getBytes());
+    /**
+     * Returns unique group key with supplied source device ID as a hash.
+     *
+     * @param srcDeviceId source device id
+     * @return group key
+     */
+    public GroupKey getGroupKey(DeviceId srcDeviceId) {
+        return new DefaultGroupKey(srcDeviceId.toString().getBytes());
+    }
 
+    private GroupId getGroupId(DeviceId srcDeviceId) {
+        return new DefaultGroupId(srcDeviceId.toString().hashCode());
     }
 
     /**
      * Updates groupBuckets in select type group.
      *
-     * @param deviceId target device id for group description
-     * @param oldAppCookie group key for target group
+     * @param deviceId target device id to update the group
      * @param nodeList updated gateway node list for bucket action
-     * @param nodeInsertion update type(add or remove)
+     * @param isInsert update type(add or remove)
      * @return result of process
      */
-    public boolean updateBucketToSelectGroupInVxlan(DeviceId deviceId, GroupKey oldAppCookie,
-                                                    List<GatewayNode> nodeList, boolean nodeInsertion) {
+    public void updateGatewayGroupBuckets(DeviceId deviceId,
+                                          List<GatewayNode> nodeList,
+                                          boolean isInsert) {
         List<GroupBucket> bucketList = generateBucketsForSelectGroup(deviceId, nodeList);
-
-        GroupKey newAppCookie = generateGroupKey(deviceId, nodeList);
-        if (nodeInsertion) {
-            groupService.addBucketsToGroup(deviceId, oldAppCookie,
-                    new GroupBuckets(bucketList), newAppCookie, appId);
+        GroupKey groupKey = getGroupKey(deviceId);
+        if (isInsert) {
+            groupService.addBucketsToGroup(
+                    deviceId,
+                    groupKey,
+                    new GroupBuckets(bucketList),
+                    groupKey, appId);
         } else {
-            groupService.removeBucketsFromGroup(deviceId, oldAppCookie,
-                    new GroupBuckets(bucketList), newAppCookie, appId);
+            groupService.removeBucketsFromGroup(
+                    deviceId,
+                    groupKey,
+                    new GroupBuckets(bucketList),
+                    groupKey, appId);
         }
-        Group group = groupService.getGroup(deviceId, newAppCookie);
-        return group != null ? true : false;
     }
 
     private List<GroupBucket> generateBucketsForSelectGroup(DeviceId deviceId, List<GatewayNode> nodeList) {
@@ -175,7 +192,7 @@
      */
     private PortNumber getTunnelPort(DeviceId deviceId) {
         Port port = deviceService.getPorts(deviceId).stream()
-                .filter(p -> p.annotations().value(PORTNAME).equals(PORTNAME_PREFIX_TUNNEL))
+                .filter(p -> p.annotations().value(PORT_NAME).equals(PORTNAME_PREFIX_TUNNEL))
                 .findAny().orElse(null);
 
         if (port == null) {