[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/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java b/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java
index 7ef72fa..31297ff 100644
--- a/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java
+++ b/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java
@@ -247,6 +247,7 @@
     private void addGroup(NextObjective nextObjective) {
         log.debug("addGroup with type{} for nextObjective id {}",
                   nextObjective.type(), nextObjective.id());
+        List<GroupBucket> buckets;
         switch (nextObjective.type()) {
             case SIMPLE:
                 log.debug("processing SIMPLE next objective");
@@ -274,7 +275,7 @@
                 break;
             case HASHED:
                 log.debug("processing HASHED next objective");
-                List<GroupBucket> buckets = nextObjective
+                buckets = nextObjective
                         .next()
                         .stream()
                         .map((treatment) -> DefaultGroupBucket
@@ -298,8 +299,32 @@
                 }
                 break;
             case BROADCAST:
+                log.debug("processing BROADCAST next objective");
+                buckets = nextObjective
+                        .next()
+                        .stream()
+                        .map((treatment) -> DefaultGroupBucket
+                                .createAllGroupBucket(treatment))
+                        .collect(Collectors.toList());
+                if (!buckets.isEmpty()) {
+                    final GroupKey key = new DefaultGroupKey(
+                            appKryo.serialize(nextObjective
+                                                      .id()));
+                    GroupDescription groupDescription = new DefaultGroupDescription(
+                            deviceId,
+                            GroupDescription.Type.ALL,
+                            new GroupBuckets(buckets),
+                            key,
+                            null,
+                            nextObjective.appId());
+                    log.debug("Creating BROADCAST group for next objective id {}",
+                              nextObjective.id());
+                    groupService.addGroup(groupDescription);
+                    pendingGroups.put(key, nextObjective);
+                }
+                break;
             case FAILOVER:
-                log.debug("BROADCAST and FAILOVER next objectives not supported");
+                log.debug("FAILOVER next objectives not supported");
                 fail(nextObjective, ObjectiveError.UNSUPPORTED);
                 log.warn("Unsupported next objective type {}", nextObjective.type());
                 break;
@@ -327,6 +352,8 @@
             bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
         } else if (group.type() == GroupDescription.Type.SELECT) {
             bucket = DefaultGroupBucket.createSelectGroupBucket(treatment);
+        } else if (group.type() == GroupDescription.Type.ALL) {
+            bucket = DefaultGroupBucket.createAllGroupBucket(treatment);
         } else {
             log.warn("Unsupported Group type {}", group.type());
             return;
@@ -357,6 +384,8 @@
                 bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
             } else if (group.type() == GroupDescription.Type.SELECT) {
                 bucket = DefaultGroupBucket.createSelectGroupBucket(treatment);
+            } else if (group.type() == GroupDescription.Type.ALL) {
+                bucket = DefaultGroupBucket.createAllGroupBucket(treatment);
             } else {
                 log.warn("Unsupported Group type {}", group.type());
                 return;
@@ -539,7 +568,7 @@
     protected List<FlowRule> processEthDstFilter(Criterion c,
                                        FilteringObjective filt,
                                        ApplicationId applicationId) {
-        List<FlowRule> rules = new ArrayList<FlowRule>();
+        List<FlowRule> rules = new ArrayList<>();
         EthCriterion e = (EthCriterion) c;
         TrafficSelector.Builder selectorIp = DefaultTrafficSelector
                 .builder();
@@ -577,7 +606,7 @@
     protected List<FlowRule> processVlanIdFilter(Criterion c,
                                                  FilteringObjective filt,
                                                  ApplicationId applicationId) {
-        List<FlowRule> rules = new ArrayList<FlowRule>();
+        List<FlowRule> rules = new ArrayList<>();
         VlanIdCriterion v = (VlanIdCriterion) c;
         log.debug("adding rule for VLAN: {}", v.vlanId());
         TrafficSelector.Builder selector = DefaultTrafficSelector