[ONOS-6248] VPLS refactoring

Change-Id: I8ffb2199ca108ad8dfe271681068636fc4af2a40
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/intent/VplsIntentUtility.java b/apps/vpls/src/main/java/org/onosproject/vpls/intent/VplsIntentUtility.java
new file mode 100644
index 0000000..a81a72f
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/intent/VplsIntentUtility.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vpls.intent;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.EncapsulationType;
+import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.Host;
+import org.onosproject.net.ResourceGroup;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.intent.ConnectivityIntent;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.net.intent.SinglePointToMultiPointIntent;
+import org.onosproject.net.intent.constraint.EncapsulationConstraint;
+import org.onosproject.net.intent.constraint.PartialFailureConstraint;
+import org.onosproject.vpls.api.VplsData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.*;
+import static org.onosproject.net.EncapsulationType.*;
+
+/**
+ * Intent utilities for VPLS.
+ */
+public final class VplsIntentUtility {
+    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(
+            VplsIntentUtility.class);
+
+    private static final int PRIORITY_OFFSET = 1000;
+    private static final int PRIORITY_UNI = 200;
+    private static final int PRIORITY_BRC = 100;
+
+    public static final String PREFIX_BROADCAST = "brc";
+    public static final String PREFIX_UNICAST = "uni";
+    private static final String SEPARATOR = "-";
+
+    public static final ImmutableList<Constraint> PARTIAL_FAILURE_CONSTRAINT =
+            ImmutableList.of(new PartialFailureConstraint());
+
+    private VplsIntentUtility() {
+        // Utility classes should not have a public or default constructor.
+    }
+
+    /**
+     * Builds broadcast Intents for a VPLS.
+     *
+     * @param vplsData the VPLS
+     * @param appId the application id for Intents
+     * @return broadcast Intents for the VPLS
+     */
+    public static Set<Intent> buildBrcIntents(VplsData vplsData, ApplicationId appId) {
+        Set<Interface> interfaces = vplsData.interfaces();
+
+        // At least two or more network interfaces to build broadcast Intents
+        if (interfaces.size() < 2) {
+            return ImmutableSet.of();
+        }
+        Set<Intent> brcIntents = Sets.newHashSet();
+        ResourceGroup resourceGroup = ResourceGroup.of(vplsData.name());
+
+        // Generates broadcast Intents from any network interface to other
+        // network interface from the VPLS.
+        interfaces.forEach(src -> {
+            FilteredConnectPoint srcFcp = VplsIntentUtility.buildFilteredConnectedPoint(src);
+            Set<FilteredConnectPoint> dstFcps =
+                    interfaces.stream()
+                            .filter(iface -> !iface.equals(src))
+                            .map(VplsIntentUtility::buildFilteredConnectedPoint)
+                            .collect(Collectors.toSet());
+            Key key = VplsIntentUtility.buildKey(PREFIX_BROADCAST,
+                                                 srcFcp.connectPoint(),
+                                                 vplsData.name(),
+                                                 MacAddress.BROADCAST,
+                                                 appId);
+            Intent brcIntent = buildBrcIntent(key,
+                                              appId,
+                                              srcFcp,
+                                              dstFcps,
+                                              vplsData.encapsulationType(),
+                                              resourceGroup);
+
+            brcIntents.add(brcIntent);
+        });
+        return brcIntents;
+    }
+
+    /**
+     * Builds a broadcast intent.
+     *
+     * @param key key to identify the intent
+     * @param appId application ID for this Intent
+     * @param src the source connect point
+     * @param dsts the destination connect points
+     * @param encap the encapsulation type
+     * @param resourceGroup resource group for this Intent
+     * @return the generated single-point to multi-point intent
+     */
+    protected static SinglePointToMultiPointIntent buildBrcIntent(Key key,
+                                                                  ApplicationId appId,
+                                                                  FilteredConnectPoint src,
+                                                                  Set<FilteredConnectPoint> dsts,
+                                                                  EncapsulationType encap,
+                                                                  ResourceGroup resourceGroup) {
+        log.debug("Building broadcast intent {} for source {}", SP2MP, src);
+
+        SinglePointToMultiPointIntent.Builder intentBuilder;
+
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthDst(MacAddress.BROADCAST)
+                .build();
+
+        intentBuilder = SinglePointToMultiPointIntent.builder()
+                .appId(appId)
+                .key(key)
+                .selector(selector)
+                .filteredIngressPoint(src)
+                .filteredEgressPoints(dsts)
+                .constraints(PARTIAL_FAILURE_CONSTRAINT)
+                .priority(PRIORITY_OFFSET + PRIORITY_BRC)
+                .resourceGroup(resourceGroup);
+
+        setEncap(intentBuilder, PARTIAL_FAILURE_CONSTRAINT, encap);
+
+        return intentBuilder.build();
+    }
+
+    /**
+     * Builds unicast Intents for a VPLS.
+     *
+     * @param vplsData the VPLS
+     * @param hosts the hosts of the VPLS
+     * @param appId application ID for Intents
+     * @return unicast Intents for the VPLS
+     */
+    public static Set<Intent> buildUniIntents(VplsData vplsData, Set<Host> hosts, ApplicationId appId) {
+        Set<Interface> interfaces = vplsData.interfaces();
+        if (interfaces.size() < 2) {
+            return ImmutableSet.of();
+        }
+        Set<Intent> uniIntents = Sets.newHashSet();
+        ResourceGroup resourceGroup = ResourceGroup.of(vplsData.name());
+        hosts.forEach(host -> {
+            FilteredConnectPoint hostFcp = buildFilteredConnectedPoint(host);
+            Set<FilteredConnectPoint> srcFcps =
+                    interfaces.stream()
+                            .map(VplsIntentUtility::buildFilteredConnectedPoint)
+                            .filter(fcp -> !fcp.equals(hostFcp))
+                            .collect(Collectors.toSet());
+            Key key = buildKey(PREFIX_UNICAST,
+                               hostFcp.connectPoint(),
+                               vplsData.name(),
+                               host.mac(),
+                               appId);
+            Intent uniIntent = buildUniIntent(key,
+                                              appId,
+                                              srcFcps,
+                                              hostFcp,
+                                              host,
+                                              vplsData.encapsulationType(),
+                                              resourceGroup);
+            uniIntents.add(uniIntent);
+        });
+
+        return uniIntents;
+    }
+
+    /**
+     * Builds a unicast intent.
+     *
+     * @param key key to identify the intent
+     * @param appId application ID for this Intent
+     * @param srcs the source Connect Points
+     * @param dst the destination Connect Point
+     * @param host destination Host
+     * @param encap the encapsulation type
+     * @param resourceGroup resource group for this Intent
+     * @return the generated multi-point to single-point intent
+     */
+    protected static MultiPointToSinglePointIntent buildUniIntent(Key key,
+                                                                  ApplicationId appId,
+                                                                  Set<FilteredConnectPoint> srcs,
+                                                                  FilteredConnectPoint dst,
+                                                                  Host host,
+                                                                  EncapsulationType encap,
+                                                                  ResourceGroup resourceGroup) {
+        log.debug("Building unicast intent {} for destination {}", MP2SP, dst);
+
+        MultiPointToSinglePointIntent.Builder intentBuilder;
+
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthDst(host.mac())
+                .build();
+
+        intentBuilder = MultiPointToSinglePointIntent.builder()
+                .appId(appId)
+                .key(key)
+                .selector(selector)
+                .filteredIngressPoints(srcs)
+                .filteredEgressPoint(dst)
+                .constraints(PARTIAL_FAILURE_CONSTRAINT)
+                .priority(PRIORITY_OFFSET + PRIORITY_UNI)
+                .resourceGroup(resourceGroup);
+
+        setEncap(intentBuilder, PARTIAL_FAILURE_CONSTRAINT, encap);
+
+        return intentBuilder.build();
+    }
+
+    /**
+     * Builds an intent key either for single-point to multi-point or
+     * multi-point to single-point intents, based on a prefix that defines
+     * the type of intent, the single connect point representing the single
+     * source or destination for that intent, the name of the VPLS the intent
+     * belongs to, and the destination host MAC address the intent reaches.
+     *
+     * @param prefix the key prefix
+     * @param cPoint the connect point identifying the source/destination
+     * @param vplsName the name of the VPLS
+     * @param hostMac the source/destination MAC address
+     * @param appId application ID for the key
+     * @return the key to identify the intent
+     */
+    protected static Key buildKey(String prefix,
+                                  ConnectPoint cPoint,
+                                  String vplsName,
+                                  MacAddress hostMac,
+                                  ApplicationId appId) {
+        String keyString = vplsName +
+                SEPARATOR +
+                prefix +
+                SEPARATOR +
+                cPoint.deviceId() +
+                SEPARATOR +
+                cPoint.port() +
+                SEPARATOR +
+                hostMac;
+
+        return Key.of(keyString, appId);
+    }
+
+
+    /**
+     * Sets one or more encapsulation constraints on the intent builder given.
+     *
+     * @param builder the intent builder
+     * @param constraints the existing intent constraints
+     * @param encap the encapsulation type to be set
+     */
+    public static void setEncap(ConnectivityIntent.Builder builder,
+                                List<Constraint> constraints,
+                                EncapsulationType encap) {
+        // Constraints might be an immutable list, so a new modifiable list
+        // is created
+        List<Constraint> newConstraints = new ArrayList<>(constraints);
+
+        // Remove any encapsulation constraint if already in the list
+        constraints.stream()
+                .filter(c -> c instanceof EncapsulationConstraint)
+                .forEach(newConstraints::remove);
+
+        // if the new encapsulation is different from NONE, a new encapsulation
+        // constraint should be added to the list
+        if (!encap.equals(NONE)) {
+            newConstraints.add(new EncapsulationConstraint(encap));
+        }
+
+        // Submit new constraint list as immutable list
+        builder.constraints(ImmutableList.copyOf(newConstraints));
+    }
+
+    /**
+     * Builds filtered connected point by a given network interface.
+     *
+     * @param iface the network interface
+     * @return the filtered connected point of a given network interface
+     */
+    protected static FilteredConnectPoint buildFilteredConnectedPoint(Interface iface) {
+        Objects.requireNonNull(iface);
+        TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
+
+        if (iface.vlan() != null && !iface.vlan().equals(VlanId.NONE)) {
+            trafficSelector.matchVlanId(iface.vlan());
+        }
+
+        return new FilteredConnectPoint(iface.connectPoint(), trafficSelector.build());
+    }
+
+    /**
+     * Builds filtered connected point by a given host.
+     *
+     * @param host the host
+     * @return the filtered connected point of the given host
+     */
+    protected static FilteredConnectPoint buildFilteredConnectedPoint(Host host) {
+        requireNonNull(host);
+        TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
+
+        if (host.vlan() != null && !host.vlan().equals(VlanId.NONE)) {
+            trafficSelector.matchVlanId(host.vlan());
+        }
+        return new FilteredConnectPoint(host.location(), trafficSelector.build());
+    }
+}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/intent/package-info.java b/apps/vpls/src/main/java/org/onosproject/vpls/intent/package-info.java
new file mode 100644
index 0000000..197ea02
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/intent/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Utility for generate Intent for VPLS.
+ */
+package org.onosproject.vpls.intent;
\ No newline at end of file