[ONOS-5283] Arbitrary connect points, support multiple vlans

Change-Id: I9bd3536c08dfd8a637293460395de7e2a1dc1dc1
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/IntentInstaller.java b/apps/vpls/src/main/java/org/onosproject/vpls/IntentInstaller.java
index 79ed713..52a3924 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/IntentInstaller.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/IntentInstaller.java
@@ -15,18 +15,18 @@
  */
 package org.onosproject.vpls;
 
-import com.google.common.collect.SetMultimap;
-import org.apache.commons.lang3.tuple.Pair;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
 import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.Host;
 import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.IntentState;
 import org.onosproject.net.intent.Key;
 import org.onosproject.net.intent.MultiPointToSinglePointIntent;
 import org.onosproject.net.intent.SinglePointToMultiPointIntent;
@@ -34,24 +34,37 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
-import java.util.stream.Collectors;
 
 /**
  * Synchronizes intents between the in-memory intent store and the
  * IntentService.
  */
 public class IntentInstaller {
+    private static final String SUBMIT =
+            "Submitting intents to the Intent Synchronizer";
+    private static final String WITHDRAW =
+            "Withdrawing intents to the Intent Synchronizer";
+    private static final String SP2MP =
+            "Building sp2mp intent from {}";
+    private static final String MP2SP =
+            "Building mp2sp intent to {}";
+
     private static final Logger log = LoggerFactory.getLogger(
             IntentInstaller.class);
 
     private static final int PRIORITY_OFFSET = 1000;
 
-    private static final String PREFIX_BROADCAST = "brc";
-    private static final String PREFIX_UNICAST = "uni";
+    private static final Set<IntentState> WITHDRAWN_INTENT_STATES =
+            ImmutableSet.of(IntentState.WITHDRAWN,
+                            IntentState.WITHDRAW_REQ,
+                            IntentState.WITHDRAWING);
+
+    static final String PREFIX_BROADCAST = "brc";
+    static final String PREFIX_UNICAST = "uni";
+    static final String DASH = "-";
 
     private final ApplicationId appId;
     private final IntentSynchronizationService intentSynchronizer;
@@ -72,173 +85,150 @@
     }
 
     /**
-     * Formats the requests for creating and submit intents.
-     * Single Points to Multi Point intents are created for all the configured
-     * Connect Points. Multi Point to Single Point intents are created for
-     * Connect Points configured that have hosts attached.
-     *
-     * @param confHostPresentCPoint A map of Connect Points with the eventual
-     *                              MAC address of the host attached, by VLAN
-     */
-    protected void installIntents(SetMultimap<VlanId,
-            Pair<ConnectPoint,
-                    MacAddress>> confHostPresentCPoint) {
-        List<Intent> intents = new ArrayList<>();
-
-        confHostPresentCPoint.keySet()
-                .stream()
-                .filter(vlanId -> confHostPresentCPoint.get(vlanId) != null)
-                .forEach(vlanId -> {
-                    Set<Pair<ConnectPoint, MacAddress>> cPoints =
-                            confHostPresentCPoint.get(vlanId);
-                    cPoints.forEach(cPoint -> {
-                        MacAddress mac = cPoint.getValue();
-                        ConnectPoint src = cPoint.getKey();
-                        Set<ConnectPoint> dsts = cPoints.stream()
-                                .map(Pair::getKey)
-                                .filter(cp -> !cp.equals(src))
-                                .collect(Collectors.toSet());
-                        Key brcKey = buildKey(PREFIX_BROADCAST, src, vlanId);
-
-                        if (dsts.isEmpty()) {
-                            return;
-                        }
-
-                        intents.add(buildBrcIntent(brcKey, src, dsts, vlanId));
-
-                        if (mac != null && countMacInCPoints(cPoints) > 1) {
-                            Key uniKey = buildKey(PREFIX_UNICAST, src, vlanId);
-                            MultiPointToSinglePointIntent uniIntent =
-                                    buildUniIntent(uniKey,
-                                                   dsts,
-                                                   src,
-                                                   vlanId,
-                                                   mac);
-                            intents.add(uniIntent);
-                        }
-                    });
-                });
-        submitIntents(intents);
-    }
-
-    /**
      * Requests to install the intents passed as argument to the Intent Service.
      *
      * @param intents intents to be submitted
      */
-    private void submitIntents(Collection<Intent> intents) {
-        log.debug("Submitting intents to the Intent Synchronizer");
-        intents.forEach(intent -> {
-            intentSynchronizer.submit(intent);
-        });
+    protected void submitIntents(Collection<Intent> intents) {
+        log.debug(SUBMIT);
+        intents.forEach(intentSynchronizer::submit);
     }
 
     /**
-     * Builds a Single Point to Multi Point intent.
+     * Requests to withdraw the intents passed as argument to the Intent Service.
      *
-     * @param src  The source Connect Point
-     * @param dsts The destination Connect Points
-     * @return Single Point to Multi Point intent generated.
+     * @param intents intents to be withdraw
      */
-    private SinglePointToMultiPointIntent buildBrcIntent(Key key,
-                                                         ConnectPoint src,
-                                                         Set<ConnectPoint> dsts,
-                                                         VlanId vlanId) {
-        log.debug("Building p2mp intent from {}", src);
+    protected void withdrawIntents(Collection<Intent> intents) {
+        log.debug(WITHDRAW);
+        intents.forEach(intentSynchronizer::withdraw);
+    }
+
+    /**
+     * Returns list of intents belongs to a VPLS.
+     *
+     * @param name required VPLS network name
+     * @return list of intents belongs to a VPLS
+     */
+    protected List<Intent> getIntentsFromVpls(String name) {
+        List<Intent> intents = Lists.newArrayList();
+
+        intentService.getIntents().forEach(intent -> {
+            if (intent.key().toString().startsWith(name)) {
+                intents.add(intent);
+            }
+        });
+
+        return intents;
+    }
+
+    /**
+     * Builds a broadcast intent.
+     *
+     * @param key key to identify the intent
+     * @param src the source connect point
+     * @param dsts the destination connect points
+     * @return the generated single-point to multi-point intent
+     */
+    protected SinglePointToMultiPointIntent buildBrcIntent(Key key,
+                                                           FilteredConnectPoint src,
+                                                           Set<FilteredConnectPoint> dsts) {
+        log.debug(SP2MP, src);
 
         SinglePointToMultiPointIntent intent;
 
-        TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
-
-        TrafficSelector.Builder builder = DefaultTrafficSelector.builder()
+        TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthDst(MacAddress.BROADCAST)
-                .matchVlanId(vlanId);
-
-        TrafficSelector selector = builder.build();
+                .build();
 
         intent = SinglePointToMultiPointIntent.builder()
                 .appId(appId)
                 .key(key)
                 .selector(selector)
-                .treatment(treatment)
-                .ingressPoint(src)
-                .egressPoints(dsts)
+                .filteredIngressPoint(src)
+                .filteredEgressPoints(dsts)
                 .priority(PRIORITY_OFFSET)
                 .build();
         return intent;
     }
 
     /**
-     * Builds a Multi Point to Single Point intent.
+     * Builds a unicast intent.
      *
-     * @param srcs The source Connect Points
-     * @param dst  The destination Connect Point
-     * @return Multi Point to Single Point intent generated.
+     * @param key key to identify the intent
+     * @param srcs the source Connect Points
+     * @param dst the destination Connect Point
+     * @param host destination Host
+     * @return the generated multi-point to single-point intent
      */
-    private MultiPointToSinglePointIntent buildUniIntent(Key key,
-                                                         Set<ConnectPoint> srcs,
-                                                         ConnectPoint dst,
-                                                         VlanId vlanId,
-                                                         MacAddress mac) {
-        log.debug("Building mp2p intent to {}", dst);
+    protected MultiPointToSinglePointIntent buildUniIntent(Key key,
+                                                           Set<FilteredConnectPoint> srcs,
+                                                           FilteredConnectPoint dst,
+                                                           Host host) {
+        log.debug(MP2SP, dst);
 
-        MultiPointToSinglePointIntent intent;
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthDst(host.mac())
+                .build();
 
-        TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
 
-        TrafficSelector.Builder builder = DefaultTrafficSelector.builder()
-                .matchEthDst(mac)
-                .matchVlanId(vlanId);
-
-        TrafficSelector selector = builder.build();
-
-        intent = MultiPointToSinglePointIntent.builder()
+        return MultiPointToSinglePointIntent.builder()
                 .appId(appId)
                 .key(key)
                 .selector(selector)
-                .treatment(treatment)
-                .ingressPoints(srcs)
-                .egressPoint(dst)
+                .filteredIngressPoints(srcs)
+                .filteredEgressPoint(dst)
                 .priority(PRIORITY_OFFSET)
                 .build();
-        return intent;
+
     }
 
     /**
-     * Builds an intent Key for either for a Single Point to Multi Point or
-     * Multi Point to Single Point intent, based on a prefix that defines
+     * Builds an intent Key for either for a single-point to multi-point or
+     * multi-point to single-point intent, based on a prefix that defines
      * the type of intent, the single connection point representing the source
-     * or the destination and the vlan id representing the network.
+     * or the destination and the VLAN identifier representing the network.
      *
-     * @param cPoint the source or destination connect point
-     * @param vlanId the network vlan id
-     * @param prefix prefix string
-     * @return
+     * @param prefix key prefix
+     * @param cPoint connect point for single source/destination
+     * @param networkName VPLS network name
+     * @param hostMac source/destination mac address
+     * @return key to identify the intent
      */
-    private Key buildKey(String prefix, ConnectPoint cPoint, VlanId vlanId) {
-        String keyString = new StringBuilder()
-                .append(prefix)
-                .append("-")
-                .append(cPoint.deviceId())
-                .append("-")
-                .append(cPoint.port())
-                .append("-")
-                .append(vlanId)
-                .toString();
+    protected Key buildKey(String prefix,
+                           ConnectPoint cPoint,
+                           String networkName,
+                           MacAddress hostMac) {
+        String keyString = networkName +
+                DASH +
+                prefix +
+                DASH +
+                cPoint.deviceId() +
+                DASH +
+                cPoint.port() +
+                DASH +
+                hostMac;
 
         return Key.of(keyString, appId);
     }
 
     /**
-     * Counts the number of mac addresses associated to a specific list of
-     * ConnectPoint.
+     * Returns true if the specified intent exists; false otherwise.
      *
-     * @param cPoints Set of ConnectPoints, eventually bound to the MAC of the
-     *                host attached
-     * @return number of mac addresses found.
+     * @param intentKey intent key
+     * @return true if the intent exists, false otherwise
      */
-    private int countMacInCPoints(Set<Pair<ConnectPoint, MacAddress>> cPoints) {
-        return (int) cPoints.stream().filter(p -> p.getValue() != null).count();
-    }
+    protected boolean intentExists(Key intentKey) {
+        if (intentService.getIntent(intentKey) == null) {
+            return false;
+        }
 
+        // Intent does not exist if intent withdrawn
+        IntentState currentIntentState = intentService.getIntentState(intentKey);
+        if (WITHDRAWN_INTENT_STATES.contains(currentIntentState)) {
+            return false;
+        }
+
+        return true;
+    }
 }