[CORD-46] Create a broadcast group for each subnet

DONE
- Expose subnet-to-ports information from DeviceProperties and DeviceConfiguration
- Create subnetNextObjectiveStore to store <DeviceId, IpPrefix> to nextId mapping
- Implement broadcast NextObjective in SpringOpenTTP
      Use ALL group type to achieve broadcast

TODO (not in this submission)
- Push ARP table for a host when its location is learned
- Push default ARP table miss rule. Action = to the broadcast group

Change-Id: I2de28095e85289e75af3fc7a02c811b270b342ad
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 9305541..a5ac678 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -22,13 +22,17 @@
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.packet.Ethernet;
-import org.onlab.packet.IPv4;
-import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.VlanId;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
 import org.onlab.util.KryoNamespace;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.event.Event;
+import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.config.ConfigFactory;
 import org.onosproject.net.config.NetworkConfigEvent;
 import org.onosproject.net.config.NetworkConfigRegistry;
@@ -57,6 +61,7 @@
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
 import org.onosproject.net.topology.TopologyService;
+import org.onosproject.segmentrouting.grouphandler.SubnetNextObjectiveStoreKey;
 import org.onosproject.store.service.EventuallyConsistentMap;
 import org.onosproject.store.service.EventuallyConsistentMapBuilder;
 import org.onosproject.store.service.StorageService;
@@ -136,6 +141,7 @@
     // Per device next objective ID store with (device id + neighbor set) as key
     private EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey,
         Integer> nsNextObjStore = null;
+    private EventuallyConsistentMap<SubnetNextObjectiveStoreKey, Integer> subnetNextObjStore = null;
     private EventuallyConsistentMap<String, Tunnel> tunnelStore = null;
     private EventuallyConsistentMap<String, Policy> policyStore = null;
     // Per device, per-subnet assigned-vlans store, with (device id + subnet
@@ -180,6 +186,8 @@
 
         kryoBuilder = new KryoNamespace.Builder()
             .register(NeighborSetNextObjectiveStoreKey.class,
+                    SubnetNextObjectiveStoreKey.class,
+                    SubnetAssignedVidStoreKey.class,
                     NeighborSet.class,
                     DeviceId.class,
                     URI.class,
@@ -191,8 +199,11 @@
                     Policy.class,
                     TunnelPolicy.class,
                     Policy.Type.class,
-                    SubnetAssignedVidStoreKey.class,
-                    VlanId.class
+                    VlanId.class,
+                    Ip4Address.class,
+                    Ip4Prefix.class,
+                    IpAddress.Version.class,
+                    ConnectPoint.class
             );
 
         log.debug("Creating EC map nsnextobjectivestore");
@@ -206,6 +217,16 @@
                 .build();
         log.trace("Current size {}", nsNextObjStore.size());
 
+        log.debug("Creating EC map subnetnextobjectivestore");
+        EventuallyConsistentMapBuilder<SubnetNextObjectiveStoreKey, Integer>
+                subnetNextObjMapBuilder = storageService.eventuallyConsistentMapBuilder();
+
+        subnetNextObjStore = subnetNextObjMapBuilder
+                .withName("subnetnextobjectivestore")
+                .withSerializer(kryoBuilder)
+                .withTimestampProvider((k, v) -> new WallClockTimestamp())
+                .build();
+
         EventuallyConsistentMapBuilder<String, Tunnel> tunnelMapBuilder =
                 storageService.eventuallyConsistentMapBuilder();
 
@@ -399,6 +420,25 @@
         }
     }
 
+    /**
+     * Returns the next objective ID for the Subnet given. If the nextObjectiveID does not exist,
+     * a new one is created and returned.
+     *
+     * @param deviceId Device ID
+     * @param prefix Subnet
+     * @return next objective ID
+     */
+    public int getSubnetNextObjectiveId(DeviceId deviceId, IpPrefix prefix) {
+        if (groupHandlerMap.get(deviceId) != null) {
+            log.trace("getSubnetNextObjectiveId query in device {}", deviceId);
+            return groupHandlerMap
+                    .get(deviceId).getSubnetNextObjectiveId(prefix);
+        } else {
+            log.warn("getSubnetNextObjectiveId query in device {} not found", deviceId);
+            return -1;
+        }
+    }
+
     private class InternalPacketProcessor implements PacketProcessor {
         @Override
         public void process(PacketContext context) {
@@ -559,15 +599,20 @@
         //Because in a multi-instance setup, instances can initiate
         //groups for any devices. Also the default TTP rules are needed
         //to be pushed before inserting any IP table entries for any device
-        DefaultGroupHandler dgh = DefaultGroupHandler.
+        DefaultGroupHandler groupHandler = DefaultGroupHandler.
                 createGroupHandler(device.id(),
                                    appId,
                                    deviceConfiguration,
                                    linkService,
                                    flowObjectiveService,
-                                   nsNextObjStore);
-        groupHandlerMap.put(device.id(), dgh);
+                                   nsNextObjStore,
+                                   subnetNextObjStore);
+        groupHandlerMap.put(device.id(), groupHandler);
         defaultRoutingHandler.populatePortAddressingRules(device.id());
+
+        if (mastershipService.isLocalMaster(device.id())) {
+            groupHandler.createGroupsFromSubnetConfig();
+        }
     }
 
     private void processPortRemoved(Device device, Port port) {
@@ -610,9 +655,14 @@
                         .createGroupHandler(device.id(), appId,
                                             deviceConfiguration, linkService,
                                             flowObjectiveService,
-                                            nsNextObjStore);
+                                            nsNextObjStore,
+                                            subnetNextObjStore);
                 groupHandlerMap.put(device.id(), groupHandler);
                 defaultRoutingHandler.populatePortAddressingRules(device.id());
+
+                if (mastershipService.isLocalMaster(device.id())) {
+                    groupHandler.createGroupsFromSubnetConfig();
+                }
             }
 
             defaultRoutingHandler.startPopulationProcess();