[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;
+    }
 }
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/Vpls.java b/apps/vpls/src/main/java/org/onosproject/vpls/Vpls.java
index 2951d13..4e4bd4b 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/Vpls.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/Vpls.java
@@ -15,10 +15,10 @@
  */
 package org.onosproject.vpls;
 
-import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
 import com.google.common.collect.SetMultimap;
-
-import org.apache.commons.lang3.tuple.Pair;
+import com.google.common.collect.Sets;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -29,29 +29,56 @@
 import org.onosproject.app.ApplicationService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
+import org.onosproject.incubator.net.intf.Interface;
 import org.onosproject.incubator.net.intf.InterfaceEvent;
 import org.onosproject.incubator.net.intf.InterfaceListener;
 import org.onosproject.incubator.net.intf.InterfaceService;
-import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.FilteredConnectPoint;
 import org.onosproject.net.Host;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
 import org.onosproject.net.host.HostEvent;
 import org.onosproject.net.host.HostListener;
 import org.onosproject.net.host.HostService;
+import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.Key;
 import org.onosproject.routing.IntentSynchronizationService;
+import org.onosproject.vpls.config.VplsConfigurationService;
 import org.slf4j.Logger;
 
-import java.util.Map;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.vpls.IntentInstaller.PREFIX_BROADCAST;
+import static org.onosproject.vpls.IntentInstaller.PREFIX_UNICAST;
 
 /**
  * Application to create L2 broadcast overlay networks using VLAN.
  */
 @Component(immediate = true)
 public class Vpls {
-    protected static final String VPLS_APP = "org.onosproject.vpls";
+    /**
+     * Application name of VPLS.
+     */
+    static final String VPLS_APP = "org.onosproject.vpls";
+
+    private static final String HOST_FCP_NOT_FOUND =
+            "Filtered connected point for host {} not found";
+    private static final String HOST_EVENT = "Received HostEvent {}";
+    private static final String INTF_CONF_EVENT =
+            "Received InterfaceConfigEvent {}";
+    private static final String NET_CONF_EVENT =
+            "Received NetworkConfigEvent {}";
 
     private final Logger log = getLogger(getClass());
 
@@ -73,11 +100,20 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected IntentSynchronizationService intentSynchronizer;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigService configService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected VplsConfigurationService vplsConfigService;
+
     private final HostListener hostListener = new InternalHostListener();
 
     private final InternalInterfaceListener interfaceListener
             = new InternalInterfaceListener();
 
+    private final InternalNetworkConfigListener configListener =
+            new InternalNetworkConfigListener();
+
     private IntentInstaller intentInstaller;
 
     private ApplicationId appId;
@@ -97,111 +133,250 @@
 
         hostService.addListener(hostListener);
         interfaceService.addListener(interfaceListener);
+        configService.addListener(configListener);
 
-        setupConnectivity();
+        setupConnectivity(false);
 
         log.info("Activated");
     }
 
     @Deactivate
     public void deactivate() {
+        configService.removeListener(configListener);
         intentSynchronizer.removeIntentsByAppId(appId);
         log.info("Deactivated");
     }
 
-    protected void setupConnectivity() {
-        /*
-         * Parse Configuration and get Connect Point by VlanId.
-         */
-        SetMultimap<VlanId, ConnectPoint> confCPointsByVlan = getConfigCPoints();
+    /**
+     * Sets up connectivity for all VPLSs.
+     *
+     * @param isNetworkConfigEvent true if this function is triggered
+     *                             by NetworkConfigEvent; false otherwise
+     */
+    private void setupConnectivity(boolean isNetworkConfigEvent) {
+        SetMultimap<String, Interface> networkInterfaces =
+                vplsConfigService.getVplsNetworks();
 
-        /*
-         * Check that configured Connect Points have hosts attached and
-         * associate their Mac Address to the Connect Points configured.
-         */
-        SetMultimap<VlanId, Pair<ConnectPoint, MacAddress>> confHostPresentCPoint =
-                pairAvailableHosts(confCPointsByVlan);
+        Set<String> vplsAffectedByApi =
+                new HashSet<>(vplsConfigService.getVplsAffectedByApi());
 
-        /*
-         * Create and submit intents between the Connect Points.
-         * Intents for broadcast between all the configured Connect Points.
-         * Intents for unicast between all the configured Connect Points with
-         * hosts attached.
-         */
-        intentInstaller.installIntents(confHostPresentCPoint);
+        if (isNetworkConfigEvent && vplsAffectedByApi.isEmpty()) {
+            vplsAffectedByApi.addAll(vplsConfigService.getOldVpls());
+        }
 
+        networkInterfaces.asMap().forEach((networkName, interfaces) -> {
+            Set<Host> hosts = Sets.newHashSet();
+            interfaces.forEach(intf -> {
+                // Add hosts that belongs to the specific VPLS
+                hostService.getConnectedHosts(intf.connectPoint())
+                        .stream()
+                        .filter(host -> host.vlan().equals(intf.vlan()))
+                        .forEach(hosts::add);
+            });
+
+            setupConnectivity(networkName, interfaces, hosts,
+                    vplsAffectedByApi.contains(networkName));
+            vplsAffectedByApi.remove(networkName);
+        });
+
+        if (!vplsAffectedByApi.isEmpty()) {
+            for (String networkName:vplsAffectedByApi) {
+                withdrawIntents(networkName, Lists.newArrayList());
+            }
+        }
     }
 
     /**
-     * Computes the list of configured interfaces with a VLAN Id.
+     * Sets up connectivity for specific VPLS.
      *
-     * @return the interfaces grouped by vlan id
+     * @param networkName the VPLS name
+     * @param interfaces the interfaces that belong to the VPLS
+     * @param hosts the hosts that belong to the VPLS
+     * @param affectedByApi true if this function is triggered from the APIs;
+     *                      false otherwise
      */
-    protected SetMultimap<VlanId, ConnectPoint> getConfigCPoints() {
-        log.debug("Checking interface configuration");
+    private void setupConnectivity(String networkName,
+                                   Collection<Interface> interfaces,
+                                   Set<Host> hosts,
+                                   boolean affectedByApi) {
+        List<Intent> intents = Lists.newArrayList();
+        List<Key> keys = Lists.newArrayList();
+        Set<FilteredConnectPoint> fcPoints = buildFCPoints(interfaces);
 
-        SetMultimap<VlanId, ConnectPoint> confCPointsByVlan =
-                HashMultimap.create();
+        intents.addAll(buildUnicastIntents(
+                networkName, hosts, fcPoints, affectedByApi));
+        intents.addAll(buildBroadcastIntents(
+                networkName, fcPoints, affectedByApi));
 
-        interfaceService.getInterfaces()
-                .stream()
-                .filter(intf -> intf.ipAddressesList().isEmpty())
-                .forEach(intf -> confCPointsByVlan.put(intf.vlan(), intf.connectPoint()));
-        return confCPointsByVlan;
+        if (affectedByApi) {
+            intents.forEach(intent -> keys.add(intent.key()));
+            withdrawIntents(networkName, keys);
+        }
+
+        intentInstaller.submitIntents(intents);
     }
 
     /**
-     * Checks if for any ConnectPoint configured there's an host presents
-     * and in case it associates them together.
+     * Withdraws intents belonging to a VPLS, given a VPLS name.
      *
-     * @param confCPointsByVlan the configured ConnectPoints grouped by VLAN Id
-     * @return the configured ConnectPoints with eventual hosts associated.
+     * @param networkName the VPLS name
+     * @param keys the keys of the intents to be installed
      */
-    protected SetMultimap<VlanId, Pair<ConnectPoint, MacAddress>> pairAvailableHosts(
-            SetMultimap<VlanId, ConnectPoint> confCPointsByVlan) {
-        log.debug("Binding connected hosts MAC addresses");
+    private void withdrawIntents(String networkName,
+                                 List<Key> keys) {
+        List<Intent> intents = Lists.newArrayList();
 
-        SetMultimap<VlanId, Pair<ConnectPoint, MacAddress>> confHostPresentCPoint =
-                HashMultimap.create();
+        intentInstaller.getIntentsFromVpls(networkName)
+                .forEach(intent -> {
+                    if (!keys.contains(intent.key())) {
+                        intents.add(intent);
+                    }
+        });
 
-        confCPointsByVlan.entries()
-                .forEach(e -> bindMacAddr(e, confHostPresentCPoint));
-
-        return confHostPresentCPoint;
+        intentInstaller.withdrawIntents(intents);
     }
 
-    // Bind VLAN Id with hosts and connect points
-    private void bindMacAddr(Map.Entry<VlanId, ConnectPoint> e,
-                             SetMultimap<VlanId, Pair<ConnectPoint,
-                                     MacAddress>> confHostPresentCPoint) {
-        VlanId vlanId = e.getKey();
-        ConnectPoint cp = e.getValue();
-        Set<Host> connectedHosts = hostService.getConnectedHosts(cp);
-        connectedHosts.forEach(host -> {
-            if (host.vlan().equals(vlanId)) {
-                confHostPresentCPoint.put(vlanId, Pair.of(cp, host.mac()));
-            } else {
-                confHostPresentCPoint.put(vlanId, Pair.of(cp, null));
+    /**
+     * Sets up broadcast intents between any given filtered connect point.
+     *
+     * @param networkName the VPLS name
+     * @param fcPoints the set of filtered connect points
+     * @param affectedByApi true if the function triggered from APIs;
+     *                      false otherwise
+     * @return the set of broadcast intents
+     */
+    private Set<Intent> buildBroadcastIntents(String networkName,
+                                              Set<FilteredConnectPoint> fcPoints,
+                                              boolean affectedByApi) {
+        Set<Intent> intents = Sets.newHashSet();
+        fcPoints.forEach(point -> {
+            Set<FilteredConnectPoint> otherPoints =
+                    fcPoints.stream()
+                            .filter(fcp -> !fcp.equals(point))
+                            .collect(Collectors.toSet());
+
+            Key brcKey = intentInstaller.buildKey(PREFIX_BROADCAST,
+                                                  point.connectPoint(),
+                                                  networkName,
+                                                  MacAddress.BROADCAST);
+
+            if ((!intentInstaller.intentExists(brcKey) || affectedByApi) &&
+                    !otherPoints.isEmpty()) {
+                intents.add(intentInstaller.buildBrcIntent(brcKey,
+                                                           point,
+                                                           otherPoints));
             }
         });
-        if (connectedHosts.isEmpty()) {
-            confHostPresentCPoint.put(vlanId, Pair.of(cp, null));
-        }
+
+        return ImmutableSet.copyOf(intents);
+    }
+
+    /**
+     * Sets up unicast intents between any given filtered connect point.
+     *
+     * @param networkName the VPLS name
+     * @param hosts the set of destination hosts
+     * @param fcPoints the set of filtered connect points
+     * @param affectedByApi true if the function triggered from APIs;
+     *                      false otherwise
+     * @return the set of unicast intents
+     */
+    private Set<Intent> buildUnicastIntents(String networkName,
+                                            Set<Host> hosts,
+                                            Set<FilteredConnectPoint> fcPoints,
+                                            boolean affectedByApi) {
+        Set<Intent> intents = Sets.newHashSet();
+        hosts.forEach(host -> {
+            FilteredConnectPoint hostPoint = getHostPoint(host, fcPoints);
+
+            if (hostPoint == null) {
+                log.warn(HOST_FCP_NOT_FOUND, host);
+                return;
+            }
+
+            Set<FilteredConnectPoint> otherPoints =
+                    fcPoints.stream()
+                            .filter(fcp -> !fcp.equals(hostPoint))
+                            .collect(Collectors.toSet());
+
+            Key uniKey = intentInstaller.buildKey(PREFIX_UNICAST,
+                                                  host.location(),
+                                                  networkName,
+                                                  host.mac());
+
+            if ((!intentInstaller.intentExists(uniKey) || affectedByApi) &&
+                    !otherPoints.isEmpty()) {
+                intents.add(intentInstaller.buildUniIntent(uniKey,
+                                                           otherPoints,
+                                                           hostPoint,
+                                                           host));
+            }
+        });
+
+        return ImmutableSet.copyOf(intents);
+    }
+
+    /**
+     * Finds the filtered connect point a host is attached to.
+     *
+     * @param host the target host
+     * @param fcps the filtered connected points
+     * @return null if not found; the filtered connect point otherwise
+     */
+    private FilteredConnectPoint getHostPoint(Host host,
+                                              Set<FilteredConnectPoint> fcps) {
+        return fcps.stream()
+                .filter(fcp -> fcp.connectPoint().equals(host.location()))
+                .filter(fcp -> {
+                    VlanIdCriterion vlanCriterion =
+                            (VlanIdCriterion) fcp.trafficSelector().
+                                    getCriterion(Criterion.Type.VLAN_VID);
+
+                    return vlanCriterion != null &&
+                            vlanCriterion.vlanId().equals(host.vlan());
+                })
+                .findFirst()
+                .orElse(null);
+    }
+
+    /**
+     * Computes a set of filtered connect points from a list of given interfaces.
+     *
+     * @param interfaces the interfaces to compute
+     * @return the set of filtered connect points
+     */
+    private Set<FilteredConnectPoint> buildFCPoints(Collection<Interface> interfaces) {
+        // Build all filtered connected points in the network
+        return interfaces
+                .stream()
+                .map(intf -> {
+                    TrafficSelector.Builder selectorBuilder =
+                            DefaultTrafficSelector.builder();
+
+                    if (!intf.vlan().equals(VlanId.NONE)) {
+                        selectorBuilder.matchVlanId(intf.vlan());
+                    }
+
+                    return new FilteredConnectPoint(intf.connectPoint(),
+                                                    selectorBuilder.build());
+                })
+                .collect(Collectors.toSet());
     }
 
     /**
      * Listener for host events.
      */
-    class InternalHostListener implements HostListener {
+    private class InternalHostListener implements HostListener {
         @Override
         public void event(HostEvent event) {
-            log.debug("Received HostEvent {}", event);
+            log.debug(HOST_EVENT, event);
             switch (event.type()) {
                 case HOST_ADDED:
                 case HOST_UPDATED:
                 case HOST_REMOVED:
-                    setupConnectivity();
+                    setupConnectivity(false);
                     break;
+
                 default:
                     break;
             }
@@ -214,16 +389,39 @@
     private class InternalInterfaceListener implements InterfaceListener {
         @Override
         public void event(InterfaceEvent event) {
-            log.debug("Received InterfaceConfigEvent {}", event);
+            log.debug(INTF_CONF_EVENT, event);
             switch (event.type()) {
                 case INTERFACE_ADDED:
                 case INTERFACE_UPDATED:
                 case INTERFACE_REMOVED:
-                    setupConnectivity();
+                    setupConnectivity(false);
                     break;
+
                 default:
                     break;
             }
         }
     }
+
+    /**
+     * Listener for VPLS configuration events.
+     */
+    private class InternalNetworkConfigListener implements NetworkConfigListener {
+        @Override
+        public void event(NetworkConfigEvent event) {
+            if (event.configClass() == VplsConfigurationService.CONFIG_CLASS) {
+                log.debug(NET_CONF_EVENT, event.configClass());
+                switch (event.type()) {
+                    case CONFIG_ADDED:
+                    case CONFIG_UPDATED:
+                    case CONFIG_REMOVED:
+                        setupConnectivity(true);
+                        break;
+
+                    default:
+                        break;
+                }
+            }
+        }
+    }
 }
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsConfig.java b/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsConfig.java
new file mode 100644
index 0000000..f8323ef
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsConfig.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2016-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.config;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Sets;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.config.Config;
+
+import java.util.Set;
+
+/**
+ * Configuration object for VPLS config.
+ */
+public class VplsConfig extends Config<ApplicationId> {
+    private static final String VPLS = "vplsNetworks";
+    private static final String NAME = "name";
+    private static final String INTERFACE = "interfaces";
+
+    /**
+     * Returns a set of configured VPLSs.
+     *
+     * @return set of VPLSs
+     */
+    public Set<VplsNetworkConfig> vplsNetworks() {
+        Set<VplsNetworkConfig> vpls = Sets.newHashSet();
+
+        JsonNode vplsNode = object.get(VPLS);
+
+        if (vplsNode == null) {
+            return vpls;
+        }
+
+        vplsNode.forEach(jsonNode -> {
+            Set<String> ifaces = Sets.newHashSet();
+            jsonNode.path(INTERFACE).forEach(ifacesNode ->
+                    ifaces.add(ifacesNode.asText())
+            );
+
+            String name = jsonNode.get(NAME).asText();
+
+            vpls.add(new VplsNetworkConfig(name, ifaces));
+        });
+
+        return vpls;
+    }
+
+    /**
+     * Returns the VPLS configuration given a VPLS name.
+     *
+     * @param name the VPLS name
+     * @return the VPLS configuration if it exists; null otherwise
+     */
+    public VplsNetworkConfig getVplsWithName(String name) {
+        for (VplsNetworkConfig vpls : vplsNetworks()) {
+            if (vpls.name().equals(name)) {
+                return vpls;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Adds a VPLS to the configuration.
+     *
+     * @param name the name of the VPLS to be added
+     */
+    public void addVpls(VplsNetworkConfig name) {
+        ObjectNode vplsNode = JsonNodeFactory.instance.objectNode();
+
+        vplsNode.put(NAME, name.name());
+
+        ArrayNode ifacesNode = vplsNode.putArray(INTERFACE);
+        name.ifaces().forEach(ifacesNode::add);
+
+        ArrayNode vplsArray = vplsNetworks().isEmpty() ?
+                initVplsConfiguration() : (ArrayNode) object.get(VPLS);
+        vplsArray.add(vplsNode);
+    }
+
+    /**
+     * Removes a VPLS from the configuration.
+     *
+     * @param name the name of the VPLS to be removed
+     */
+    public void removeVpls(String name) {
+        ArrayNode vplsArray = (ArrayNode) object.get(VPLS);
+
+        for (int i = 0; i < vplsArray.size(); i++) {
+            if (vplsArray.get(i).hasNonNull(NAME) &&
+                    vplsArray.get(i).get(NAME).asText().equals(name)) {
+                vplsArray.remove(i);
+                return;
+            }
+        }
+    }
+
+    /**
+     * Finds a VPLS with a given network interface.
+     *
+     * @param iface the network interface
+     * @return the VPLS if found; null otherwise
+     */
+    public VplsNetworkConfig getVplsFromInterface(String iface) {
+        for (VplsNetworkConfig vpls : vplsNetworks()) {
+            if (vpls.isAttached(iface)) {
+                return vpls;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Adds a network interface to a VPLS.
+     *
+     * @param name the name of the VPLS
+     * @param iface the network interface to be added
+     */
+    public void addInterfaceToVpls(String name, String iface) {
+        JsonNode vplsNode = object.get(VPLS);
+        vplsNode.forEach(jsonNode -> {
+
+            if (hasNamedNode(jsonNode, name)) {
+                ArrayNode ifacesNode = (ArrayNode) jsonNode.get(INTERFACE);
+                for (int i = 0; i < ifacesNode.size(); i++) {
+                    if (ifacesNode.get(i).asText().equals(iface)) {
+                        return; // Interface already exists.
+                    }
+                }
+                ifacesNode.add(iface);
+            }
+        });
+    }
+
+    /**
+     * Removes a network interface from a VPLS.
+     *
+     * @param name the name of the VPLS
+     * @param iface the network interface to be removed
+     */
+    public void removeInterfaceFromVpls(VplsNetworkConfig name, String iface) {
+        JsonNode vplsNode = object.get(VPLS);
+        vplsNode.forEach(jsonNode -> {
+            if (hasNamedNode(jsonNode, name.name())) {
+                ArrayNode ifacesNode = (ArrayNode) jsonNode.get(INTERFACE);
+                for (int i = 0; i < ifacesNode.size(); i++) {
+                    if (ifacesNode.get(i).asText().equals(iface)) {
+                        ifacesNode.remove(i);
+                        return;
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * States if a JSON node has a "name" attribute and if the value is equal to
+     * the name given.
+     *
+     * @param jsonNode the JSON node
+     * @param name the node name
+     * @return true if the JSON node has a "name" attribute with value equal to
+     * the name given; false otherwise
+     */
+    private boolean hasNamedNode(JsonNode jsonNode, String name) {
+        return jsonNode.hasNonNull(NAME) &&
+                jsonNode.get(NAME).asText().equals(name);
+    }
+
+    /**
+     * Creates an empty VPLS configuration.
+     *
+     * @return empty ArrayNode to store the VPLS configuration
+     */
+    private ArrayNode initVplsConfiguration() {
+        return object.putArray(VPLS);
+    }
+}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsConfigurationService.java b/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsConfigurationService.java
new file mode 100644
index 0000000..16cd4c4
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsConfigurationService.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2016-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.config;
+
+import com.google.common.collect.SetMultimap;
+import org.onlab.packet.VlanId;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.net.ConnectPoint;
+
+import java.util.Set;
+
+/**
+ * Provides information about the VPLS configuration.
+ */
+public interface VplsConfigurationService {
+    Class<VplsConfig> CONFIG_CLASS = VplsConfig.class;
+
+    /**
+     * Adds a VPLS to the configuration.
+     *
+     * @param name the name of the VPLS
+     * @param ifaces the interfaces associated with the VPLS
+     */
+    void addVpls(String name, Set<String> ifaces);
+
+    /**
+     * Removes a VPLS from the configuration.
+     *
+     * @param name the name of the VPLS to be removed
+     */
+    void removeVpls(String name);
+
+    /**
+     * Adds a network interface to a VPLS.
+     *
+     * @param name the name of the VPLS
+     * @param iface the network interface to be added to the VPLS
+     */
+    void addInterfaceToVpls(String name, String iface);
+
+    /**
+     * Removes a network interface from a VPLS.
+     *
+     * @param iface the network interface to be removed from the VPLS
+     */
+    void removeInterfaceFromVpls(String iface);
+
+    /**
+     * Cleans up the VPLS configuration. Removes all VPLSs.
+     */
+    void cleanVpls();
+
+    /**
+     * Retrieves the VPLS names modified from CLI.
+     *
+     * @return a set of VPLS names modified from CLI
+     */
+    Set<String> getVplsAffectedByApi();
+    // TODO Removes this function after intent framework fix race condition
+
+    /**
+     * Retrieves the interfaces from the VPLS configuration.
+     *
+     * @return a set of interfaces contained in the VPLS configuration
+     */
+    Set<Interface> getAllInterfaces();
+
+    /**
+     * Retrieves the interfaces belonging to the VPLS.
+     *
+     * @param name the name of the VPLS
+     * @return a set of interfaces belonging to the VPLS
+     */
+    Set<Interface> getVplsInterfaces(String name);
+
+    /**
+     * Retrieves all VPLS names.
+     *
+     * @return a set of VPLS names
+     */
+    Set<String> getAllVpls();
+
+    /**
+     * Retrieves all VPLS names from the old config.
+     *
+     * @return a set of VPLS names
+     */
+    Set<String> getOldVpls();
+    // TODO Removes this function after intent framework fix race condition
+
+    /**
+     * Retrieves the VPLS names and associated interfaces from the configuration.
+     *
+     * @return a map VPLS names and associated interfaces
+     */
+    SetMultimap<String, Interface> getVplsNetworks();
+
+    /**
+     * Retrieves a VPLS network given a VLAN Id and a connect point.
+     *
+     * @param vlan the VLAN Id
+     * @param connectPoint the connect point
+     * @return a map VPLS names and associated interfaces; null otherwise
+     */
+    SetMultimap<String, Interface> getVplsNetwork(VlanId vlan,
+                                                 ConnectPoint connectPoint);
+}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsNetworkConfig.java b/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsNetworkConfig.java
new file mode 100644
index 0000000..b3c721b
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsNetworkConfig.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2016-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.config;
+
+import com.google.common.collect.ImmutableSet;
+
+import java.util.Objects;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Configuration of a VPLS Network.
+ */
+public class VplsNetworkConfig {
+    private final String name;
+    private final Set<String> ifaces;
+
+    /**
+     * Creates a new VPLS configuration.
+     *
+     * @param name the VPLS name
+     * @param ifaces the interfaces associated with the VPLS
+     */
+    public VplsNetworkConfig(String name, Set<String> ifaces) {
+        this.name = checkNotNull(name);
+        this.ifaces = checkNotNull(ImmutableSet.copyOf(ifaces));
+    }
+
+    /**
+     * Returns the name of the VPLS.
+     *
+     * @return the name of the VPLS
+     */
+    public String name() {
+        return name;
+    }
+
+    /**
+     * Returns the name of interfaces associated with the VPLS.
+     *
+     * @return a set of interface names associated with the VPLS
+     */
+    public Set<String> ifaces() {
+        return ImmutableSet.copyOf(ifaces);
+    }
+
+    /**
+     * States if a given interface is part of a VPLS.
+     *
+     * @param iface the interface attached to a VPLS
+     * @return true if the interface is associated to the VPLS; false otherwise
+     */
+    public boolean isAttached(String iface) {
+        return ifaces.stream().anyMatch(i -> i.equals(iface));
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof VplsNetworkConfig) {
+            final VplsNetworkConfig that = (VplsNetworkConfig) obj;
+            return Objects.equals(this.name, that.name) &&
+                    Objects.equals(this.ifaces, that.ifaces);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(name, ifaces);
+    }
+}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/VplsConfigurationImpl.java b/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/VplsConfigurationImpl.java
new file mode 100644
index 0000000..e977170
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/VplsConfigurationImpl.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright 2016-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.config.impl;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.SetMultimap;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.basics.SubjectFactories;
+import org.onosproject.vpls.config.VplsConfig;
+import org.onosproject.vpls.config.VplsNetworkConfig;
+import org.onosproject.vpls.config.VplsConfigurationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Implementation of VPLSConfigurationService which reads VPLS configuration
+ * from the network configuration service.
+ */
+@Component(immediate = true)
+@Service
+public class VplsConfigurationImpl implements VplsConfigurationService {
+    private static final String VPLS_APP = "org.onosproject.vpls";
+    private static final String VPLS = "vpls";
+    private static final String EMPTY = "";
+    private static final String CONFIG_NULL = "VPLS configuration not defined";
+    private static final String APP_ID_NULL = "VPLS application ID is null";
+    private static final String CONFIG_CHANGED = "VPLS configuration changed: {}";
+    private static final String CHECK_CONFIG =
+            "Checking the interface configuration";
+    private static final String NET_CONF_EVENT =
+            "Received NetworkConfigEvent {}";
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigRegistry registry;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected InterfaceService interfaceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigService configService;
+
+    private final Set<String> vplsAffectedByApi = new HashSet<>();
+
+    private VplsConfig vplsConfig = new VplsConfig();
+
+    private SetMultimap<String, String> ifacesOfVpls = HashMultimap.create();
+    private SetMultimap<String, String> oldIfacesOfVpls = HashMultimap.create();
+    private SetMultimap<String, Interface> vplsNetworks = HashMultimap.create();
+
+    private final InternalNetworkConfigListener configListener =
+            new InternalNetworkConfigListener();
+
+    private ConfigFactory<ApplicationId, VplsConfig> vplsConfigFactory =
+            new ConfigFactory<ApplicationId, VplsConfig>(
+                    SubjectFactories.APP_SUBJECT_FACTORY, VplsConfig.class, VPLS) {
+                @Override
+                public VplsConfig createConfig() {
+                    return new VplsConfig();
+                }
+            };
+
+    private ApplicationId vplsAppId;
+
+    @Activate
+    protected void active() {
+        configService.addListener(configListener);
+        registry.registerConfigFactory(vplsConfigFactory);
+        loadConfiguration();
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected  void deactive() {
+        registry.unregisterConfigFactory(vplsConfigFactory);
+        configService.removeListener(configListener);
+        log.info("Stopped");
+    }
+
+    /**
+     * Retrieves the VPLS configuration from network configuration.
+     */
+    private void loadConfiguration() {
+        loadAppId();
+
+        vplsConfig = configService.getConfig(vplsAppId, VplsConfig.class);
+
+        if (vplsConfig == null) {
+            log.warn(CONFIG_NULL);
+            configService.addConfig(vplsAppId, VplsConfig.class);
+            return;
+        }
+
+        oldIfacesOfVpls = ifacesOfVpls;
+        ifacesOfVpls = getConfigInterfaces();
+        vplsNetworks = getConfigCPoints();
+        log.debug(CONFIG_CHANGED, ifacesOfVpls);
+    }
+
+    /**
+     * Retrieves the application identifier from core service.
+     */
+    private void loadAppId() {
+        vplsAppId = coreService.getAppId(VPLS_APP);
+        if (vplsAppId == null) {
+            log.warn(APP_ID_NULL);
+        }
+    }
+
+    /**
+     * Applies a given configuration to the VPLS application.
+     */
+    private void applyConfig(VplsConfig vplsConfig) {
+        loadAppId();
+        configService.applyConfig(vplsAppId, VplsConfig.class, vplsConfig.node());
+    }
+
+    /**
+     * Retrieves the VPLS names and associated interfaces names from the configuration.
+     *
+     * @return a map VPLS names and associated interface names
+     */
+    private SetMultimap<String, String> getConfigInterfaces() {
+        SetMultimap<String, String> confIntfByVpls =
+                HashMultimap.create();
+
+        vplsConfig.vplsNetworks().forEach(vpls -> {
+            if (vpls.ifaces().isEmpty()) {
+                confIntfByVpls.put(vpls.name(), EMPTY);
+            } else {
+                vpls.ifaces().forEach(iface -> confIntfByVpls.put(vpls.name(), iface));
+            }
+        });
+
+        return confIntfByVpls;
+    }
+
+    /**
+     * Retrieves the VPLS names and associated interfaces from the configuration.
+     *
+     * @return a map VPLS names and associated interfaces
+     */
+    private SetMultimap<String, Interface> getConfigCPoints() {
+        log.debug(CHECK_CONFIG);
+
+        SetMultimap<String, Interface> confCPointsByIntf =
+                HashMultimap.create();
+
+        ifacesOfVpls.entries().forEach(vpls -> {
+            interfaceService.getInterfaces()
+                    .stream()
+                    .filter(intf -> intf.ipAddressesList().isEmpty())
+                    .filter(intf -> intf.name().equals(vpls.getValue()))
+                    .forEach(intf -> confCPointsByIntf.put(vpls.getKey(), intf));
+        });
+
+        return confCPointsByIntf;
+    }
+
+    /**
+     * Listener for VPLS configuration events.
+     */
+    private class InternalNetworkConfigListener implements NetworkConfigListener {
+        @Override
+        public void event(NetworkConfigEvent event) {
+            if (event.configClass() == VplsConfigurationService.CONFIG_CLASS) {
+                log.debug(NET_CONF_EVENT, event.configClass());
+                switch (event.type()) {
+                    case CONFIG_ADDED:
+                    case CONFIG_UPDATED:
+                    case CONFIG_REMOVED:
+                        loadConfiguration();
+                        break;
+
+                    default:
+                        break;
+                }
+            }
+        }
+    }
+
+    @Override
+    public void addVpls(String name, Set<String> ifaces) {
+        VplsNetworkConfig vpls;
+
+        if (ifacesOfVpls.containsKey(name)) {
+            if (ifaces.isEmpty()) {
+                return;
+            }
+
+            ifaces.forEach(iface ->
+                    vplsConfig.addInterfaceToVpls(name, iface));
+        } else {
+            vpls = new VplsNetworkConfig(name, ifaces);
+            vplsConfig.addVpls(vpls);
+        }
+
+        vplsAffectedByApi.add(name);
+        applyConfig(vplsConfig);
+    }
+
+    @Override
+    public void removeVpls(String name) {
+        if (ifacesOfVpls.containsKey(name)) {
+            vplsConfig.removeVpls(name);
+            vplsAffectedByApi.add(name);
+            applyConfig(vplsConfig);
+        }
+    }
+
+    @Override
+    public void addInterfaceToVpls(String name, String iface) {
+        if (ifacesOfVpls.containsKey(name)) {
+            vplsConfig.addInterfaceToVpls(name, iface);
+            vplsAffectedByApi.add(name);
+            applyConfig(vplsConfig);
+        }
+    }
+
+    @Override
+    public void removeInterfaceFromVpls(String iface) {
+        if (ifacesOfVpls.containsValue(iface)) {
+            VplsNetworkConfig vpls = vplsConfig.getVplsFromInterface(iface);
+            vplsConfig.removeInterfaceFromVpls(vpls, iface);
+            vplsAffectedByApi.add(vpls.name());
+            applyConfig(vplsConfig);
+        }
+    }
+
+    @Override
+    public void cleanVpls() {
+        ifacesOfVpls.entries().forEach(e -> {
+            vplsConfig.removeVpls(e.getKey());
+            vplsAffectedByApi.add(e.getKey());
+        });
+        applyConfig(vplsConfig);
+    }
+
+    @Override
+    public Set<String> getVplsAffectedByApi() {
+        Set<String> vplsNames = ImmutableSet.copyOf(vplsAffectedByApi);
+
+        vplsAffectedByApi.clear();
+
+        return vplsNames;
+    }
+
+    @Override
+    public Set<Interface> getAllInterfaces() {
+        Set<Interface> allInterfaces = new HashSet<>();
+        vplsNetworks.values().forEach(allInterfaces::add);
+
+        return allInterfaces;
+    }
+
+    @Override
+    public Set<Interface> getVplsInterfaces(String name) {
+        Set<Interface> vplsInterfaces = new HashSet<>();
+        vplsNetworks.get(name).forEach(vplsInterfaces::add);
+
+        return vplsInterfaces;
+    }
+
+    @Override
+    public Set<String> getAllVpls() {
+        return ifacesOfVpls.keySet();
+    }
+
+    @Override
+    public Set<String> getOldVpls() {
+        return oldIfacesOfVpls.keySet();
+    }
+
+    @Override
+    public SetMultimap<String, Interface> getVplsNetworks() {
+        return ImmutableSetMultimap.copyOf(vplsNetworks);
+    }
+
+    @Override
+    public SetMultimap<String, Interface> getVplsNetwork(VlanId vlan,
+                                                        ConnectPoint connectPoint) {
+        String vplsNetworkName =
+                vplsNetworks.entries().stream()
+                        .filter(e -> e.getValue().connectPoint().equals(connectPoint))
+                        .filter(e -> e.getValue().vlan().equals(vlan))
+                        .map(e -> e.getKey())
+                        .findFirst()
+                        .orElse(null);
+        SetMultimap<String, Interface> result = HashMultimap.create();
+        if (vplsNetworkName != null && vplsNetworks.containsKey(vplsNetworkName)) {
+            vplsNetworks.get(vplsNetworkName)
+                    .forEach(intf -> result.put(vplsNetworkName, intf));
+            return result;
+        }
+        return null;
+    }
+}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/package-info.java b/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/package-info.java
new file mode 100644
index 0000000..3229c7b
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016-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.
+ */
+
+/**
+ * Configuration implementation  to create L2 broadcast network using VLAN.
+ */
+package org.onosproject.vpls.config.impl;
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/config/package-info.java b/apps/vpls/src/main/java/org/onosproject/vpls/config/package-info.java
new file mode 100644
index 0000000..f080044
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/config/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016-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.
+ */
+
+/**
+ * Configuration to create L2 broadcast network using VLAN.
+ */
+package org.onosproject.vpls.config;