SinglePoint to MultiPoint Intent initial implementation

Change-Id: I1010997ce4ea993ae34afb8dab4b6c0ae112448d
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/LinkCollectionIntentInstaller.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/LinkCollectionIntentInstaller.java
index fb8e2d4..66e5fb1 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/LinkCollectionIntentInstaller.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/LinkCollectionIntentInstaller.java
@@ -15,9 +15,12 @@
  */
 package org.onlab.onos.net.intent.impl;
 
-import static org.slf4j.LoggerFactory.getLogger;
-
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
 
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -26,6 +29,7 @@
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onlab.onos.core.ApplicationId;
 import org.onlab.onos.core.CoreService;
+import org.onlab.onos.net.ConnectPoint;
 import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.Link;
 import org.onlab.onos.net.PortNumber;
@@ -42,18 +46,16 @@
 import org.onlab.onos.net.intent.IntentInstaller;
 import org.onlab.onos.net.intent.LinkCollectionIntent;
 import org.onlab.onos.net.intent.PathIntent;
-import org.slf4j.Logger;
 
 import com.google.common.collect.Lists;
 
 /**
- * Installer for {@link org.onlab.onos.net.intent.LinkCollectionIntent}
- * path segment intents.
+ * Installer for {@link org.onlab.onos.net.intent.LinkCollectionIntent} path
+ * segment intents.
  */
 @Component(immediate = true)
-public class LinkCollectionIntentInstaller implements IntentInstaller<LinkCollectionIntent> {
-
-    private final Logger log = getLogger(getClass());
+public class LinkCollectionIntentInstaller
+        implements IntentInstaller<LinkCollectionIntent> {
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected IntentExtensionService intentManager;
@@ -76,37 +78,58 @@
 
     @Override
     public List<FlowRuleBatchOperation> install(LinkCollectionIntent intent) {
+        Map<DeviceId, Set<PortNumber>> outputMap = new HashMap<DeviceId, Set<PortNumber>>();
         List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
+
         for (Link link : intent.links()) {
-            rules.add(createBatchEntry(FlowRuleOperation.ADD,
-                   intent,
-                   link.src().deviceId(),
-                   link.src().port()));
+            if (outputMap.get(link.src().deviceId()) == null) {
+                outputMap.put(link.src().deviceId(), new HashSet<PortNumber>());
+            }
+            outputMap.get(link.src().deviceId()).add(link.src().port());
+
         }
 
-        rules.add(createBatchEntry(FlowRuleOperation.ADD,
-                intent,
-                intent.egressPoint().deviceId(),
-                intent.egressPoint().port()));
+        for (ConnectPoint egressPoint : intent.egressPoints()) {
+            if (outputMap.get(egressPoint.deviceId()) == null) {
+                outputMap
+                        .put(egressPoint.deviceId(), new HashSet<PortNumber>());
+            }
+            outputMap.get(egressPoint.deviceId()).add(egressPoint.port());
+
+        }
+
+        for (Entry<DeviceId, Set<PortNumber>> entry : outputMap.entrySet()) {
+            rules.add(createBatchEntry(FlowRuleOperation.ADD, intent,
+                                       entry.getKey(), entry.getValue()));
+        }
 
         return Lists.newArrayList(new FlowRuleBatchOperation(rules));
     }
 
     @Override
     public List<FlowRuleBatchOperation> uninstall(LinkCollectionIntent intent) {
+        Map<DeviceId, Set<PortNumber>> outputMap = new HashMap<DeviceId, Set<PortNumber>>();
         List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
 
         for (Link link : intent.links()) {
-            rules.add(createBatchEntry(FlowRuleOperation.REMOVE,
-                    intent,
-                    link.src().deviceId(),
-                    link.src().port()));
+            if (outputMap.get(link.src().deviceId()) == null) {
+                outputMap.put(link.src().deviceId(), new HashSet<PortNumber>());
+            }
+            outputMap.get(link.src().deviceId()).add(link.src().port());
         }
 
-        rules.add(createBatchEntry(FlowRuleOperation.REMOVE,
-               intent,
-               intent.egressPoint().deviceId(),
-               intent.egressPoint().port()));
+        for (ConnectPoint egressPoint : intent.egressPoints()) {
+            if (outputMap.get(egressPoint.deviceId()) == null) {
+                outputMap
+                        .put(egressPoint.deviceId(), new HashSet<PortNumber>());
+            }
+            outputMap.get(egressPoint.deviceId()).add(egressPoint.port());
+        }
+
+        for (Entry<DeviceId, Set<PortNumber>> entry : outputMap.entrySet()) {
+            rules.add(createBatchEntry(FlowRuleOperation.REMOVE, intent,
+                                       entry.getKey(), entry.getValue()));
+        }
 
         return Lists.newArrayList(new FlowRuleBatchOperation(rules));
     }
@@ -128,17 +151,20 @@
      * @return the new flow rule batch entry
      */
     private FlowRuleBatchEntry createBatchEntry(FlowRuleOperation operation,
-                                    LinkCollectionIntent intent,
-                                    DeviceId deviceId,
-                                    PortNumber outPort) {
+                                                LinkCollectionIntent intent,
+                                                DeviceId deviceId,
+                                                Set<PortNumber> outPorts) {
 
-        TrafficTreatment.Builder treatmentBuilder =
-                DefaultTrafficTreatment.builder(intent.treatment());
+        TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
+                .builder(intent.treatment());
 
-        TrafficTreatment treatment = treatmentBuilder.setOutput(outPort).build();
+        for (PortNumber outPort : outPorts) {
+            treatmentBuilder.setOutput(outPort);
+        }
+        TrafficTreatment treatment = treatmentBuilder.build();
 
-        TrafficSelector selector = DefaultTrafficSelector.builder(intent.selector())
-                                   .build();
+        TrafficSelector selector = DefaultTrafficSelector
+                .builder(intent.selector()).build();
 
         FlowRule rule = new DefaultFlowRule(deviceId,
                 selector, treatment, 123,
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/SinglePointToMultiPointIntentCompiler.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/SinglePointToMultiPointIntentCompiler.java
new file mode 100644
index 0000000..46689ec
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/SinglePointToMultiPointIntentCompiler.java
@@ -0,0 +1,58 @@
+package org.onlab.onos.net.intent.impl;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.Path;
+import org.onlab.onos.net.intent.Intent;
+import org.onlab.onos.net.intent.LinkCollectionIntent;
+import org.onlab.onos.net.intent.SinglePointToMultiPointIntent;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.net.resource.LinkResourceAllocations;
+
+@Component(immediate = true)
+public class SinglePointToMultiPointIntentCompiler
+        extends ConnectivityIntentCompiler<SinglePointToMultiPointIntent> {
+
+    // TODO: use off-the-shell core provider ID
+    private static final ProviderId PID =
+            new ProviderId("core", "org.onlab.onos.core", true);
+
+    @Activate
+    public void activate() {
+        intentManager.registerCompiler(SinglePointToMultiPointIntent.class,
+                                       this);
+    }
+
+    @Deactivate
+    public void deactivate() {
+        intentManager.unregisterCompiler(SinglePointToMultiPointIntent.class);
+    }
+
+
+    @Override
+    public List<Intent> compile(SinglePointToMultiPointIntent intent,
+                                List<Intent> installable,
+                                Set<LinkResourceAllocations> resources) {
+        Set<Link> links = new HashSet<>();
+        //FIXME: need to handle the case where ingress/egress points are on same switch
+        for (ConnectPoint egressPoint : intent.egressPoints()) {
+            Path path = getPath(intent, intent.ingressPoint().deviceId(), egressPoint.deviceId());
+            links.addAll(path.links());
+        }
+
+        Intent result = new LinkCollectionIntent(intent.appId(),
+                                                 intent.selector(),
+                                                 intent.treatment(), links,
+                                                 intent.egressPoints(), null);
+
+        return Arrays.asList(result);
+    }
+}