[ONOS-6248] VPLS refactoring

Change-Id: I8ffb2199ca108ad8dfe271681068636fc4af2a40
diff --git a/apps/vpls/BUCK b/apps/vpls/BUCK
index 471e91c..eb1ddec 100644
--- a/apps/vpls/BUCK
+++ b/apps/vpls/BUCK
@@ -6,7 +6,7 @@
     '//cli:onos-cli',
     '//utils/rest:onlab-rest',
     '//incubator/api:onos-incubator-api',
-    '//apps/intentsync:onos-apps-intentsync',
+    '//core/store/serializers:onos-core-serializers'
 ]
 
 TEST_DEPS = [
diff --git a/apps/vpls/pom.xml b/apps/vpls/pom.xml
index f3dd04f..dbe5ac4 100644
--- a/apps/vpls/pom.xml
+++ b/apps/vpls/pom.xml
@@ -35,7 +35,6 @@
         <onos.app.category>Traffic Steering</onos.app.category>
         <onos.app.title>VLAN L2 Broadcast Network App</onos.app.title>
         <onos.app.url>http://onosproject.org</onos.app.url>
-        <onos.app.requires>org.onosproject.intentsynchronizer</onos.app.requires>
     </properties>
 
     <dependencies>
@@ -45,21 +44,11 @@
         </dependency>
         <dependency>
             <groupId>org.onosproject</groupId>
-            <artifactId>onos-apps-intentsync</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.onosproject</groupId>
             <artifactId>onos-api</artifactId>
             <scope>test</scope>
             <classifier>tests</classifier>
         </dependency>
         <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>onos-app-sdnip</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
             <groupId>org.easymock</groupId>
             <artifactId>easymock</artifactId>
             <scope>test</scope>
@@ -73,6 +62,15 @@
             <groupId>org.apache.karaf.shell</groupId>
             <artifactId>org.apache.karaf.shell.console</artifactId>
         </dependency>
-
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-core-serializers</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-cli</artifactId>
+            <version>${project.version}</version>
+        </dependency>
     </dependencies>
 </project>
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/IntentInstaller.java b/apps/vpls/src/main/java/org/onosproject/vpls/IntentInstaller.java
deleted file mode 100644
index 94b819a..0000000
--- a/apps/vpls/src/main/java/org/onosproject/vpls/IntentInstaller.java
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * 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;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import org.onlab.packet.MacAddress;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.EncapsulationType;
-import org.onosproject.net.FilteredConnectPoint;
-import org.onosproject.net.Host;
-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.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;
-import org.onosproject.net.intent.constraint.EncapsulationConstraint;
-import org.onosproject.net.intent.constraint.PartialFailureConstraint;
-import org.onosproject.intentsync.IntentSynchronizationService;
-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 static org.onosproject.net.EncapsulationType.*;
-
-/**
- * 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 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 SEPARATOR = "-";
-
-    private final ApplicationId appId;
-    private final IntentSynchronizationService intentSynchronizer;
-    private final IntentService intentService;
-
-    public static final ImmutableList<Constraint> PARTIAL_FAILURE_CONSTRAINT =
-            ImmutableList.of(new PartialFailureConstraint());
-
-    /**
-     * Class constructor.
-     *
-     * @param appId              the Application ID
-     * @param intentService      the intent service
-     * @param intentSynchronizer the intent synchronizer service
-     */
-    public IntentInstaller(ApplicationId appId, IntentService intentService,
-                           IntentSynchronizationService intentSynchronizer) {
-        this.appId = appId;
-        this.intentService = intentService;
-        this.intentSynchronizer = intentSynchronizer;
-    }
-
-    /**
-     * Requests to install the intents passed as argument to the Intent Service.
-     *
-     * @param intents intents to be submitted
-     */
-    protected void submitIntents(Collection<Intent> intents) {
-        log.debug(SUBMIT);
-        intents.forEach(intentSynchronizer::submit);
-    }
-
-    /**
-     * Requests to withdraw the intents passed as argument to the Intent Service.
-     *
-     * @param intents intents to be withdraw
-     */
-    protected void withdrawIntents(Collection<Intent> intents) {
-        log.debug(WITHDRAW);
-        intents.forEach(intentSynchronizer::withdraw);
-    }
-
-    /**
-     * Returns list of intents belongs to a VPLS.
-     *
-     * @param name the name of the VPLS
-     * @return the list of intents belonging 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
-     * @param encap the encapsulation type
-     * @return the generated single-point to multi-point intent
-     */
-    protected SinglePointToMultiPointIntent buildBrcIntent(Key key,
-                                                           FilteredConnectPoint src,
-                                                           Set<FilteredConnectPoint> dsts,
-                                                           EncapsulationType encap) {
-        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);
-
-        setEncap(intentBuilder, PARTIAL_FAILURE_CONSTRAINT, encap);
-
-        return intentBuilder.build();
-    }
-
-    /**
-     * Builds a unicast intent.
-     *
-     * @param key key to identify the intent
-     * @param srcs the source Connect Points
-     * @param dst the destination Connect Point
-     * @param host destination Host
-     * @param encap the encapsulation type
-     * @return the generated multi-point to single-point intent
-     */
-    protected MultiPointToSinglePointIntent buildUniIntent(Key key,
-                                                           Set<FilteredConnectPoint> srcs,
-                                                           FilteredConnectPoint dst,
-                                                           Host host,
-                                                           EncapsulationType encap) {
-        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);
-
-        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
-     * @return the key to identify the intent
-     */
-    protected Key buildKey(String prefix,
-                           ConnectPoint cPoint,
-                           String vplsName,
-                           MacAddress hostMac) {
-        String keyString = vplsName +
-                SEPARATOR +
-                prefix +
-                SEPARATOR +
-                cPoint.deviceId() +
-                SEPARATOR +
-                cPoint.port() +
-                SEPARATOR +
-                hostMac;
-
-        return Key.of(keyString, appId);
-    }
-
-    /**
-     * Returns true if the specified intent exists; false otherwise.
-     *
-     * @param intentKey the intent key
-     * @return true if the intent exists; false otherwise
-     */
-    protected boolean intentExists(Key intentKey) {
-        if (intentService.getIntent(intentKey) == null) {
-            return false;
-        }
-
-        // Intent does not exist if intent withdrawn
-        IntentState currentIntentState = intentService.getIntentState(intentKey);
-        return !WITHDRAWN_INTENT_STATES.contains(currentIntentState);
-
-    }
-
-    /**
-     * 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));
-    }
-}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/Vpls.java b/apps/vpls/src/main/java/org/onosproject/vpls/Vpls.java
deleted file mode 100644
index af47248..0000000
--- a/apps/vpls/src/main/java/org/onosproject/vpls/Vpls.java
+++ /dev/null
@@ -1,432 +0,0 @@
-/*
- * Copyright 2015-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;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.common.collect.SetMultimap;
-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;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-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.EncapsulationType;
-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.intentsync.IntentSynchronizationService;
-import org.onosproject.vpls.config.VplsConfigService;
-import org.slf4j.Logger;
-
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import static org.onosproject.vpls.IntentInstaller.PREFIX_BROADCAST;
-import static org.onosproject.vpls.IntentInstaller.PREFIX_UNICAST;
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * Application to create L2 broadcast overlay networks using VLANs.
- */
-@Component(immediate = true)
-public class 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());
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected ApplicationService applicationService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected CoreService coreService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected HostService hostService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected IntentService intentService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected InterfaceService interfaceService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected IntentSynchronizationService intentSynchronizer;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected NetworkConfigService configService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected VplsConfigService 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;
-
-    @Activate
-    public void activate() {
-        appId = coreService.registerApplication(VPLS_APP);
-
-        intentInstaller = new IntentInstaller(appId,
-                                              intentService,
-                                              intentSynchronizer);
-
-        applicationService.registerDeactivateHook(appId, () -> {
-            intentSynchronizer.removeIntentsByAppId(appId);
-        });
-
-        hostService.addListener(hostListener);
-        interfaceService.addListener(interfaceListener);
-        configService.addListener(configListener);
-
-        setupConnectivity(false);
-
-        log.info("Activated");
-    }
-
-    @Deactivate
-    public void deactivate() {
-        hostService.removeListener(hostListener);
-        interfaceService.removeListener(interfaceListener);
-        configService.removeListener(configListener);
-        intentSynchronizer.removeIntentsByAppId(appId);
-        log.info("Deactivated");
-    }
-
-    /**
-     * 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.ifacesByVplsName();
-
-        Set<String> vplsAffectedByApi =
-                new HashSet<>(vplsConfigService.vplsAffectedByApi());
-
-        if (isNetworkConfigEvent && vplsAffectedByApi.isEmpty()) {
-            vplsAffectedByApi.addAll(vplsConfigService.vplsNamesOld());
-        }
-
-        networkInterfaces.asMap().forEach((vplsName, 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);
-            });
-
-            EncapsulationType encap =
-                    vplsConfigService.encap(vplsName);
-
-            setupConnectivity(vplsName, interfaces, hosts, encap,
-                              vplsAffectedByApi.contains(vplsName));
-            vplsAffectedByApi.remove(vplsName);
-        });
-
-        if (!vplsAffectedByApi.isEmpty()) {
-            for (String vplsName : vplsAffectedByApi) {
-                withdrawIntents(vplsName, Lists.newArrayList());
-            }
-        }
-    }
-
-    /**
-     * Sets up connectivity for specific VPLS.
-     *
-     * @param vplsName      the VPLS name
-     * @param interfaces    the interfaces that belong to the VPLS
-     * @param hosts         the hosts that belong to the VPLS
-     * @param encap         the encapsulation type
-     * @param affectedByApi true if this function is triggered from the APIs;
-     *                      false otherwise
-     */
-    private void setupConnectivity(String vplsName,
-                                   Collection<Interface> interfaces,
-                                   Set<Host> hosts,
-                                   EncapsulationType encap,
-                                   boolean affectedByApi) {
-
-        List<Intent> intents = Lists.newArrayList();
-        List<Key> keys = Lists.newArrayList();
-        Set<FilteredConnectPoint> fcPoints = buildFCPoints(interfaces);
-
-        intents.addAll(buildBroadcastIntents(
-                vplsName, fcPoints, encap, affectedByApi));
-        intents.addAll(buildUnicastIntents(
-                vplsName, hosts, fcPoints, encap, affectedByApi));
-
-        if (affectedByApi) {
-            intents.forEach(intent -> keys.add(intent.key()));
-            withdrawIntents(vplsName, keys);
-        }
-
-        intentInstaller.submitIntents(intents);
-    }
-
-    /**
-     * Withdraws intents belonging to a VPLS, given a VPLS name.
-     *
-     * @param vplsName the VPLS name
-     * @param keys     the keys of the intents to be installed
-     */
-    private void withdrawIntents(String vplsName, List<Key> keys) {
-        List<Intent> intents = Lists.newArrayList();
-
-        intentInstaller.getIntentsFromVpls(vplsName)
-                .forEach(intent -> {
-                    if (!keys.contains(intent.key())) {
-                        intents.add(intent);
-                    }
-                });
-
-        intentInstaller.withdrawIntents(intents);
-    }
-
-    /**
-     * Sets up broadcast intents between any given filtered connect point.
-     *
-     * @param vplsName      the VPLS name
-     * @param fcPoints      the set of filtered connect points
-     * @param encap         the encapsulation type
-     * @param affectedByApi true if the function triggered from APIs;
-     *                      false otherwise
-     * @return the set of broadcast intents
-     */
-    private Set<Intent> buildBroadcastIntents(String vplsName,
-                                              Set<FilteredConnectPoint> fcPoints,
-                                              EncapsulationType encap,
-                                              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(),
-                                                  vplsName,
-                                                  MacAddress.BROADCAST);
-
-            if ((!intentInstaller.intentExists(brcKey) || affectedByApi)
-                    && !otherPoints.isEmpty()) {
-                intents.add(intentInstaller.buildBrcIntent(brcKey,
-                                                           point,
-                                                           otherPoints,
-                                                           encap));
-            }
-        });
-
-        return ImmutableSet.copyOf(intents);
-    }
-
-    /**
-     * Sets up unicast intents between any given filtered connect point.
-     *
-     * @param vplsName      the VPLS name
-     * @param hosts         the set of destination hosts
-     * @param fcPoints      the set of filtered connect points
-     * @param encap         the encapsulation type
-     * @param affectedByApi true if the function triggered from APIs;
-     *                      false otherwise
-     * @return the set of unicast intents
-     */
-    private Set<Intent> buildUnicastIntents(String vplsName,
-                                            Set<Host> hosts,
-                                            Set<FilteredConnectPoint> fcPoints,
-                                            EncapsulationType encap,
-                                            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(),
-                                                  vplsName,
-                                                  host.mac());
-
-            if ((!intentInstaller.intentExists(uniKey) || affectedByApi) &&
-                    !otherPoints.isEmpty()) {
-                intents.add(intentInstaller.buildUniIntent(uniKey,
-                                                           otherPoints,
-                                                           hostPoint,
-                                                           host,
-                                                           encap));
-            }
-        });
-
-        return ImmutableSet.copyOf(intents);
-    }
-
-    /**
-     * Returns the filtered connect point associated to a given host.
-     *
-     * @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 VPLS
-        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.
-     */
-    private class InternalHostListener implements HostListener {
-        @Override
-        public void event(HostEvent event) {
-            log.debug(HOST_EVENT, event);
-            switch (event.type()) {
-                case HOST_ADDED:
-                case HOST_UPDATED:
-                case HOST_REMOVED:
-                    setupConnectivity(false);
-                    break;
-
-                default:
-                    break;
-            }
-        }
-    }
-
-    /**
-     * Listener for interface configuration events.
-     */
-    private class InternalInterfaceListener implements InterfaceListener {
-        @Override
-        public void event(InterfaceEvent event) {
-            log.debug(INTF_CONF_EVENT, event);
-            switch (event.type()) {
-                case INTERFACE_ADDED:
-                case INTERFACE_UPDATED:
-                case INTERFACE_REMOVED:
-                    setupConnectivity(false);
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-
-    /**
-     * Listener for VPLS configuration events.
-     */
-    private class InternalNetworkConfigListener implements NetworkConfigListener {
-        @Override
-        public void event(NetworkConfigEvent event) {
-            if (event.configClass() == VplsConfigService.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/VplsManager.java b/apps/vpls/src/main/java/org/onosproject/vpls/VplsManager.java
new file mode 100644
index 0000000..58ea4a2
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/VplsManager.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2015-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;
+
+import com.google.common.collect.ImmutableSet;
+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.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
+import org.onosproject.net.EncapsulationType;
+import org.onosproject.net.Host;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostService;
+import org.onosproject.store.StoreDelegate;
+import org.onosproject.vpls.api.VplsData;
+import org.onosproject.vpls.api.VplsOperationService;
+import org.onosproject.vpls.api.VplsOperation;
+import org.onosproject.vpls.api.Vpls;
+import org.onosproject.vpls.api.VplsStore;
+import org.onosproject.vpls.store.VplsStoreEvent;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.Set;
+
+import static java.util.Objects.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Application to create L2 broadcast overlay networks using VLANs.
+ */
+@Component(immediate = true)
+@Service
+public class VplsManager implements Vpls {
+    public static final String VPLS_APP = "org.onosproject.vpls";
+    private static final String UNSUPPORTED_STORE_EVENT_TYPE =
+            "Unsupported store event type {}.";
+    private final Logger log = getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostService hostService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected InterfaceService interfaceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected VplsStore vplsStore;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected VplsOperationService operationService;
+
+    private StoreDelegate<VplsStoreEvent> vplsStoreDelegate;
+    private HostListener vplsHostListener;
+
+    @Activate
+    public void activate() {
+        vplsStoreDelegate = new VplsStoreDelegate();
+        vplsHostListener = new VplsHostListener();
+
+        vplsStore.setDelegate(vplsStoreDelegate);
+        hostService.addListener(vplsHostListener);
+    }
+
+    @Deactivate
+    public void deactivate() {
+        vplsStore.unsetDelegate(vplsStoreDelegate);
+        hostService.removeListener(vplsHostListener);
+    }
+
+    @Override
+    public VplsData createVpls(String vplsName, EncapsulationType encapsulationType) {
+        requireNonNull(vplsName);
+        requireNonNull(encapsulationType);
+
+        if (vplsStore.getVpls(vplsName) != null) {
+            return null;
+        }
+
+        VplsData vplsData = VplsData.of(vplsName, encapsulationType);
+        vplsStore.addVpls(vplsData);
+
+        return vplsData;
+    }
+
+    @Override
+    public VplsData removeVpls(VplsData vplsData) {
+        requireNonNull(vplsData);
+        vplsData.state(VplsData.VplsState.REMOVING);
+        vplsStore.removeVpls(vplsData);
+        return vplsData;
+    }
+
+    @Override
+    public void addInterfaces(VplsData vplsData, Collection<Interface> interfaces) {
+        requireNonNull(vplsData);
+        requireNonNull(interfaces);
+        vplsData.addInterfaces(interfaces);
+        updateVplsStatus(vplsData, VplsData.VplsState.UPDATING);
+    }
+
+    @Override
+    public void addInterface(VplsData vplsData, Interface iface) {
+        requireNonNull(vplsData);
+        requireNonNull(iface);
+        vplsData.addInterface(iface);
+        updateVplsStatus(vplsData, VplsData.VplsState.UPDATING);
+    }
+
+    @Override
+    public void setEncapsulationType(VplsData vplsData, EncapsulationType encapsulationType) {
+        requireNonNull(vplsData);
+        requireNonNull(encapsulationType);
+        if (vplsData.encapsulationType().equals(encapsulationType)) {
+            // Encap type not changed.
+            return;
+        }
+        vplsData.encapsulationType(encapsulationType);
+        updateVplsStatus(vplsData, VplsData.VplsState.UPDATING);
+    }
+
+    @Override
+    public VplsData getVpls(String vplsName) {
+        requireNonNull(vplsName);
+        return vplsStore.getVpls(vplsName);
+    }
+
+    @Override
+    public Collection<Interface> removeInterfaces(VplsData vplsData, Collection<Interface> interfaces) {
+        requireNonNull(vplsData);
+        requireNonNull(interfaces);
+        vplsData.removeInterfaces(interfaces);
+        updateVplsStatus(vplsData, VplsData.VplsState.UPDATING);
+        return interfaces;
+    }
+
+    @Override
+    public Interface removeInterface(VplsData vplsData, Interface iface) {
+        requireNonNull(vplsData);
+        requireNonNull(iface);
+        vplsData.removeInterface(iface);
+        updateVplsStatus(vplsData, VplsData.VplsState.UPDATING);
+        return iface;
+    }
+
+    @Override
+    public void removeAllVpls() {
+        Set<VplsData> allVplses = ImmutableSet.copyOf(vplsStore.getAllVpls());
+        allVplses.forEach(this::removeVpls);
+    }
+
+    @Override
+    public Collection<VplsData> getAllVpls() {
+        return ImmutableSet.copyOf(vplsStore.getAllVpls());
+    }
+
+    /**
+     * Updates VPLS status to the store.
+     *
+     * @param vplsData the VPLS
+     * @param vplsState the new state to the VPLS
+     */
+    private void updateVplsStatus(VplsData vplsData, VplsData.VplsState vplsState) {
+        vplsData.state(vplsState);
+        vplsStore.updateVpls(vplsData);
+    }
+
+    /**
+     * A listener for host events.
+     * Updates a VPLS if host added or removed.
+     */
+    class VplsHostListener implements HostListener {
+        @Override
+        public void event(HostEvent event) {
+            Host host = event.subject();
+            Interface iface = getHostInterface(host);
+            if (iface == null) {
+                return;
+            }
+            VplsData vplsData = vplsStore.getAllVpls().stream()
+                    .filter(v -> v.interfaces().contains(iface))
+                    .findFirst()
+                    .orElse(null);
+            if (vplsData == null) {
+                // the host does not related to any vpls
+                return;
+            }
+            updateVplsStatus(vplsData, VplsData.VplsState.UPDATING);
+        }
+
+        /**
+         * Gets the network interface of the host.
+         *
+         * @param host the host
+         * @return the network interface of the host; null if no network
+         * interface found
+         */
+        private Interface getHostInterface(Host host) {
+            Set<Interface> interfaces = interfaceService.getInterfaces();
+            return interfaces.stream()
+                    .filter(iface -> iface.connectPoint().equals(host.location()) &&
+                            iface.vlan().equals(host.vlan()))
+                    .findFirst()
+                    .orElse(null);
+        }
+    }
+
+    /**
+     * Store delegate for VPLS store.
+     * Handles VPLS store event and generate VPLS operation according to event
+     * type.
+     */
+    class VplsStoreDelegate implements StoreDelegate<VplsStoreEvent> {
+
+        @Override
+        public void notify(VplsStoreEvent event) {
+            VplsOperation vplsOperation;
+            VplsOperation.Operation op;
+            VplsData vplsData = event.subject();
+            switch (event.type()) {
+                case ADD:
+                    op = VplsOperation.Operation.ADD;
+                    break;
+                case REMOVE:
+                    op = VplsOperation.Operation.REMOVE;
+                    break;
+                case UPDATE:
+                    if (vplsData.state() == VplsData.VplsState.FAILED ||
+                            vplsData.state() == VplsData.VplsState.ADDED ||
+                            vplsData.state() == VplsData.VplsState.REMOVED) {
+                        // Update the state only. Nothing to do if it is updated
+                        // to ADDED, REMOVED or FAILED
+                        op = null;
+                    } else {
+                        op = VplsOperation.Operation.UPDATE;
+                    }
+                    break;
+                default:
+                    log.warn(UNSUPPORTED_STORE_EVENT_TYPE, event.type());
+                    return;
+            }
+            if (op != null) {
+                vplsOperation = VplsOperation.of(vplsData, op);
+                operationService.submit(vplsOperation);
+            }
+        }
+    }
+}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/VplsNeighbourHandler.java b/apps/vpls/src/main/java/org/onosproject/vpls/VplsNeighbourHandler.java
index de4365f..8a3ac7d 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/VplsNeighbourHandler.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/VplsNeighbourHandler.java
@@ -15,12 +15,13 @@
  */
 package org.onosproject.vpls;
 
-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.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.incubator.net.intf.Interface;
@@ -30,16 +31,20 @@
 import org.onosproject.incubator.net.neighbour.NeighbourMessageContext;
 import org.onosproject.incubator.net.neighbour.NeighbourMessageHandler;
 import org.onosproject.incubator.net.neighbour.NeighbourResolutionService;
+import org.onosproject.net.ConnectPoint;
 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.device.DeviceService;
 import org.onosproject.net.host.HostService;
-import org.onosproject.vpls.config.VplsConfigService;
+import org.onosproject.vpls.api.VplsData;
+import org.onosproject.vpls.api.VplsStore;
 import org.slf4j.Logger;
 
+import java.util.Collection;
+import java.util.Objects;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -58,16 +63,13 @@
     protected CoreService coreService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected DeviceService deviceService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected InterfaceService interfaceService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected NeighbourResolutionService neighbourService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected VplsConfigService vplsConfigService;
+    protected VplsStore vplsStore;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected NetworkConfigService configService;
@@ -88,7 +90,7 @@
 
     @Activate
     protected void activate() {
-        appId = coreService.registerApplication(Vpls.VPLS_APP);
+        appId = coreService.registerApplication(VplsManager.VPLS_APP);
         interfaceService.addListener(interfaceListener);
         configService.addListener(configListener);
         configNeighbourHandler();
@@ -101,18 +103,16 @@
         neighbourService.unregisterNeighbourHandlers(appId);
     }
 
-    private void configNeighbourHandler() {
+    /**
+     * Registers neighbour handler to all available interfaces.
+     */
+    protected void configNeighbourHandler() {
         neighbourService.unregisterNeighbourHandlers(appId);
-        Set<Interface> interfaces = vplsConfigService.allIfaces();
-
-        interfaceService.getInterfaces()
-                .stream()
-                .filter(interfaces::contains)
-                .forEach(intf -> {
-                    neighbourService.registerNeighbourHandler(intf,
-                                                              neighbourHandler,
-                                                              appId);
-                });
+        interfaceService
+                .getInterfaces()
+                .forEach(intf -> neighbourService.registerNeighbourHandler(intf,
+                                                                       neighbourHandler,
+                                                                       appId));
     }
 
     /**
@@ -123,16 +123,13 @@
         @Override
         public void handleMessage(NeighbourMessageContext context,
                                   HostService hostService) {
-
             switch (context.type()) {
                 case REQUEST:
                     handleRequest(context);
                     break;
-
                 case REPLY:
                     handleReply(context, hostService);
                     break;
-
                 default:
                     log.warn(UNKNOWN_CONTEXT, context.type());
                     break;
@@ -146,16 +143,15 @@
      * @param context the message context
      */
     protected void handleRequest(NeighbourMessageContext context) {
-        SetMultimap<String, Interface> interfaces =
-                vplsConfigService.ifacesByVplsName(context.vlan(),
-                                                   context.inPort());
-        if (interfaces != null) {
-            interfaces.values().stream()
+        // Find target VPLS first, then broadcast to all interface of this VPLS
+        VplsData vplsData = findVpls(context);
+        if (vplsData != null) {
+            vplsData.interfaces().stream()
                     .filter(intf -> !context.inPort().equals(intf.connectPoint()))
                     .forEach(context::forward);
-
         } else {
-            log.debug(CAN_NOT_FIND_VPLS, context.inPort(), context.vlan());
+            log.warn(CAN_NOT_FIND_VPLS, context.inPort(), context.vlan());
+            context.drop();
         }
     }
 
@@ -167,21 +163,66 @@
      */
     protected void handleReply(NeighbourMessageContext context,
                                HostService hostService) {
-        Set<Host> hosts = hostService.getHostsByMac(context.dstMac());
-        SetMultimap<String, Interface> interfaces =
-                vplsConfigService.ifacesByVplsName(context.vlan(),
-                                                   context.inPort());
-        if (interfaces != null) {
-            hosts.forEach(host -> interfaces.values().stream()
-                    .filter(intf -> intf.connectPoint().equals(host.location()))
-                    .filter(intf -> intf.vlan().equals(host.vlan()))
-                    .forEach(context::forward));
+        // Find target VPLS, then reply to the host
+        VplsData vplsData = findVpls(context);
+        if (vplsData != null) {
+            MacAddress dstMac = context.dstMac();
+            Set<Host> hosts = hostService.getHostsByMac(dstMac);
+            hosts = hosts.stream()
+                    .filter(host -> vplsData.interfaces().contains(getHostInterface(host)))
+                    .collect(Collectors.toSet());
+
+            // reply to all host in same VPLS
+            hosts.stream()
+                    .map(this::getHostInterface)
+                    .filter(Objects::nonNull)
+                    .forEach(context::forward);
         } else {
-            log.debug(CAN_NOT_FIND_VPLS, context.inPort(), context.vlan());
+            // this might be happened when we remove an interface from VPLS
+            // just ignore this message
+            log.warn(CAN_NOT_FIND_VPLS, context.inPort(), context.vlan());
+            context.drop();
         }
     }
 
     /**
+     * Finds the VPLS with given neighbour message context.
+     *
+     * @param context the neighbour message context
+     * @return the VPLS for specific neighbour message context
+     */
+    private VplsData findVpls(NeighbourMessageContext context) {
+        Collection<VplsData> vplses = vplsStore.getAllVpls();
+        for (VplsData vplsData : vplses) {
+            Set<Interface> interfaces = vplsData.interfaces();
+            ConnectPoint port = context.inPort();
+            VlanId vlanId = context.vlan();
+            boolean match = interfaces.stream()
+                    .anyMatch(iface -> iface.connectPoint().equals(port) &&
+                            iface.vlan().equals(vlanId));
+            if (match) {
+                return vplsData;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Finds the network interface related to the host.
+     *
+     * @param host the host
+     * @return the interface related to the host
+     */
+    private Interface getHostInterface(Host host) {
+        Set<Interface> interfaces = interfaceService.getInterfaces();
+        return interfaces.stream()
+                .filter(iface -> iface.connectPoint().equals(host.location()) &&
+                                 iface.vlan().equals(host.vlan()))
+                .findFirst()
+                .orElse(null);
+    }
+
+    /**
      * Listener for interface configuration events.
      */
     private class VplsInterfaceListener implements InterfaceListener {
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/VplsOperationManager.java b/apps/vpls/src/main/java/org/onosproject/vpls/VplsOperationManager.java
new file mode 100644
index 0000000..4c0262d
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/VplsOperationManager.java
@@ -0,0 +1,645 @@
+/*
+ * 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;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Queues;
+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;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.Tools;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipEvent;
+import org.onosproject.cluster.LeadershipEventListener;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.net.Host;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentEvent;
+import org.onosproject.net.intent.IntentException;
+import org.onosproject.net.intent.IntentListener;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.IntentUtils;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.net.intent.SinglePointToMultiPointIntent;
+import org.onosproject.vpls.api.VplsData;
+import org.onosproject.vpls.api.VplsOperationException;
+import org.onosproject.vpls.api.VplsOperationService;
+import org.onosproject.vpls.api.VplsOperation;
+import org.onosproject.vpls.api.VplsStore;
+import org.onosproject.vpls.intent.VplsIntentUtility;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.Deque;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.onlab.util.BoundedThreadPool.newFixedThreadPool;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * An implementation of VplsOperationService.
+ * Handles the execution order of the VPLS operations generated by the
+ * application.
+ */
+@Component(immediate = true)
+@Service
+public class VplsOperationManager implements VplsOperationService {
+    private static final int NUM_THREADS = 4;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected IntentService intentService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected LeadershipService leadershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ClusterService clusterService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostService hostService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected VplsStore vplsStore;
+
+    private final Logger log = getLogger(getClass());
+    protected Map<String, Deque<VplsOperation>> pendingVplsOperations;
+    protected final Map<String, VplsOperation> runningOperations = Maps.newHashMap();
+    protected ScheduledExecutorService schedulerExecutor;
+    protected ExecutorService workerExecutor;
+    protected ApplicationId appId;
+    protected boolean isLeader;
+    protected NodeId localNodeId;
+    protected LeadershipEventListener leadershipEventListener;
+
+    @Activate
+    public void activate() {
+        appId = coreService.registerApplication(VplsManager.VPLS_APP);
+        localNodeId = clusterService.getLocalNode().id();
+
+        leadershipEventListener = new InternalLeadershipListener();
+        leadershipService.addListener(leadershipEventListener);
+        leadershipService.runForLeadership(appId.name());
+        pendingVplsOperations = Maps.newConcurrentMap();
+
+        // Thread pool for VplsOperationExecutor
+        workerExecutor = newFixedThreadPool(NUM_THREADS,
+                                            groupedThreads("onos/apps/vpls",
+                                                           "worker-%d",
+                                                           log));
+        // A single thread pool for VplsOperationScheduler
+        schedulerExecutor = Executors.newScheduledThreadPool(1,
+                                                             groupedThreads("onos/apps/vpls",
+                                                                            "scheduler-%d",
+                                                                            log));
+        // Start the scheduler
+        schedulerExecutor.scheduleAtFixedRate(new VplsOperationScheduler(),
+                                              0,
+                                              500,
+                                              TimeUnit.MILLISECONDS);
+
+    }
+
+    @Deactivate
+    public void deactivate() {
+        pendingVplsOperations.clear();
+        runningOperations.clear();
+        leadershipService.removeListener(leadershipEventListener);
+        schedulerExecutor.shutdown();
+        workerExecutor.shutdown();
+
+        // remove all intents from VPLS application when deactivated
+        Tools.stream(intentService.getIntents())
+                .filter(intent -> intent.appId().equals(appId))
+                .forEach(intentService::withdraw);
+    }
+
+    @Override
+    public void submit(VplsOperation vplsOperation) {
+        if (isLeader) {
+            // Only leader can execute operation
+            addVplsOperation(vplsOperation);
+        }
+    }
+
+    /**
+     * Adds a VPLS operation to the queue of pending operations.
+     *
+     * @param vplsOperation the VPLS operation to add
+     */
+    private void addVplsOperation(VplsOperation vplsOperation) {
+        VplsData vplsData = vplsOperation.vpls();
+        pendingVplsOperations.compute(vplsData.name(), (name, opQueue) -> {
+            opQueue = opQueue == null ? Queues.newArrayDeque() : opQueue;
+
+            // If the operation already exist in queue, ignore it.
+            if (opQueue.contains(vplsOperation)) {
+                return opQueue;
+            }
+            opQueue.add(vplsOperation);
+            return opQueue;
+        });
+    }
+
+    /**
+     * Optimizes the VPLS operation queue and return a single VPLS operation to
+     * execute.
+     *
+     * @param operations the queue to be optimized
+     * @return optimized VPLS operation from the queue
+     */
+    protected static VplsOperation getOptimizedVplsOperation(Deque<VplsOperation> operations) {
+        if (operations.isEmpty()) {
+            return null;
+        }
+        // no need to optimize if the queue contains only one operation
+        if (operations.size() == 1) {
+            return operations.getFirst();
+        }
+
+        final VplsOperation firstOperation = operations.peekFirst();
+        final VplsOperation lastOperation = operations.peekLast();
+        final VplsOperation.Operation firstOp = firstOperation.op();
+        final VplsOperation.Operation lastOp = lastOperation.op();
+
+        if (firstOp.equals(VplsOperation.Operation.REMOVE)) {
+            if (lastOp.equals(VplsOperation.Operation.REMOVE)) {
+                // case 1: both first and last operation are REMOVE; do remove
+                return firstOperation;
+            } else if (lastOp.equals(VplsOperation.Operation.ADD)) {
+                // case 2: if first is REMOVE, and last is ADD; do update
+                return VplsOperation.of(lastOperation.vpls(),
+                                                 VplsOperation.Operation.UPDATE);
+            } else {
+                // case 3: first is REMOVE, last is UPDATE; do update
+                return lastOperation;
+            }
+        } else if (firstOp.equals(VplsOperation.Operation.ADD)) {
+            if (lastOp.equals(VplsOperation.Operation.REMOVE)) {
+                // case 4: first is ADD, last is REMOVE; nothing to do
+                return null;
+            } else if (lastOp.equals(VplsOperation.Operation.ADD)) {
+                // case 5: both first and last are ADD, do add
+                return VplsOperation.of(lastOperation.vpls(),
+                                                 VplsOperation.Operation.ADD);
+            } else {
+                // case 6: first is ADD and last is update, do add
+                return VplsOperation.of(lastOperation.vpls(),
+                                                 VplsOperation.Operation.ADD);
+            }
+        } else {
+            if (lastOp.equals(VplsOperation.Operation.REMOVE)) {
+                // case 7: last is remove, do remove
+                return lastOperation;
+            } else if (lastOp.equals(VplsOperation.Operation.ADD)) {
+                // case 8: do update only
+                return VplsOperation.of(lastOperation.vpls(),
+                                                 VplsOperation.Operation.UPDATE);
+            } else {
+                // case 9: from UPDATE to UPDATE
+                // only need last UPDATE operation
+                return VplsOperation.of(lastOperation.vpls(),
+                                                 VplsOperation.Operation.UPDATE);
+            }
+        }
+    }
+
+    /**
+     * Scheduler for VPLS operation.
+     * Processes a batch of VPLS operations in a period.
+     */
+    class VplsOperationScheduler implements Runnable {
+        private static final String UNKNOWN_STATE =
+                "Unknown state {} for success consumer";
+        private static final String OP_EXEC_ERR =
+                "Error when executing VPLS operation {}, error: {}";
+
+        /**
+         * Process a batch of VPLS operations.
+         */
+        @Override
+        public void run() {
+            Set<String> vplsNames = pendingVplsOperations.keySet();
+            vplsNames.forEach(vplsName -> {
+                VplsOperation operation;
+                synchronized (runningOperations) {
+                    // Only one operation for a VPLS at the same time
+                    if (runningOperations.containsKey(vplsName)) {
+                        return;
+                    }
+                    Deque<VplsOperation> operations = pendingVplsOperations.remove(vplsName);
+                    operation = getOptimizedVplsOperation(operations);
+                    if (operation == null) {
+                        // Nothing to do, this only happened when we add a VPLS
+                        // and remove it before batch operations been processed.
+                        return;
+                    }
+                    runningOperations.put(vplsName, operation);
+                }
+
+                VplsOperationExecutor operationExecutor =
+                        new VplsOperationExecutor(operation);
+                operationExecutor.setConsumers(
+                        (vplsOperation) -> {
+                            // Success consumer
+                            VplsData vplsData = vplsOperation.vpls();
+                            log.debug("VPLS operation success: {}", vplsOperation);
+                            switch (vplsData.state()) {
+                                case ADDING:
+                                case UPDATING:
+                                    vplsData.state(VplsData.VplsState.ADDED);
+                                    vplsStore.updateVpls(vplsData);
+                                    break;
+                                case REMOVING:
+                                    // The VPLS information does not exists in
+                                    // store. No need to update the store.
+                                    break;
+                                default:
+                                    log.warn(UNKNOWN_STATE, vplsData.state());
+                                    vplsData.state(VplsData.VplsState.FAILED);
+                                    vplsStore.updateVpls(vplsData);
+                                    break;
+                            }
+                            runningOperations.remove(vplsName);
+                        },
+                        (vplsOperationException) -> {
+                            // Error consumer
+                            VplsOperation vplsOperation =
+                                    vplsOperationException.vplsOperation();
+                            log.debug("VPLS operation failed: {}", vplsOperation);
+                            VplsData vplsData = vplsOperation.vpls();
+                            vplsData.state(VplsData.VplsState.FAILED);
+                            vplsStore.updateVpls(vplsData);
+                            log.error(OP_EXEC_ERR,
+                                      vplsOperation.toString(),
+                                      vplsOperationException.getMessage());
+                            runningOperations.remove(vplsName);
+                        });
+                log.debug("Applying operation: {}", operation);
+                workerExecutor.execute(operationExecutor);
+            });
+        }
+    }
+
+    /**
+     * Direction for Intent installation.
+     */
+    private enum Direction {
+        ADD,
+        REMOVE
+    }
+
+    /**
+     * VPLS operation executor.
+     * Installs, updates or removes Intents according to the given VPLS operation.
+     */
+    class VplsOperationExecutor implements Runnable {
+        private static final String UNKNOWN_OP = "Unknown operation.";
+        private static final String UNKNOWN_INTENT_DIR = "Unknown Intent install direction.";
+        private static final int OPERATION_TIMEOUT = 10;
+        private VplsOperation vplsOperation;
+        private Consumer<VplsOperation> successConsumer;
+        private Consumer<VplsOperationException> errorConsumer;
+        private VplsOperationException error;
+
+        public VplsOperationExecutor(VplsOperation vplsOperation) {
+            this.vplsOperation = vplsOperation;
+            this.error = null;
+        }
+
+        /**
+         * Sets success consumer and error consumer for this executor.
+         *
+         * @param successConsumer the success consumer
+         * @param errorConsumer the error consumer
+         */
+        public void setConsumers(Consumer<VplsOperation> successConsumer,
+                                 Consumer<VplsOperationException> errorConsumer) {
+            this.successConsumer = successConsumer;
+            this.errorConsumer = errorConsumer;
+
+        }
+
+        @Override
+        public void run() {
+            switch (vplsOperation.op()) {
+                case ADD:
+                    installVplsIntents();
+                    break;
+                case REMOVE:
+                    removeVplsIntents();
+                    break;
+                case UPDATE:
+                    updateVplsIntents();
+                    break;
+                default:
+                    this.error = new VplsOperationException(vplsOperation,
+                                                            UNKNOWN_OP);
+                    break;
+            }
+
+            if (this.error != null) {
+                errorConsumer.accept(this.error);
+            } else {
+                successConsumer.accept(vplsOperation);
+            }
+        }
+
+        /**
+         * Updates Intents of the VPLS.
+         */
+        private void updateVplsIntents() {
+            // check which part we need to update
+            // if we update host only, we don't need to reinstall
+            // every Intents
+            Set<Intent> intentsToInstall = Sets.newHashSet();
+            Set<Intent> intentsToUninstall = Sets.newHashSet();
+            VplsData vplsData = vplsOperation.vpls();
+            Set<Intent> currentIntents = getCurrentIntents();
+
+            // Compares broadcast Intents
+            Set<Intent> currentBrcIntents = currentIntents.stream()
+                    .filter(intent -> intent instanceof SinglePointToMultiPointIntent)
+                    .collect(Collectors.toSet());
+            Set<Intent> targetBrcIntents = VplsIntentUtility.buildBrcIntents(vplsData, appId);
+            if (!intentSetEquals(currentBrcIntents, targetBrcIntents)) {
+                // If broadcast Intents changes, it means some network
+                // interfaces or encapsulation constraint changed; Need to
+                // reinstall all intents
+                removeVplsIntents();
+                installVplsIntents();
+                return;
+            }
+
+            // Compares unicast Intents
+            Set<Intent> currentUniIntents = currentIntents.stream()
+                    .filter(intent -> intent instanceof MultiPointToSinglePointIntent)
+                    .collect(Collectors.toSet());
+            Set<Intent> targetUniIntents = VplsIntentUtility.buildUniIntents(vplsData,
+                                                                             hostsFromVpls(),
+                                                                             appId);
+
+            // New unicast Intents to install
+            targetUniIntents.forEach(intent -> {
+                if (!currentUniIntents.contains(intent)) {
+                    intentsToInstall.add(intent);
+                }
+            });
+
+            // Old unicast Intents to remove
+            currentUniIntents.forEach(intent -> {
+                if (!targetUniIntents.contains(intent)) {
+                    intentsToUninstall.add(intent);
+                }
+            });
+            applyIntentsSync(intentsToUninstall, Direction.REMOVE);
+            applyIntentsSync(intentsToInstall, Direction.ADD);
+        }
+
+        private Set<Host> hostsFromVpls() {
+            VplsData vplsData = vplsOperation.vpls();
+            Set<Interface> interfaces = vplsData.interfaces();
+            return interfaces.stream()
+                    .map(this::hostsFromInterface)
+                    .flatMap(Collection::stream)
+                    .collect(Collectors.toSet());
+        }
+
+        private Set<Host> hostsFromInterface(Interface iface) {
+            return hostService.getConnectedHosts(iface.connectPoint())
+                    .stream()
+                    .filter(host -> host.vlan().equals(iface.vlan()))
+                    .collect(Collectors.toSet());
+        }
+
+        /**
+         * Applies Intents synchronously with a specific direction.
+         *
+         * @param intents the Intents
+         * @param direction the direction
+         */
+        private void applyIntentsSync(Set<Intent> intents, Direction direction) {
+            Set<Key> pendingIntentKeys = intents.stream()
+                    .map(Intent::key).collect(Collectors.toSet());
+            IntentCompleter completer;
+
+            switch (direction) {
+                case ADD:
+                    completer = new IntentCompleter(pendingIntentKeys,
+                                                    IntentEvent.Type.INSTALLED);
+                    intentService.addListener(completer);
+                    intents.forEach(intentService::submit);
+                    break;
+                case REMOVE:
+                    completer = new IntentCompleter(pendingIntentKeys,
+                                                    IntentEvent.Type.WITHDRAWN);
+                    intentService.addListener(completer);
+                    intents.forEach(intentService::withdraw);
+                    break;
+                default:
+                    this.error = new VplsOperationException(this.vplsOperation,
+                                                            UNKNOWN_INTENT_DIR);
+                    return;
+            }
+
+            try {
+                // Wait until Intent operation completed
+                completer.complete();
+            } catch (VplsOperationException e) {
+                this.error = e;
+            } finally {
+                intentService.removeListener(completer);
+            }
+        }
+
+        /**
+         * Checks if two sets of Intents are equal.
+         *
+         * @param intentSet1 the first set of Intents
+         * @param intentSet2 the second set of Intents
+         * @return true if both set of Intents are equal; otherwise false
+         */
+        private boolean intentSetEquals(Set<Intent> intentSet1, Set<Intent> intentSet2) {
+            if (intentSet1.size() != intentSet2.size()) {
+                return false;
+            }
+            for (Intent intent1 : intentSet1) {
+                if (intentSet2.stream()
+                        .noneMatch(intent2 -> IntentUtils.intentsAreEqual(intent1, intent2))) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        /**
+         * Retrieves installed Intents from IntentService which related to
+         * specific VPLS.
+         *
+         * @return the Intents which related to the VPLS
+         */
+        private Set<Intent> getCurrentIntents() {
+            VplsData vplsData = vplsOperation.vpls();
+            String vplsName = vplsData.name();
+            return Tools.stream(intentService.getIntents())
+                    .filter(intent -> intent.key().toString().startsWith(vplsName))
+                    .collect(Collectors.toSet());
+        }
+
+        /**
+         * Generates unicast Intents and broadcast Intents for the VPLS.
+         *
+         * @return Intents for the VPLS
+         */
+        private Set<Intent> generateVplsIntents() {
+            VplsData vplsData = vplsOperation.vpls();
+            Set<Intent> brcIntents = VplsIntentUtility.buildBrcIntents(vplsData, appId);
+            Set<Intent> uniIntent = VplsIntentUtility.buildUniIntents(vplsData, hostsFromVpls(), appId);
+
+            return Stream.concat(brcIntents.stream(), uniIntent.stream())
+                    .collect(Collectors.toSet());
+        }
+
+        /**
+         * Removes all Intents from the VPLS.
+         */
+        private void removeVplsIntents() {
+            Set<Intent> intentsToWithdraw = getCurrentIntents();
+            applyIntentsSync(intentsToWithdraw, Direction.REMOVE);
+        }
+
+        /**
+         * Installs Intents of the VPLS.
+         */
+        private void installVplsIntents() {
+            Set<Intent> intentsToInstall = generateVplsIntents();
+            applyIntentsSync(intentsToInstall, Direction.ADD);
+        }
+
+        /**
+         * Helper class which monitors if all Intent operations are completed.
+         */
+        class IntentCompleter implements IntentListener {
+            private static final String INTENT_COMPILE_ERR = "Got {} from intent completer";
+            private CompletableFuture<Void> completableFuture;
+            private Set<Key> pendingIntentKeys;
+            private IntentEvent.Type expectedEventType;
+
+            /**
+             * Initialize completer with given Intent keys and expect Intent
+             * event type.
+             *
+             * @param pendingIntentKeys the Intent keys to wait
+             * @param expectedEventType expect Intent event type
+             */
+            public IntentCompleter(Set<Key> pendingIntentKeys, IntentEvent.Type expectedEventType) {
+                this.completableFuture = new CompletableFuture<>();
+                this.pendingIntentKeys = Sets.newConcurrentHashSet(pendingIntentKeys);
+                this.expectedEventType = expectedEventType;
+            }
+
+            @Override
+            public void event(IntentEvent event) {
+                Intent intent = event.subject();
+                // Intent failed, throw an exception to completable future
+                if (event.type() == IntentEvent.Type.CORRUPT ||
+                        event.type() == IntentEvent.Type.FAILED) {
+                    completableFuture.completeExceptionally(new IntentException(intent.toString()));
+                    return;
+                }
+                // If event type matched to expected type, remove from pending
+                if (event.type() == expectedEventType) {
+                    Key key = intent.key();
+                    pendingIntentKeys.remove(key);
+                }
+                if (pendingIntentKeys.isEmpty()) {
+                    completableFuture.complete(null);
+                }
+            }
+
+            /**
+             * Waits until all pending Intents completed ot timeout.
+             */
+            public void complete() {
+                // If no pending Intent keys, complete directly
+                if (pendingIntentKeys.isEmpty()) {
+                    return;
+                }
+                try {
+                    completableFuture.get(OPERATION_TIMEOUT, TimeUnit.SECONDS);
+                } catch (TimeoutException | InterruptedException |
+                         ExecutionException | IntentException e) {
+                    // TODO: handle errors more carefully
+                    log.warn(INTENT_COMPILE_ERR, e.toString());
+                    throw new VplsOperationException(vplsOperation, e.toString());
+                }
+            }
+        }
+    }
+
+    /**
+     * A listener for leadership events.
+     * Only the leader can process VPLS operation in the ONOS cluster.
+     */
+    private class InternalLeadershipListener implements LeadershipEventListener {
+        private static final String LEADER_CHANGE = "Change leader to {}";
+
+        @Override
+        public void event(LeadershipEvent event) {
+            switch (event.type()) {
+                case LEADER_CHANGED:
+                case LEADER_AND_CANDIDATES_CHANGED:
+                    isLeader = localNodeId.equals(event.subject().leaderNodeId());
+                    if (isLeader) {
+                        log.debug(LEADER_CHANGE, localNodeId);
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        @Override
+        public boolean isRelevant(LeadershipEvent event) {
+            return event.subject().topic().equals(appId.name());
+        }
+    }
+}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/api/Vpls.java b/apps/vpls/src/main/java/org/onosproject/vpls/api/Vpls.java
new file mode 100644
index 0000000..5dec2b2
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/api/Vpls.java
@@ -0,0 +1,104 @@
+/*
+ * 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.api;
+
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.net.EncapsulationType;
+
+import java.util.Collection;
+
+public interface Vpls {
+
+    /**
+     * Creates a new VPLS.
+     *
+     * @param vplsName the name of the VPLS
+     * @param encapsulationType the encapsulation type
+     * @return a VPLS instance if the operation is successful; null otherwise
+     */
+    VplsData createVpls(String vplsName, EncapsulationType encapsulationType);
+
+    /**
+     * Removes a VPLS.
+     *
+     * @param vplsData the VPLS to be removed
+     * @return the VPLS removed if the operation is successful; null otherwise
+     */
+    VplsData removeVpls(VplsData vplsData);
+
+    /**
+     * Adds network interfaces to a VPLS.
+     *
+     * @param vplsData the VPLS to which the interfaces have to be added to
+     * @param interfaces the interfaces to add
+     */
+    void addInterfaces(VplsData vplsData, Collection<Interface> interfaces);
+
+    /**
+     * Adds a network interface to a VPLS.
+     *
+     * @param vplsData the VPLS to which the interface has to be added to
+     * @param iface the interface to add
+     */
+    void addInterface(VplsData vplsData, Interface iface);
+
+    /**
+     * Sets an encapsulation type for a VPLS.
+     *
+     * @param vplsData the VPLS for which the encapsulation has to be set
+     * @param encapsulationType the encapsulation type
+     */
+    void setEncapsulationType(VplsData vplsData, EncapsulationType encapsulationType);
+
+    /**
+     * Retrieves a VPLS.
+     *
+     * @param vplsName the name of the VPLS
+     * @return the VPLS instance if the VPLS exists; null otherwise
+     */
+    VplsData getVpls(String vplsName);
+
+    /**
+     * Gets all VPLSs.
+     *
+     * @return a collection of VPLSs
+     */
+    Collection<VplsData> getAllVpls();
+
+    /**
+     * Removes the interfaces specified from a VPLS.
+     *
+     * @param vplsData the VPLS from which the interfaces are to be removed
+     * @param interfaces the interfaces to remove
+     * @return the interfaces removed
+     */
+    Collection<Interface> removeInterfaces(VplsData vplsData, Collection<Interface> interfaces);
+
+    /**
+     * Removes the interface specified from a VPLS.
+     *
+     * @param vplsData the VPLS from which the interface is to be removed
+     * @param iface the interface to remove
+     * @return the interface removed
+     */
+    Interface removeInterface(VplsData vplsData, Interface iface);
+
+    /**
+     * Removes all VPLSs and cleans up the VPLS configuration.
+     */
+    void removeAllVpls();
+}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsData.java b/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsData.java
new file mode 100644
index 0000000..bba9d78
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsData.java
@@ -0,0 +1,183 @@
+/*
+ * 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.api;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.net.EncapsulationType;
+
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Set;
+
+import static java.util.Objects.*;
+
+/**
+ * Class stores a VPLS information.
+ */
+public final class VplsData {
+    /**
+     * States of a VPLS.
+     */
+    public enum VplsState {
+        UPDATING,
+        ADDING,
+        REMOVING,
+        ADDED,
+        REMOVED,
+        FAILED
+    }
+
+    private String name;
+    private Set<Interface> interfaces;
+    private EncapsulationType encapsulationType;
+    private VplsState state;
+
+    /**
+     * Constructs a VPLS data by given name and encapsulation type.
+     *
+     * @param name the given name
+     * @param encapType the encapsulation type
+     */
+    private VplsData(String name, EncapsulationType encapType) {
+        this.name = name;
+        this.encapsulationType = encapType;
+        this.interfaces = Sets.newHashSet();
+        this.state = VplsState.ADDING;
+    }
+
+    /**
+     * Creates a VPLS data by given name.
+     * The encapsulation type of the VPLS will be NONE.
+     *
+     * @param name the given name
+     * @return the VPLS data
+     */
+    public static VplsData of(String name) {
+        requireNonNull(name);
+        return new VplsData(name, EncapsulationType.NONE);
+    }
+
+    /**
+     * Creates a VPLS data by given name and encapsulation type.
+     *
+     * @param name the given name
+     * @param encapType the encapsulation type
+     * @return the VPLS data
+     */
+    public static VplsData of(String name, EncapsulationType encapType) {
+        requireNonNull(name);
+        if (encapType == null) {
+            return new VplsData(name, EncapsulationType.NONE);
+        } else {
+            return new VplsData(name, encapType);
+        }
+    }
+
+    /**
+     * Creates a copy of VPLS data.
+     *
+     * @param vplsData the VPLS data
+     * @return the copy of the VPLS data
+     */
+    public static VplsData of(VplsData vplsData) {
+        requireNonNull(vplsData);
+        VplsData vplsDataCopy = new VplsData(vplsData.name(), vplsData.encapsulationType());
+        vplsDataCopy.addInterfaces(vplsData.interfaces());
+        return vplsData;
+    }
+
+    /**
+     * Gets name of the VPLS.
+     *
+     * @return the name of the VPLS
+     */
+    public String name() {
+        return name;
+    }
+
+    public Set<Interface> interfaces() {
+        return ImmutableSet.copyOf(interfaces);
+    }
+
+    public EncapsulationType encapsulationType() {
+        return encapsulationType;
+    }
+
+    public void addInterfaces(Collection<Interface> interfaces) {
+        requireNonNull(interfaces);
+        this.interfaces.addAll(interfaces);
+    }
+
+    public void addInterface(Interface iface) {
+        requireNonNull(iface);
+        this.interfaces.add(iface);
+    }
+
+    public void removeInterfaces(Collection<Interface> interfaces) {
+        requireNonNull(interfaces);
+        this.interfaces.removeAll(interfaces);
+    }
+
+    public void removeInterface(Interface iface) {
+        requireNonNull(iface);
+        this.interfaces.remove(iface);
+    }
+
+    public void encapsulationType(EncapsulationType encapType) {
+        this.encapsulationType = encapType;
+    }
+
+    public VplsState state() {
+        return state;
+    }
+
+    public void state(VplsState state) {
+        this.state = state;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("name", name)
+                .add("interfaces", interfaces)
+                .add("encap type", encapsulationType)
+                .add("state", state)
+                .toString();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof VplsData)) {
+            return false;
+        }
+        VplsData other = (VplsData) obj;
+        return Objects.equals(other.name, this.name) &&
+                Objects.equals(other.interfaces, this.interfaces) &&
+                Objects.equals(other.encapsulationType, this.encapsulationType);
+    }
+
+    @Override
+    public int hashCode() {
+        return hash(name, interfaces, encapsulationType);
+    }
+}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsOperation.java b/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsOperation.java
new file mode 100644
index 0000000..abc5025
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsOperation.java
@@ -0,0 +1,110 @@
+/*
+ * 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.api;
+
+import com.google.common.base.MoreObjects;
+
+import java.util.Objects;
+
+import static java.util.Objects.*;
+
+/**
+ * Operation for VPLS.
+ */
+public class VplsOperation {
+
+    /**
+     * The operation type.
+     */
+    public enum Operation {
+        REMOVE,
+        ADD,
+        UPDATE
+    }
+
+    private VplsData vplsData;
+    private Operation op;
+
+    /**
+     * Defines a VPLS operation by binding a given VPLS and operation type.
+     *
+     * @param vplsData the VPLS
+     * @param op the operation
+     */
+    protected VplsOperation(VplsData vplsData, Operation op) {
+        requireNonNull(vplsData);
+        requireNonNull(op);
+        // Make a copy of the VPLS data to ensure other thread won't change it.
+        this.vplsData = VplsData.of(vplsData);
+        this.op = op;
+    }
+
+    /**
+     * Defines a VPLS operation by binding a given VPLS and operation type.
+     *
+     * @param vplsData the VPLS
+     * @param op the operation
+     * @return the VPLS operation
+     */
+    public static VplsOperation of(VplsData vplsData, Operation op) {
+        return new VplsOperation(vplsData, op);
+    }
+
+    /**
+     * Retrieves the VPLS from the operation.
+     *
+     * @return the VPLS
+     */
+    public VplsData vpls() {
+        return vplsData;
+    }
+
+    /**
+     * Retrieves the operation type from the operation.
+     *
+     * @return the operation type
+     */
+    public Operation op() {
+        return op;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("vplsData", vplsData.toString())
+                .add("op", op.toString())
+                .toString();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof VplsOperation)) {
+            return false;
+        }
+        VplsOperation other = (VplsOperation) obj;
+        return Objects.equals(other.vplsData, this.vplsData) &&
+                Objects.equals(other.op, this.op);
+    }
+
+    @Override
+    public int hashCode() {
+        return hash(vplsData, op);
+    }
+}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsOperationException.java b/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsOperationException.java
new file mode 100644
index 0000000..95dfdfa
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsOperationException.java
@@ -0,0 +1,45 @@
+/*
+ * 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.api;
+
+/**
+ * Exception for VPLS operation.
+ */
+public class VplsOperationException extends RuntimeException {
+    private static final long serialVersionUID = 4514685940685335886L;
+    private VplsOperation vplsOperation;
+
+    /**
+     * Constructs a VPLS operation exception with given VPLS operation and
+     * message.
+     *
+     * @param operation the VPLS operation
+     * @param msg the description of the exception
+     */
+    public VplsOperationException(VplsOperation operation, String msg) {
+        super(msg);
+        this.vplsOperation = operation;
+    }
+
+    /**
+     * Gets VPLS operation for this exception.
+     * @return the VPLS operation
+     */
+    public VplsOperation vplsOperation() {
+        return vplsOperation;
+    }
+}
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/api/VplsOperationService.java
similarity index 62%
copy from apps/vpls/src/main/java/org/onosproject/vpls/config/impl/package-info.java
copy to apps/vpls/src/main/java/org/onosproject/vpls/api/VplsOperationService.java
index b5d6ec9..3044f7e 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/package-info.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsOperationService.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016-present Open Networking Laboratory
+ * 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.
@@ -14,7 +14,17 @@
  * limitations under the License.
  */
 
+package org.onosproject.vpls.api;
+
 /**
- * Configuration service implementation for VPLS.
+ * Service for application submitting VPLS operation.
  */
-package org.onosproject.vpls.config.impl;
+public interface VplsOperationService {
+
+    /**
+     * Submits a VPLS operation.
+     *
+     * @param vplsOperation the VPLS operation to submit
+     */
+    void submit(VplsOperation vplsOperation);
+}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsStore.java b/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsStore.java
new file mode 100644
index 0000000..caf7550
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsStore.java
@@ -0,0 +1,63 @@
+/*
+ * 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.api;
+
+import org.onosproject.store.Store;
+import org.onosproject.store.StoreDelegate;
+import org.onosproject.vpls.store.VplsStoreEvent;
+
+import java.util.Collection;
+
+/**
+ * Definition of the operations regarding the management of the VPLS elements.
+ */
+public interface VplsStore extends Store<VplsStoreEvent, StoreDelegate<VplsStoreEvent>> {
+    /**
+     * Adds a VPLS to the configuration.
+     *
+     * @param vplsData the VPLS to add
+     */
+    void addVpls(VplsData vplsData);
+
+    /**
+     * Removes a VPLS from the configuration.
+     *
+     * @param vplsData the VPLS to remove
+     */
+    void removeVpls(VplsData vplsData);
+
+    /**
+     * Updates a VPLS.
+     *
+     * @param vplsData the VPLS to update
+     */
+    void updateVpls(VplsData vplsData);
+
+    /**
+     * Gets a VPLS by name.
+     *
+     * @param vplsName the VPLS name
+     * @return the VPLS instance if it exists; null otherwise
+     */
+    VplsData getVpls(String vplsName);
+
+    /**
+     * Gets all the VPLSs.
+     *
+     * @return all the VPLSs
+     */
+    Collection<VplsData> getAllVpls();
+}
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/api/package-info.java
similarity index 79%
rename from apps/vpls/src/main/java/org/onosproject/vpls/config/impl/package-info.java
rename to apps/vpls/src/main/java/org/onosproject/vpls/api/package-info.java
index b5d6ec9..91d3937 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/package-info.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/api/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016-present Open Networking Laboratory
+ * 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.
@@ -15,6 +15,6 @@
  */
 
 /**
- * Configuration service implementation for VPLS.
+ * APIs for VPLS.
  */
-package org.onosproject.vpls.config.impl;
+package org.onosproject.vpls.api;
\ No newline at end of file
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/cli/VplsCommand.java b/apps/vpls/src/main/java/org/onosproject/vpls/cli/VplsCommand.java
index d4bfa8d..4718453 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/cli/VplsCommand.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/cli/VplsCommand.java
@@ -15,24 +15,24 @@
  */
 package org.onosproject.vpls.cli;
 
-import com.google.common.collect.Lists;
-import com.google.common.collect.SetMultimap;
-import com.google.common.collect.Sets;
 import org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
 import org.onosproject.cli.AbstractShellCommand;
 import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
 import org.onosproject.net.EncapsulationType;
-import org.onosproject.vpls.config.VplsConfigService;
+import org.onosproject.vpls.api.VplsData;
+import org.onosproject.vpls.api.Vpls;
 
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
-import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import static com.google.common.base.Strings.isNullOrEmpty;
 
+
 /**
  * CLI to interact with the VPLS application.
  */
@@ -85,7 +85,8 @@
                     " not found" + RESET;
 
     private static final String VPLS_DISPLAY = "VPLS name: " + BOLD +
-            "%s" + RESET + "\nAssociated interfaces: %s\nEncapsulation: %s";
+            "%s" + RESET + "\nAssociated interfaces: %s\nEncapsulation: %s\n" +
+            "State: %s";
 
     private static final String VPLS_NOT_FOUND =
             COLOR_ERROR + "VPLS " + BOLD + "%s" + RESET + COLOR_ERROR +
@@ -93,15 +94,13 @@
 
     private static final String IFACE_NOT_ASSOCIATED =
             COLOR_ERROR + "Interface " + BOLD + "%s" + RESET + COLOR_ERROR +
-                    " cannot be removed from VPLS " + BOLD + "%s" + RESET +
-                    COLOR_ERROR + ". The interface is associated to another" +
-                    "VPLS (" + BOLD + "%s" + RESET + COLOR_ERROR + ")" + RESET;
+                    " cannot be removed from VPLS " + BOLD + "%s" + RESET + ".";
 
-    private static VplsConfigService vplsConfigService =
-            get(VplsConfigService.class);
+    protected static Vpls vpls;
+    protected static InterfaceService interfaceService;
 
     @Argument(index = 0, name = "command", description = "Command name (add-if|" +
-            "clean|create|delete|list|rem-if|set-encap|show)",
+            "create|delete|list|rem-if|set-encap|show)",
             required = true, multiValued = false)
     String command = null;
 
@@ -116,15 +115,19 @@
 
     @Override
     protected void execute() {
+        if (vpls == null) {
+            vpls = get(Vpls.class);
+        }
+        if (interfaceService == null) {
+            interfaceService = get(InterfaceService.class);
+        }
+
         VplsCommandEnum enumCommand = VplsCommandEnum.enumFromString(command);
         if (enumCommand != null) {
             switch (enumCommand) {
                 case ADD_IFACE:
                     addIface(vplsName, optArg);
                     break;
-                case CLEAN:
-                    clean();
-                    break;
                 case CREATE:
                     create(vplsName);
                     break;
@@ -143,6 +146,9 @@
                 case SHOW:
                     show(vplsName);
                     break;
+                case CLEAN:
+                    cleanVpls();
+                    break;
                 default:
                     print(VPLS_COMMAND_NOT_FOUND, command);
             }
@@ -157,7 +163,7 @@
      * @param vplsName the name of the VLPS
      * @param ifaceName the name of the interface to add
      */
-    private void addIface(String vplsName, String ifaceName) {
+    protected void addIface(String vplsName, String ifaceName) {
         if (vplsName == null) {
             print(INSERT_VPLS_NAME);
             return;
@@ -166,28 +172,24 @@
             print(INSERT_INTERFACE);
             return;
         }
-        if (!vplsExists(vplsName)) {
+
+        Interface iface = getInterface(ifaceName);
+        VplsData vplsData = vpls.getVpls(vplsName);
+
+        if (vplsData == null) {
             print(VPLS_NOT_FOUND, vplsName);
             return;
         }
-        if (!ifaceExists(ifaceName)) {
+        if (iface == null) {
             print(IFACE_NOT_FOUND, ifaceName);
             return;
         }
-        if (isIfaceAssociated(ifaceName)) {
+        if (isIfaceAssociated(iface)) {
             print(IFACE_ALREADY_ASSOCIATED,
-                  ifaceName, vplsNameFromIfaceName(ifaceName));
+                  ifaceName, getVplsByInterface(iface).name());
             return;
         }
-
-        vplsConfigService.addIface(vplsName, ifaceName);
-    }
-
-    /**
-     * Cleans the VPLS configuration.
-     */
-    private void clean() {
-        vplsConfigService.cleanVplsConfig();
+        vpls.addInterface(vplsData, iface);
     }
 
     /**
@@ -195,16 +197,17 @@
      *
      * @param vplsName the name of the VLPS
      */
-    private void create(String vplsName) {
+    protected void create(String vplsName) {
         if (vplsName == null || vplsName.isEmpty()) {
             print(INSERT_VPLS_NAME);
             return;
         }
-        if (vplsExists(vplsName)) {
+        VplsData vplsData = vpls.getVpls(vplsName);
+        if (vplsData != null) {
             print(VPLS_ALREADY_EXISTS, vplsName);
             return;
         }
-        vplsConfigService.addVpls(vplsName, Sets.newHashSet(), null);
+        vpls.createVpls(vplsName, EncapsulationType.NONE);
     }
 
     /**
@@ -212,23 +215,26 @@
      *
      * @param vplsName the name of the VLPS
      */
-    private void delete(String vplsName) {
+    protected void delete(String vplsName) {
         if (vplsName == null) {
             print(INSERT_VPLS_NAME);
             return;
         }
-        if (!vplsExists(vplsName)) {
+        VplsData vplsData = vpls.getVpls(vplsName);
+        if (vplsData == null) {
             print(VPLS_NOT_FOUND, vplsName);
             return;
         }
-        vplsConfigService.removeVpls(vplsName);
+        vpls.removeVpls(vplsData);
     }
 
     /**
      * Lists the configured VPLSs.
      */
-    private void list() {
-        List<String> vplsNames = Lists.newArrayList(vplsConfigService.vplsNames());
+    protected void list() {
+        List<String> vplsNames = vpls.getAllVpls().stream()
+                .map(VplsData::name)
+                .collect(Collectors.toList());
         Collections.sort(vplsNames);
 
         vplsNames.forEach(vpls -> {
@@ -242,7 +248,7 @@
      * @param vplsName the name of the VLPS
      * @param ifaceName the name of the interface to remove
      */
-    private void removeIface(String vplsName, String ifaceName) {
+    protected void removeIface(String vplsName, String ifaceName) {
         if (vplsName == null) {
             print(INSERT_VPLS_NAME);
             return;
@@ -251,20 +257,21 @@
             print(INSERT_INTERFACE);
             return;
         }
-        if (!vplsExists(vplsName)) {
+        VplsData vplsData = vpls.getVpls(vplsName);
+        Interface iface = getInterface(ifaceName);
+        if (vplsData == null) {
             print(VPLS_NOT_FOUND, vplsName);
             return;
         }
-        if (!ifaceExists(ifaceName)) {
+        if (iface == null) {
             print(IFACE_NOT_FOUND, ifaceName);
             return;
         }
-        String vplsNameFromIfaceName = vplsNameFromIfaceName(ifaceName);
-        if (!vplsNameFromIfaceName.equals(vplsName)) {
-            print(IFACE_NOT_ASSOCIATED, ifaceName, vplsName, vplsNameFromIfaceName);
+        if (!vplsData.interfaces().contains(iface)) {
+            print(IFACE_NOT_ASSOCIATED, ifaceName, vplsName);
             return;
         }
-        vplsConfigService.removeIface(ifaceName);
+        vpls.removeInterface(vplsData, iface);
     }
 
     /**
@@ -273,7 +280,7 @@
      * @param vplsName the name of the VPLS
      * @param encap the encapsulation type
      */
-    private void setEncap(String vplsName, String encap) {
+    protected void setEncap(String vplsName, String encap) {
         if (vplsName == null) {
             print(INSERT_VPLS_NAME);
             return;
@@ -282,7 +289,8 @@
             print(INSERT_ENCAP_TYPE);
             return;
         }
-        if (!vplsExists(vplsName)) {
+        VplsData vplsData = vpls.getVpls(vplsName);
+        if (vplsData == null) {
             print(VPLS_NOT_FOUND, vplsName);
             return;
         }
@@ -292,7 +300,7 @@
             print(ENCAP_NOT_FOUND, encap);
             return;
         }
-        vplsConfigService.setEncap(vplsName, encap);
+        vpls.setEncapsulationType(vplsData, encapType);
     }
 
     /**
@@ -300,113 +308,88 @@
      *
      * @param vplsName the name of the VPLS
      */
-    private void show(String vplsName) {
-        List<String> vplsNames = Lists.newArrayList(vplsConfigService.vplsNames());
-        Collections.sort(vplsNames);
-
-        Map<String, EncapsulationType> encapByVplsName =
-                vplsConfigService.encapByVplsName();
-
+    protected void show(String vplsName) {
         if (!isNullOrEmpty(vplsName)) {
             // A VPLS name is provided. Check first if the VPLS exists
-            if (vplsExists(vplsName)) {
+            VplsData vplsData = vpls.getVpls(vplsName);
+            if (vplsData != null) {
+                Set<String> ifaceNames = vplsData.interfaces().stream()
+                        .map(Interface::name)
+                        .collect(Collectors.toSet());
                 print(VPLS_DISPLAY,
                       vplsName,
-                      ifacesFromVplsName(vplsName).toString(),
-                      encapByVplsName.get(vplsName).toString());
+                      ifaceNames,
+                      vplsData.encapsulationType().toString(),
+                      vplsData.state());
             } else {
                 print(VPLS_NOT_FOUND, vplsName);
             }
         } else {
+            Collection<VplsData> vplses = vpls.getAllVpls();
             // No VPLS names are provided. Display all VPLSs configured
             print(SEPARATOR);
-            vplsNames.forEach(v -> {
+            vplses.forEach(vplsData -> {
+                Set<String> ifaceNames = vplsData.interfaces().stream()
+                        .map(Interface::name)
+                        .collect(Collectors.toSet());
                 print(VPLS_DISPLAY,
-                      v,
-                      ifacesFromVplsName(v).toString(),
-                      encapByVplsName.get(v).toString());
+                      vplsData.name(),
+                      ifaceNames,
+                      vplsData.encapsulationType().toString(),
+                      vplsData.state());
                 print(SEPARATOR);
             });
         }
     }
 
     /**
-     * States if a VPLS exists or not.
-     *
-     * @param vplsName the name of the VPLS
-     * @return true if the VPLS exists; false otherwise
+     * Remove all VPLS.
      */
-    private static boolean vplsExists(String vplsName) {
-        return vplsConfigService.vplsNames().contains(vplsName);
+    protected void cleanVpls() {
+        vpls.removeAllVpls();
     }
 
-    /**
-     * States if an interface is defined or not in the system.
-     *
-     * @param ifaceName the name of the interface
-     * @return true if the interface is defined; false otherwise
-     */
-    private static boolean ifaceExists(String ifaceName) {
-        return vplsConfigService.allIfaces()
-                .stream()
-                .anyMatch(iface -> iface.name().equals(ifaceName));
-    }
 
     /**
      * States if an interface is already associated to a VPLS.
      *
-     * @param ifaceName the name of the interface
+     * @param iface the interface
      * @return true if the interface is already associated to a VPLS; false
      * otherwise
      */
-    private static boolean isIfaceAssociated(String ifaceName) {
-        return vplsConfigService.ifaces()
+    private static boolean isIfaceAssociated(Interface iface) {
+        return vpls.getAllVpls()
                 .stream()
-                .anyMatch(iface -> iface.name().equals(ifaceName));
+                .map(VplsData::interfaces)
+                .flatMap(Collection::stream)
+                .anyMatch(iface::equals);
     }
 
     /**
-     * Returns the name of a VPLS, given the name of an interface associated to
-     * it.
+     * Gets a network interface by given interface name.
      *
-     * @param ifaceName the name of the interface
-     * @return the name of the VPLS that has the interface configured; null if
-     * the interface does not exist or is not associated to any VPLS
+     * @param interfaceName the interface name
+     * @return the network interface
      */
-    private static String vplsNameFromIfaceName(String ifaceName) {
-        String vplsName = null;
-
-        Optional<String> optVplsName = vplsConfigService.ifacesByVplsName()
-                .entries()
-                .stream()
-                .filter((entry -> entry.getValue().name().equals(ifaceName)))
-                .map(Map.Entry::getKey)
-                .findFirst();
-
-        if (optVplsName.isPresent()) {
-            vplsName = optVplsName.get();
-        }
-
-        return vplsName;
+    private Interface getInterface(String interfaceName) {
+        // FIXME: only returns first interface it found
+        // multiple interface with same name not support
+        return interfaceService.getInterfaces().stream()
+                .filter(iface -> iface.name().equals(interfaceName))
+                .findFirst()
+                .orElse(null);
     }
 
     /**
-     * Returns a list of interfaces associated to a VPLS, given a VPLS name.
+     * Gets a VPLS related to the network interface.
      *
-     * @param vplsName the name of the VPLS
-     * @return the set of interfaces associated to the given VPLS; null if the
-     * VPLS is not found
+     * @param iface the network interface
+     * @return the VPLS related to the network interface
      */
-    private static Set<String> ifacesFromVplsName(String vplsName) {
-        if (!vplsExists(vplsName)) {
-            return null;
-        }
-        SetMultimap<String, Interface> ifacesByVplsName =
-                vplsConfigService.ifacesByVplsName();
-        Set<String> ifaceNames = Sets.newHashSet();
-
-        ifacesByVplsName.get(vplsName).forEach(iface -> ifaceNames.add(iface.name()));
-
-        return ifaceNames;
+    private VplsData getVplsByInterface(Interface iface) {
+        return vpls.getAllVpls().stream()
+                .filter(vplsData -> vplsData.interfaces().contains(iface))
+                .findFirst()
+                .orElse(null);
     }
 }
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/cli/VplsCommandEnum.java b/apps/vpls/src/main/java/org/onosproject/vpls/cli/VplsCommandEnum.java
index dfdaadf..c1ccb2b 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/cli/VplsCommandEnum.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/cli/VplsCommandEnum.java
@@ -25,13 +25,13 @@
  */
 public enum VplsCommandEnum {
     ADD_IFACE("add-if"),
-    CLEAN("clean"),
     CREATE("create"),
     DELETE("delete"),
     LIST("list"),
     REMOVE_IFACE("rem-if"),
     SET_ENCAP("set-encap"),
-    SHOW("show");
+    SHOW("show"),
+    CLEAN("clean");
 
     private final String command;
 
@@ -40,7 +40,7 @@
      *
      * @param command the text representing the command
      */
-    private VplsCommandEnum(final String command) {
+    VplsCommandEnum(final String command) {
         this.command = command;
     }
 
@@ -56,7 +56,7 @@
      */
     public static List<String> toStringList() {
         return Arrays.stream(values())
-                .map(c -> c.toString())
+                .map(VplsCommandEnum::toString)
                 .collect(Collectors.toCollection(ArrayList::new));
     }
 
@@ -71,7 +71,7 @@
     public static VplsCommandEnum enumFromString(String command) {
         if (command != null && !command.isEmpty()) {
             for (VplsCommandEnum c : values()) {
-                if (command.toString().equalsIgnoreCase(c.toString())) {
+                if (command.equalsIgnoreCase(c.toString())) {
                     return c;
                 }
             }
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsCommandCompleter.java b/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsCommandCompleter.java
index cfe08b7..2b198a9 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsCommandCompleter.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsCommandCompleter.java
@@ -16,6 +16,7 @@
 package org.onosproject.vpls.cli.completer;
 
 import com.google.common.collect.Lists;
+import org.apache.karaf.shell.console.completer.ArgumentCompleter;
 import org.onosproject.cli.AbstractChoicesCompleter;
 import org.onosproject.vpls.cli.VplsCommandEnum;
 
@@ -29,17 +30,19 @@
 
     @Override
     public List<String> choices() {
-        List<String> argumentList =
-                Lists.newArrayList(getArgumentList().getArguments());
+        ArgumentCompleter.ArgumentList argumentList = getArgumentList();
+        if (argumentList == null) {
+            return Collections.emptyList();
+        }
+        List<String> argList = Lists.newArrayList(argumentList.getArguments());
         String argOne = null;
-        if (argumentList.size() > 1) {
-            argOne = argumentList.get(1);
+        if (argList.size() > 1) {
+            argOne = argList.get(1);
         }
         VplsCommandEnum vplsCommandEnum = VplsCommandEnum.enumFromString(argOne);
         if (vplsCommandEnum != null) {
             switch (vplsCommandEnum) {
                 case CREATE:
-                case CLEAN:
                 case LIST:
                     return Collections.emptyList();
                 default:
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsNameCompleter.java b/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsNameCompleter.java
index ab2479b..6900880 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsNameCompleter.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsNameCompleter.java
@@ -15,11 +15,13 @@
  */
 package org.onosproject.vpls.cli.completer;
 
-import com.google.common.collect.Lists;
 import org.onosproject.cli.AbstractChoicesCompleter;
-import org.onosproject.vpls.config.VplsConfigService;
+import org.onosproject.vpls.api.Vpls;
+import org.onosproject.vpls.api.VplsData;
 
+import java.util.Collection;
 import java.util.List;
+import java.util.stream.Collectors;
 
 import static org.onosproject.cli.AbstractShellCommand.get;
 
@@ -28,9 +30,14 @@
  */
 public class VplsNameCompleter extends AbstractChoicesCompleter {
 
+    protected static Vpls vpls;
+
     @Override
     public List<String> choices() {
-        VplsConfigService vplsConfigService = get(VplsConfigService.class);
-        return Lists.newArrayList(vplsConfigService.vplsNames());
+        if (vpls == null) {
+            vpls = get(Vpls.class);
+        }
+        Collection<VplsData> vplses = vpls.getAllVpls();
+        return vplses.stream().map(VplsData::name).collect(Collectors.toList());
     }
 }
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsOptArgCompleter.java b/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsOptArgCompleter.java
index 5b60352..7058cdc 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsOptArgCompleter.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsOptArgCompleter.java
@@ -16,15 +16,17 @@
 package org.onosproject.vpls.cli.completer;
 
 import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
 import org.apache.karaf.shell.console.completer.ArgumentCompleter;
 import org.onosproject.cli.AbstractChoicesCompleter;
 import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
 import org.onosproject.net.EncapsulationType;
+import org.onosproject.vpls.api.Vpls;
+import org.onosproject.vpls.api.VplsData;
 import org.onosproject.vpls.cli.VplsCommandEnum;
-import org.onosproject.vpls.config.VplsConfigService;
 
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
@@ -36,22 +38,29 @@
  * VPLS optional argument completer.
  */
 public class VplsOptArgCompleter extends AbstractChoicesCompleter {
+    protected static Vpls vpls;
+    protected static InterfaceService interfaceService;
 
     @Override
     public List<String> choices() {
-        VplsConfigService vplsConfigService = get(VplsConfigService.class);
-        List<String> argumentList =
-                Lists.newArrayList(getArgumentList().getArguments());
-        String argOne = argumentList.get(1);
+        if (vpls == null) {
+            vpls = get(Vpls.class);
+        }
+        ArgumentCompleter.ArgumentList argumentList = getArgumentList();
+        if (argumentList == null) {
+            return Collections.emptyList();
+        }
+        List<String> argList = Lists.newArrayList(argumentList.getArguments());
+        String argOne = argList.get(1);
         VplsCommandEnum vplsCommandEnum = VplsCommandEnum.enumFromString(argOne);
         if (vplsCommandEnum != null) {
             switch (vplsCommandEnum) {
                 case ADD_IFACE:
-                    return availableIfaces(vplsConfigService);
+                    return availableIfaces();
                 case SET_ENCAP:
                     return encap();
                 case REMOVE_IFACE:
-                    return vplsIfaces(vplsConfigService);
+                    return vplsIfaces();
                 default:
                     return Collections.emptyList();
             }
@@ -64,21 +73,27 @@
      *
      * @return the list of interfaces not yet assigned to any VPLS
      */
-    private List<String> availableIfaces(VplsConfigService vplsConfigService) {
-        List<String> ifacesAvailable = Lists.newArrayList();
-        Set<Interface> allIfaces = Sets.newHashSet(vplsConfigService.allIfaces());
-        Set<Interface> usedIfaces = Sets.newHashSet(vplsConfigService.ifaces());
+    private List<String> availableIfaces() {
+        if (interfaceService == null) {
+            interfaceService = get(InterfaceService.class);
+        }
 
-        allIfaces.removeAll(usedIfaces);
-        allIfaces.forEach(iface -> ifacesAvailable.add(iface.name()));
+        Set<Interface> allIfaces = interfaceService.getInterfaces();
+        Set<Interface> usedIfaces = vpls.getAllVpls().stream()
+                .map(VplsData::interfaces)
+                .flatMap(Collection::stream)
+                .collect(Collectors.toSet());
 
-        return ifacesAvailable;
+        return allIfaces.stream()
+                .filter(iface -> !usedIfaces.contains(iface))
+                .map(Interface::name)
+                .collect(Collectors.toList());
     }
 
     /**
      * Returns the list of supported encapsulation types.
      *
-     * @return the list of supported encapsualtion types
+     * @return the list of supported encapsulation types
      */
     private List<String> encap() {
         return Arrays.stream(EncapsulationType.values())
@@ -91,15 +106,12 @@
      *
      * @return the list of interfaces associated to a VPLS
      */
-    private List<String> vplsIfaces(VplsConfigService vplsConfigService) {
+    private List<String> vplsIfaces() {
         ArgumentCompleter.ArgumentList list = getArgumentList();
         String vplsName = list.getArguments()[2];
-
-        List<String> vplsIfaces = Lists.newArrayList();
-
-        Set<Interface> connectPoints = vplsConfigService.ifaces(vplsName);
-        connectPoints.forEach(iface -> vplsIfaces.add(iface.name()));
-
-        return vplsIfaces;
+        VplsData vplsData = vpls.getVpls(vplsName);
+        return vplsData.interfaces().stream()
+                .map(Interface::name)
+                .collect(Collectors.toList());
     }
 }
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsAppConfig.java b/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsAppConfig.java
index 66f7882..775d56b 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsAppConfig.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsAppConfig.java
@@ -35,6 +35,9 @@
     private static final String INTERFACE = "interfaces";
     private static final String ENCAPSULATION = "encapsulation";
 
+    // Record update time when VPLS distribute store update it
+    private static final String UPDATE_TIME = "lastUpdateTime";
+
     /**
      * Returns a set of configured VPLSs.
      *
@@ -42,9 +45,7 @@
      */
     public Set<VplsConfig> vplss() {
         Set<VplsConfig> vplss = Sets.newHashSet();
-
         JsonNode vplsNode = object.get(VPLS);
-
         if (vplsNode == null) {
             return vplss;
         }
@@ -67,7 +68,6 @@
                                      ifaces,
                                      EncapsulationType.enumFromString(encap)));
         });
-
         return vplss;
     }
 
@@ -215,4 +215,37 @@
     private ArrayNode initVplsConfiguration() {
         return object.putArray(VPLS);
     }
+
+    /**
+     * Sets the last time of update for the VPLS information in the store.
+     *
+     * @param timestamp the update time
+     */
+    public void updateTime(long timestamp) {
+        object.put(UPDATE_TIME, timestamp);
+    }
+
+    /**
+     * Retrieves the last time of update for the VPLS information in the store.
+     *
+     * @return update time, -1 if there is no update time in the config
+     */
+    public long updateTime() {
+        if (object.get(UPDATE_TIME) != null) {
+            return object.get(UPDATE_TIME).asLong();
+        } else {
+            // No update time
+            return -1L;
+        }
+    }
+
+    /**
+     * Clears all VPLS configurations.
+     */
+    public void clearVplsConfig() {
+        if (object.get(VPLS) != null) {
+            object.remove(VPLS);
+        }
+        initVplsConfiguration();
+    }
 }
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsConfigManager.java b/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsConfigManager.java
new file mode 100644
index 0000000..71b14ad
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsConfigManager.java
@@ -0,0 +1,260 @@
+/*
+ * 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.config;
+
+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;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
+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.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.basics.SubjectFactories;
+import org.onosproject.vpls.VplsManager;
+import org.onosproject.vpls.api.VplsData;
+import org.onosproject.vpls.api.Vpls;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import static org.onlab.util.Tools.groupedThreads;
+
+/**
+ * Component for the management of the VPLS configuration.
+ */
+@Component(immediate = true)
+public class VplsConfigManager {
+    private static final Class<VplsAppConfig> CONFIG_CLASS = VplsAppConfig.class;
+    private static final String NET_CONF_EVENT = "Received NetworkConfigEvent {}";
+    private static final String CONFIG_NULL = "VPLS configuration not defined";
+    private static final int INITIAL_RELOAD_CONFIG_DELAY = 0;
+    private static final int INITIAL_RELOAD_CONFIG_PERIOD = 1000;
+    private static final int NUM_THREADS = 1;
+    private static final String VPLS = "vpls";
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private final NetworkConfigListener configListener =
+            new InternalNetworkConfigListener();
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigService configService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected InterfaceService interfaceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigRegistry registry;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected Vpls vpls;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected LeadershipService leadershipService;
+
+    private ScheduledExecutorService reloadExecutor =
+            Executors.newScheduledThreadPool(NUM_THREADS,
+                                             groupedThreads("onos/apps/vpls",
+                                                            "config-reloader-%d",
+                                                            log)
+            );
+
+    private ConfigFactory<ApplicationId, VplsAppConfig> vplsConfigFactory =
+            new ConfigFactory<ApplicationId, VplsAppConfig>(
+                    SubjectFactories.APP_SUBJECT_FACTORY, VplsAppConfig.class, VPLS) {
+                @Override
+                public VplsAppConfig createConfig() {
+                    return new VplsAppConfig();
+                }
+            };
+
+    protected ApplicationId appId;
+
+    @Activate
+    void activate() {
+        appId = coreService.registerApplication(VplsManager.VPLS_APP);
+        configService.addListener(configListener);
+
+        // Load config when VPLS service started and there is a leader for VPLS;
+        // otherwise, try again after a period.
+        reloadExecutor.scheduleAtFixedRate(() -> {
+            NodeId vplsLeaderNode = leadershipService.getLeader(appId.name());
+            if (vpls == null || vplsLeaderNode == null) {
+                return;
+            }
+            reloadConfiguration();
+            reloadExecutor.shutdown();
+        }, INITIAL_RELOAD_CONFIG_DELAY, INITIAL_RELOAD_CONFIG_PERIOD, TimeUnit.MILLISECONDS);
+        registry.registerConfigFactory(vplsConfigFactory);
+    }
+
+    @Deactivate
+    void deactivate() {
+        configService.removeListener(configListener);
+        registry.unregisterConfigFactory(vplsConfigFactory);
+    }
+
+    /**
+     * Retrieves the VPLS configuration from network configuration.
+     * Checks difference between new configuration and old configuration.
+     */
+    private synchronized void reloadConfiguration() {
+        VplsAppConfig vplsAppConfig = configService.getConfig(appId, VplsAppConfig.class);
+
+        if (vplsAppConfig == null) {
+            log.warn(CONFIG_NULL);
+
+            // If the config is null, removes all VPLS.
+            vpls.removeAllVpls();
+            return;
+        }
+
+        // If there exists a update time in the configuration; it means the
+        // configuration was pushed by VPLS store; ignore this configuration.
+        long updateTime = vplsAppConfig.updateTime();
+        if (updateTime != -1L) {
+            return;
+        }
+
+        Collection<VplsData> oldVplses = vpls.getAllVpls();
+        Collection<VplsData> newVplses;
+
+        // Generates collection of new VPLSs
+        newVplses = vplsAppConfig.vplss().stream()
+                .map(vplsConfig -> {
+                    Set<Interface> interfaces = vplsConfig.ifaces().stream()
+                            .map(this::getInterfaceByName)
+                            .filter(Objects::nonNull)
+                            .collect(Collectors.toSet());
+                    VplsData vplsData = VplsData.of(vplsConfig.name(), vplsConfig.encap());
+                    vplsData.addInterfaces(interfaces);
+                    return vplsData;
+                }).collect(Collectors.toSet());
+
+        if (newVplses.containsAll(oldVplses) && oldVplses.containsAll(newVplses)) {
+            // no update, ignore
+            return;
+        }
+
+        // To add or update
+        newVplses.forEach(newVplsData -> {
+            boolean vplsExists = false;
+            for (VplsData oldVplsData : oldVplses) {
+                if (oldVplsData.name().equals(newVplsData.name())) {
+                    vplsExists = true;
+
+                    // VPLS exists; but need to be updated.
+                    if (!oldVplsData.equals(newVplsData)) {
+                        // Update VPLS
+                        Set<Interface> newInterfaces = newVplsData.interfaces();
+                        Set<Interface> oldInterfaces = oldVplsData.interfaces();
+
+                        Set<Interface> ifaceToAdd = newInterfaces.stream()
+                                .filter(iface -> !oldInterfaces.contains(iface))
+                                .collect(Collectors.toSet());
+
+                        Set<Interface> ifaceToRem = oldInterfaces.stream()
+                                .filter(iface -> !newInterfaces.contains(iface))
+                                .collect(Collectors.toSet());
+
+                        vpls.addInterfaces(oldVplsData, ifaceToAdd);
+                        vpls.removeInterfaces(oldVplsData, ifaceToRem);
+                        vpls.setEncapsulationType(oldVplsData, newVplsData.encapsulationType());
+                    }
+                }
+            }
+            // VPLS not exist; add new VPLS
+            if (!vplsExists) {
+                vpls.createVpls(newVplsData.name(), newVplsData.encapsulationType());
+                vpls.addInterfaces(newVplsData, newVplsData.interfaces());
+            }
+        });
+
+        // VPLS not exists in old VPLS configuration; remove it.
+        Set<VplsData> vplsToRemove = Sets.newHashSet();
+        oldVplses.forEach(oldVpls -> {
+            Set<String> newVplsNames = newVplses.stream()
+                    .map(VplsData::name)
+                    .collect(Collectors.toSet());
+
+            if (!newVplsNames.contains(oldVpls.name())) {
+                // To avoid ConcurrentModificationException; do remove after this
+                // iteration.
+                vplsToRemove.add(oldVpls);
+            }
+        });
+        vplsToRemove.forEach(vpls::removeVpls);
+    }
+
+    /**
+     * Gets network interface by a given name.
+     *
+     * @param interfaceName the interface name
+     * @return the network interface if there exist with the given name; null
+     * otherwise
+     */
+    private Interface getInterfaceByName(String interfaceName) {
+        return interfaceService.getInterfaces().stream()
+                .filter(iface -> iface.name().equals(interfaceName))
+                .findFirst()
+                .orElse(null);
+    }
+
+    /**
+     * Listener for VPLS configuration events.
+     * Reloads VPLS configuration when configuration added or updated.
+     * Removes all VPLS when configuration removed or unregistered.
+     */
+    private class InternalNetworkConfigListener implements NetworkConfigListener {
+        @Override
+        public void event(NetworkConfigEvent event) {
+            if (event.configClass() == CONFIG_CLASS) {
+                log.debug(NET_CONF_EVENT, event.configClass());
+                switch (event.type()) {
+                    case CONFIG_ADDED:
+                    case CONFIG_UPDATED:
+                        reloadConfiguration();
+                        break;
+                    case CONFIG_REMOVED:
+                    case CONFIG_UNREGISTERED:
+                        vpls.removeAllVpls();
+                    default:
+                        break;
+                }
+            }
+        }
+    }
+}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsConfigService.java b/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsConfigService.java
deleted file mode 100644
index 2d2a576..0000000
--- a/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsConfigService.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * 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 org.onosproject.net.EncapsulationType;
-
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Provides information about the VPLS configuration.
- */
-public interface VplsConfigService {
-    Class<VplsAppConfig> CONFIG_CLASS = VplsAppConfig.class;
-
-    /**
-     * Adds a VPLS to the configuration.
-     *
-     * @param vplsName the name of the VPLS
-     * @param ifaces the interfaces associated with the VPLS
-     * @param encap the encapsulation type
-     */
-    void addVpls(String vplsName, Set<String> ifaces, String encap);
-
-    /**
-     * Removes a VPLS from the configuration.
-     *
-     * @param vplsName the name of the VPLS to be removed
-     */
-    void removeVpls(String vplsName);
-
-    /**
-     * Adds a network interface to a VPLS.
-     *
-     * @param vplsName the name of the VPLS
-     * @param iface the network interface to be added to the VPLS
-     */
-    void addIface(String vplsName, String iface);
-
-    /**
-     * Sets an encapsulation parameter for a VPLS.
-     *
-     * @param vplsName the name of the VPLS
-     * @param encap the encapsulation used (i.e. MPLS or VLAN) or
-     */
-    void setEncap(String vplsName, String encap);
-
-    /**
-     * Returns the encapsulation type in use for a given VPLS.
-     *
-     * @param vplsName the name of the VPLS
-     * @return the encapsulation type in use, if any
-     */
-    EncapsulationType encap(String vplsName);
-
-    /**
-     * Removes a network interface from a VPLS.
-     *
-     * @param iface the network interface to be removed from the VPLS
-     */
-    void removeIface(String iface);
-
-    /**
-     * Cleans up the VPLS configuration. Removes all VPLSs.
-     */
-    void cleanVplsConfig();
-
-    /**
-     * Retrieves the VPLS names modified from CLI.
-     *
-     * @return the VPLS names modified from CLI
-     */
-    Set<String> vplsAffectedByApi();
-    // TODO Remove this function after the intent framework race condition has been fixed
-
-    /**
-     * Retrieves the interfaces without IP configured, so compatible with VPLS,
-     * from the interface service.
-     *
-     * @return the set of interfaces configured, compatible with VPLS
-     */
-    Set<Interface> allIfaces();
-
-    /**
-     * Retrieves the interfaces associated to a VPLS.
-     *
-     * @return the set of interfaces associated to a VPLS
-     */
-    Set<Interface> ifaces();
-
-    /**
-     * Retrieves the interfaces associated to the VPLS specified.
-     *
-     * @param vplsName the name of the VPLS
-     * @return the set of interfaces associated to the VPLS specified
-     */
-    Set<Interface> ifaces(String vplsName);
-
-    /**
-     * Retrieves all VPLS names.
-     *
-     * @return a set of VPLS names
-     */
-    Set<String> vplsNames();
-
-    /**
-     * Retrieves all VPLS names from the old config.
-     *
-     * @return a set of VPLS names
-     */
-    Set<String> vplsNamesOld();
-    // TODO Removes this function after the race condition gets fixed in IF
-
-    /**
-     * Returns the VPLS names and associated interfaces from the configuration.
-     *
-     * @return a map of VPLS names and associated interfaces
-     */
-    SetMultimap<String, Interface> ifacesByVplsName();
-
-    /**
-     * Returns the list of interfaces grouped by VPLS name, given a VLAN Id and
-     * a connect point.
-     *
-     * @param vlan the VLAN Id
-     * @param connectPoint the connect point
-     * @return a map of VPLS names and associated interfaces; null otherwise
-     */
-    SetMultimap<String, Interface> ifacesByVplsName(VlanId vlan,
-                                                    ConnectPoint connectPoint);
-
-    /**
-     * Returns the VPLS names and associated encapsulation type.
-     *
-     * @return a map of VPLS names and associated encapsulation type
-     */
-    Map<String, EncapsulationType> encapByVplsName();
-}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/VplsConfigImpl.java b/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/VplsConfigImpl.java
deleted file mode 100644
index 36da63c..0000000
--- a/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/VplsConfigImpl.java
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * 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.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSetMultimap;
-import com.google.common.collect.Maps;
-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.EncapsulationType;
-import org.onosproject.net.config.ConfigFactory;
-import org.onosproject.net.config.NetworkConfigEvent;
-import org.onosproject.net.config.NetworkConfigListener;
-import org.onosproject.net.config.NetworkConfigRegistry;
-import org.onosproject.net.config.NetworkConfigService;
-import org.onosproject.net.config.basics.SubjectFactories;
-import org.onosproject.vpls.config.VplsAppConfig;
-import org.onosproject.vpls.config.VplsConfig;
-import org.onosproject.vpls.config.VplsConfigService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Implementation of VPLSConfigurationService which reads VPLS configuration
- * from the network configuration service.
- */
-@Component(immediate = true)
-@Service
-public class VplsConfigImpl implements VplsConfigService {
-    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 VplsAppConfig vplsAppConfig = null;
-
-    private SetMultimap<String, String> ifacesOfVpls = HashMultimap.create();
-    private SetMultimap<String, String> oldIfacesOfVpls = HashMultimap.create();
-    private SetMultimap<String, Interface> vplsIfaces = HashMultimap.create();
-
-    private Map<String, EncapsulationType> vplsEncaps = Maps.newHashMap();
-
-    private final InternalNetworkConfigListener configListener =
-            new InternalNetworkConfigListener();
-
-    private ConfigFactory<ApplicationId, VplsAppConfig> vplsConfigFactory =
-            new ConfigFactory<ApplicationId, VplsAppConfig>(
-                    SubjectFactories.APP_SUBJECT_FACTORY, VplsAppConfig.class, VPLS) {
-                @Override
-                public VplsAppConfig createConfig() {
-                    return new VplsAppConfig();
-                }
-            };
-
-    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");
-    }
-
-    @Override
-    public void addVpls(String vplsName, Set<String> ifaces, String encap) {
-        EncapsulationType encapType = EncapsulationType.enumFromString(encap);
-
-        if (ifacesOfVpls.containsKey(vplsName)) {
-            if (ifaces.isEmpty()) {
-                return;
-            }
-            ifaces.forEach(iface -> vplsAppConfig.addIface(vplsName, iface));
-            vplsAppConfig.setEncap(vplsName, encapType);
-        } else {
-            vplsAppConfig.addVpls(new VplsConfig(vplsName, ifaces, encapType));
-        }
-
-        vplsAffectedByApi.add(vplsName);
-        applyConfig(vplsAppConfig);
-    }
-
-    @Override
-    public void removeVpls(String vplsName) {
-        if (ifacesOfVpls.containsKey(vplsName)) {
-            vplsAppConfig.removeVpls(vplsName);
-            vplsAffectedByApi.add(vplsName);
-            applyConfig(vplsAppConfig);
-        }
-    }
-
-    @Override
-    public void addIface(String vplsName, String iface) {
-        if (ifacesOfVpls.containsKey(vplsName)) {
-            vplsAppConfig.addIface(vplsName, iface);
-            vplsAffectedByApi.add(vplsName);
-            applyConfig(vplsAppConfig);
-        }
-    }
-
-    @Override
-    public void setEncap(String vplsName, String encap) {
-        EncapsulationType encapType = EncapsulationType.enumFromString(encap);
-
-        if (ifacesOfVpls.containsKey(vplsName)) {
-            vplsAppConfig.setEncap(vplsName, encapType);
-            vplsAffectedByApi.add(vplsName);
-            applyConfig(vplsAppConfig);
-        }
-    }
-
-    @Override
-    public void removeIface(String iface) {
-        if (ifacesOfVpls.containsValue(iface)) {
-            VplsConfig vpls = vplsAppConfig.vplsFromIface(iface);
-            vplsAppConfig.removeIface(vpls, iface);
-            vplsAffectedByApi.add(vpls.name());
-            applyConfig(vplsAppConfig);
-        }
-    }
-
-    @Override
-    public void cleanVplsConfig() {
-        ifacesOfVpls.entries().forEach(e -> {
-            vplsAppConfig.removeVpls(e.getKey());
-            vplsAffectedByApi.add(e.getKey());
-        });
-        applyConfig(vplsAppConfig);
-    }
-
-    @Override
-    public EncapsulationType encap(String vplsName) {
-        EncapsulationType encap = null;
-        if (vplsEncaps.containsKey(vplsName)) {
-            encap = vplsEncaps.get(vplsName);
-        }
-
-        return encap;
-    }
-
-    @Override
-    public Set<String> vplsAffectedByApi() {
-        Set<String> vplsNames = ImmutableSet.copyOf(vplsAffectedByApi);
-        vplsAffectedByApi.clear();
-        return vplsNames;
-    }
-
-    @Override
-    public Set<Interface> allIfaces() {
-        Set<Interface> interfaces = new HashSet<>();
-        interfaceService.getInterfaces().stream()
-                .filter(iface -> iface.ipAddressesList() == null ||
-                        iface.ipAddressesList().isEmpty())
-                .forEach(interfaces::add);
-        return interfaces;
-    }
-
-    @Override
-    public Set<Interface> ifaces() {
-        Set<Interface> interfaces = new HashSet<>();
-        vplsIfaces.values().forEach(interfaces::add);
-        return interfaces;
-    }
-
-    @Override
-    public Set<Interface> ifaces(String vplsName) {
-        Set<Interface> vplsInterfaces = new HashSet<>();
-        vplsIfaces.get(vplsName).forEach(vplsInterfaces::add);
-        return vplsInterfaces;
-    }
-
-    @Override
-    public Set<String> vplsNames() {
-        return ifacesOfVpls.keySet();
-    }
-
-    @Override
-    public Set<String> vplsNamesOld() {
-        return oldIfacesOfVpls.keySet();
-    }
-
-    @Override
-    public SetMultimap<String, Interface> ifacesByVplsName() {
-        return ImmutableSetMultimap.copyOf(vplsIfaces);
-    }
-
-    @Override
-    public SetMultimap<String, Interface> ifacesByVplsName(VlanId vlan,
-                                                           ConnectPoint connectPoint) {
-        String vplsName =
-                vplsIfaces.entries().stream()
-                        .filter(e -> e.getValue().connectPoint().equals(connectPoint))
-                        .filter(e -> e.getValue().vlan().equals(vlan))
-                        .map(Map.Entry::getKey)
-                        .findFirst()
-                        .orElse(null);
-        SetMultimap<String, Interface> result = HashMultimap.create();
-        if (vplsName != null && vplsIfaces.containsKey(vplsName)) {
-            vplsIfaces.get(vplsName)
-                    .forEach(intf -> result.put(vplsName, intf));
-            return result;
-        }
-        return null;
-    }
-
-    @Override
-    public Map<String, EncapsulationType> encapByVplsName() {
-        return ImmutableMap.copyOf(vplsEncaps);
-    }
-
-    /**
-     * Retrieves the VPLS configuration from network configuration.
-     */
-    private void loadConfiguration() {
-        loadAppId();
-
-        vplsAppConfig = configService.getConfig(vplsAppId, VplsAppConfig.class);
-
-        if (vplsAppConfig == null) {
-            log.warn(CONFIG_NULL);
-            vplsAppConfig = configService.addConfig(vplsAppId, VplsAppConfig.class);
-        }
-
-        oldIfacesOfVpls = ifacesOfVpls;
-        ifacesOfVpls = getConfigInterfaces();
-        vplsIfaces = getConfigCPointsFromIfaces();
-        vplsEncaps = getConfigEncap();
-
-        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(VplsAppConfig vplsAppConfig) {
-        loadAppId();
-        configService.applyConfig(vplsAppId, VplsAppConfig.class, vplsAppConfig.node());
-    }
-
-    /**
-     * Retrieves the VPLS names and related encapsulation types from the
-     * configuration.
-     *
-     * @return a map of VPLS names and associated encapsulation types
-     */
-    private Map<String, EncapsulationType> getConfigEncap() {
-        Map<String, EncapsulationType> configEncap = new HashMap<>();
-
-        vplsAppConfig.vplss().forEach(vpls -> {
-            configEncap.put(vpls.name(), vpls.encap());
-        });
-
-        return configEncap;
-    }
-
-    /**
-     * Retrieves the VPLS names and related interfaces names from the configuration.
-     *
-     * @return a map of VPLS names and related interface names
-     */
-    private SetMultimap<String, String> getConfigInterfaces() {
-        SetMultimap<String, String> confIntfByVpls =
-                HashMultimap.create();
-
-        vplsAppConfig.vplss().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 related interfaces from the configuration.
-     *
-     * @return a map of VPLS names and related interfaces
-     */
-    private SetMultimap<String, Interface> getConfigCPointsFromIfaces() {
-        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() == VplsConfigService.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;
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
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/config/impl/package-info.java b/apps/vpls/src/main/java/org/onosproject/vpls/intent/package-info.java
similarity index 79%
copy from apps/vpls/src/main/java/org/onosproject/vpls/config/impl/package-info.java
copy to apps/vpls/src/main/java/org/onosproject/vpls/intent/package-info.java
index b5d6ec9..197ea02 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/package-info.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/intent/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016-present Open Networking Laboratory
+ * 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.
@@ -15,6 +15,6 @@
  */
 
 /**
- * Configuration service implementation for VPLS.
+ * Utility for generate Intent for VPLS.
  */
-package org.onosproject.vpls.config.impl;
+package org.onosproject.vpls.intent;
\ No newline at end of file
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/store/DistributedVplsStore.java b/apps/vpls/src/main/java/org/onosproject/vpls/store/DistributedVplsStore.java
new file mode 100644
index 0000000..1743491
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/store/DistributedVplsStore.java
@@ -0,0 +1,223 @@
+/*
+ * 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.store;
+
+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.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.store.AbstractStore;
+import org.onosproject.store.StoreDelegate;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.EventuallyConsistentMap;
+import org.onosproject.store.service.EventuallyConsistentMapEvent;
+import org.onosproject.store.service.EventuallyConsistentMapListener;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.WallClockTimestamp;
+import org.onosproject.vpls.VplsManager;
+import org.onosproject.vpls.api.VplsData;
+import org.onosproject.vpls.api.VplsOperation;
+import org.onosproject.vpls.config.VplsAppConfig;
+import org.onosproject.vpls.api.VplsStore;
+import org.onosproject.vpls.config.VplsConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.*;
+
+/**
+ * Implementation of VPLSConfigurationService which reads VPLS configuration
+ * from the network configuration service.
+ */
+@Component(immediate = true)
+@Service
+public class DistributedVplsStore
+        extends AbstractStore<VplsStoreEvent, StoreDelegate<VplsStoreEvent>>
+        implements VplsStore {
+
+    private static final KryoNamespace APP_KRYO = KryoNamespace.newBuilder()
+            .register(KryoNamespaces.API)
+            .register(VplsData.class)
+            .register(VplsOperation.class)
+            .build();
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigService networkConfigService;
+
+    private EventuallyConsistentMap<String, VplsData> vplsDataStore;
+    private EventuallyConsistentMapListener<String, VplsData> vplsDataListener;
+    private ApplicationId appId;
+
+    @Activate
+    protected void active() {
+        appId = coreService.registerApplication(VplsManager.VPLS_APP);
+
+        vplsDataStore = storageService.<String, VplsData>eventuallyConsistentMapBuilder()
+                .withName("VPLS-Data")
+                .withTimestampProvider((name, vpls) -> new WallClockTimestamp())
+                .withSerializer(APP_KRYO)
+                .build();
+        vplsDataListener = new InternalVplsDataListener();
+        vplsDataStore.addListener(vplsDataListener);
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected  void deactive() {
+        vplsDataStore.removeListener(vplsDataListener);
+        log.info("Stopped");
+    }
+
+    @Override
+    public void addVpls(VplsData vplsData) {
+        requireNonNull(vplsData);
+        if (vplsData.name().isEmpty()) {
+            throw new IllegalArgumentException("VPLS name is empty.");
+        }
+        vplsData.state(VplsData.VplsState.ADDING);
+        this.vplsDataStore.put(vplsData.name(), vplsData);
+    }
+
+    @Override
+    public void removeVpls(VplsData vplsData) {
+        requireNonNull(vplsData);
+        if (vplsData.name().isEmpty()) {
+            throw new IllegalArgumentException("VPLS name is empty.");
+        }
+        vplsData.state(VplsData.VplsState.REMOVING);
+        if (!this.vplsDataStore.containsKey(vplsData.name())) {
+            // notify the delegate asynchronously if VPLS does not exists
+            CompletableFuture.runAsync(() -> {
+                VplsStoreEvent event = new VplsStoreEvent(VplsStoreEvent.Type.REMOVE,
+                                                          vplsData);
+                notifyDelegate(event);
+            });
+            return;
+        }
+        this.vplsDataStore.remove(vplsData.name());
+    }
+
+    @Override
+    public void updateVpls(VplsData vplsData) {
+        switch (vplsData.state()) {
+            case ADDED:
+            case REMOVED:
+            case FAILED:
+                // state update only
+                this.vplsDataStore.put(vplsData.name(), vplsData);
+                break;
+            default:
+                vplsData.state(VplsData.VplsState.UPDATING);
+                this.vplsDataStore.put(vplsData.name(), vplsData);
+                break;
+        }
+    }
+
+    @Override
+    public VplsData getVpls(String vplsName) {
+        return vplsDataStore.get(vplsName);
+    }
+
+    @Override
+    public Collection<VplsData> getAllVpls() {
+        return vplsDataStore.values();
+    }
+
+    /**
+     * Writes all VPLS data to the network configuration store.
+     *
+     * @param vplsDataCollection the VPLSs data
+     */
+    public void writeVplsToNetConfig(Collection<VplsData> vplsDataCollection) {
+        VplsAppConfig config = networkConfigService.addConfig(appId, VplsAppConfig.class);
+        config.clearVplsConfig();
+
+        // Setup update time for this VPLS application configuration
+        WallClockTimestamp ts = new WallClockTimestamp();
+        config.updateTime(ts.unixTimestamp());
+
+        vplsDataCollection.forEach(vplsData -> {
+            Set<String> interfaceNames = vplsData.interfaces()
+                    .stream()
+                    .map(Interface::name)
+                    .collect(Collectors.toSet());
+            VplsConfig vplsConfig = new VplsConfig(vplsData.name(), interfaceNames,
+                                                   vplsData.encapsulationType());
+            config.addVpls(vplsConfig);
+        });
+
+        networkConfigService.applyConfig(appId, VplsAppConfig.class, config.node());
+    }
+
+    /**
+     * Listener for VPLS data store.
+     */
+    private class InternalVplsDataListener implements EventuallyConsistentMapListener<String, VplsData> {
+        private static final String STATE_UPDATE = "VPLS state updated, new VPLS: {}";
+
+        @Override
+        public void event(EventuallyConsistentMapEvent<String, VplsData> event) {
+            VplsData vplsData = event.value();
+            // Update network config
+            writeVplsToNetConfig(getAllVpls());
+            switch (event.type()) {
+                case PUT:
+                    // Add or Update
+                    if (vplsData.state() == VplsData.VplsState.ADDING) {
+                        VplsStoreEvent vplsStoreEvent =
+                                new VplsStoreEvent(VplsStoreEvent.Type.ADD, vplsData);
+                        notifyDelegate(vplsStoreEvent);
+                    } else if (vplsData.state() == VplsData.VplsState.UPDATING) {
+                        VplsStoreEvent vplsStoreEvent =
+                                new VplsStoreEvent(VplsStoreEvent.Type.UPDATE, vplsData);
+                        notifyDelegate(vplsStoreEvent);
+                    } else {
+                        // Do nothing here, just update state from operation service
+                        log.debug(STATE_UPDATE, vplsData);
+                    }
+                    break;
+                case REMOVE:
+                    VplsStoreEvent vplsStoreEvent =
+                            new VplsStoreEvent(VplsStoreEvent.Type.REMOVE, vplsData);
+                    notifyDelegate(vplsStoreEvent);
+                    break;
+                default:
+                    break;
+            }
+
+        }
+    }
+}
\ No newline at end of file
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/store/VplsStoreEvent.java b/apps/vpls/src/main/java/org/onosproject/vpls/store/VplsStoreEvent.java
new file mode 100644
index 0000000..210470c
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/store/VplsStoreEvent.java
@@ -0,0 +1,46 @@
+/*
+ * 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.store;
+
+import org.onosproject.event.AbstractEvent;
+import org.onosproject.vpls.api.VplsData;
+
+/**
+ * A class to represent a VPLS store related event.
+ */
+public class VplsStoreEvent extends AbstractEvent<VplsStoreEvent.Type, VplsData> {
+
+    /**
+     * VPLS store event type.
+     */
+    public enum Type {
+        ADD,
+        REMOVE,
+        UPDATE
+    }
+
+    /**
+     * Constructs a store event with given event type and VPLS information.
+     *
+     * @param type the event type
+     * @param subject the VPLS
+     */
+    public VplsStoreEvent(Type type, VplsData subject) {
+        super(type, subject);
+    }
+
+}
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/store/package-info.java
similarity index 78%
copy from apps/vpls/src/main/java/org/onosproject/vpls/config/impl/package-info.java
copy to apps/vpls/src/main/java/org/onosproject/vpls/store/package-info.java
index b5d6ec9..0f1a807 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/package-info.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/store/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016-present Open Networking Laboratory
+ * 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.
@@ -14,7 +14,4 @@
  * limitations under the License.
  */
 
-/**
- * Configuration service implementation for VPLS.
- */
-package org.onosproject.vpls.config.impl;
+package org.onosproject.vpls.store;
\ No newline at end of file
diff --git a/apps/vpls/src/test/java/org/onosproject/vpls/VplsConfigServiceAdapter.java b/apps/vpls/src/test/java/org/onosproject/vpls/VplsConfigServiceAdapter.java
deleted file mode 100644
index e5844fb..0000000
--- a/apps/vpls/src/test/java/org/onosproject/vpls/VplsConfigServiceAdapter.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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;
-
-import com.google.common.collect.SetMultimap;
-import org.onlab.packet.VlanId;
-import org.onosproject.incubator.net.intf.Interface;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.EncapsulationType;
-import org.onosproject.vpls.config.VplsConfigService;
-
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Test adapter for VPLS configuration service.
- */
-public class VplsConfigServiceAdapter implements VplsConfigService {
-    @Override
-    public void addVpls(String vplsName, Set<String> ifaces, String encap) {}
-
-    @Override
-    public void removeVpls(String vplsName) {}
-
-    @Override
-    public void addIface(String vplsName, String iface) {}
-
-    @Override
-    public void setEncap(String vplsName, String encap) {}
-
-    @Override
-    public EncapsulationType encap(String vplsName) {
-        return null;
-    }
-
-    @Override
-    public void removeIface(String iface) {}
-
-    @Override
-    public void cleanVplsConfig() {}
-
-    @Override
-    public Set<String> vplsAffectedByApi() {
-        return null;
-    }
-
-    @Override
-    public Set<Interface> allIfaces() {
-        return null;
-    }
-
-    @Override
-    public Set<Interface> ifaces() {
-        return null;
-    }
-
-    @Override
-    public Set<Interface> ifaces(String vplsName) {
-        return null;
-    }
-
-    @Override
-    public Set<String> vplsNames() {
-        return null;
-    }
-
-    @Override
-    public Set<String> vplsNamesOld() {
-        return null;
-    }
-
-    @Override
-    public SetMultimap<String, Interface> ifacesByVplsName() {
-        return null;
-    }
-
-    @Override
-    public SetMultimap<String, Interface> ifacesByVplsName(VlanId vlan, ConnectPoint connectPoint) {
-        return null;
-    }
-
-    @Override
-    public Map<String, EncapsulationType> encapByVplsName() {
-        return null;
-    }
-}
diff --git a/apps/vpls/src/test/java/org/onosproject/vpls/VplsIntentTest.java b/apps/vpls/src/test/java/org/onosproject/vpls/VplsIntentTest.java
new file mode 100644
index 0000000..7ee9292
--- /dev/null
+++ b/apps/vpls/src/test/java/org/onosproject/vpls/VplsIntentTest.java
@@ -0,0 +1,537 @@
+/*
+ * 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;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceListener;
+import org.onosproject.incubator.net.intf.InterfaceService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.EncapsulationType;
+import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.Host;
+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.intent.Intent;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.IntentServiceAdapter;
+import org.onosproject.net.intent.IntentUtils;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.net.intent.SinglePointToMultiPointIntent;
+import org.onosproject.vpls.api.VplsData;
+import org.onosproject.vpls.intent.VplsIntentUtility;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static java.lang.String.format;
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.onosproject.net.EncapsulationType.NONE;
+import static org.onosproject.net.EncapsulationType.VLAN;
+
+/**
+ * Tests for {@link VplsIntentUtility}.
+ */
+public class VplsIntentTest extends VplsTest {
+
+    private Set<Host> hostsAvailable;
+    private IntentService intentService;
+    private InterfaceService interfaceService;
+
+    @Before
+    public void setUp() throws Exception {
+        idGenerator = new TestIdGenerator();
+        Intent.unbindIdGenerator(idGenerator);
+        Intent.bindIdGenerator(idGenerator);
+        hostsAvailable = Sets.newHashSet();
+        intentService = new TestIntentService();
+        interfaceService = createMock(InterfaceService.class);
+        interfaceService.addListener(anyObject(InterfaceListener.class));
+        expectLastCall().anyTimes();
+        addIfaceConfig();
+    }
+
+    @After
+    public void tearDown() {
+        Intent.unbindIdGenerator(idGenerator);
+    }
+
+    /**
+     * Creates the interface configuration:
+     *  On devices 1 and 2 is configured an interface on port 1 with vlan 100.
+     *  On device 3 is configured an interface on port 3 with no vlan.
+     *  On devices 3 and 4 is configured an interface on port 1 with vlan 200.
+     *  On device 4 is an interface configured on port 2 with vlan 400.
+     *  On device 5 are configured two interfaces on port 1 and 2 with no vlan.
+     *  On device 5 and 6 is configured an interface on port 1 with vlan 300.
+     */
+    private void addIfaceConfig() {
+        Set<Interface> interfaces = ImmutableSet.copyOf(AVAILABLE_INTERFACES);
+        Set<Interface> vlanOneSet = ImmutableSet.of(V100H1, V100H2);
+        Set<Interface> vlanTwoSet = ImmutableSet.of(V200H1, V200H2);
+        Set<Interface> vlanThreeSet = ImmutableSet.of(VNONEH1, VNONEH2);
+        Set<Interface> vlanFourSet = ImmutableSet.of(V400H1, VNONEH3);
+
+        AVAILABLE_INTERFACES.forEach(intf -> {
+            expect(interfaceService.getInterfacesByPort(intf.connectPoint()))
+                    .andReturn(Sets.newHashSet(intf)).anyTimes();
+        });
+        expect(interfaceService.getInterfacesByVlan(VLAN100))
+                .andReturn(vlanOneSet).anyTimes();
+        expect(interfaceService.getInterfacesByVlan(VLAN200))
+                .andReturn(vlanTwoSet).anyTimes();
+        expect(interfaceService.getInterfacesByVlan(VLAN300))
+                .andReturn(vlanThreeSet).anyTimes();
+        expect(interfaceService.getInterfacesByVlan(VLAN400))
+                .andReturn(vlanFourSet).anyTimes();
+        expect(interfaceService.getInterfacesByVlan(VlanId.NONE))
+                .andReturn(vlanFourSet).anyTimes();
+        expect(interfaceService.getInterfaces()).andReturn(interfaces).anyTimes();
+
+        replay(interfaceService);
+    }
+
+    /**
+     * Seven ports are configured with VLANs, while three ports are not. No hosts are
+     * registered by the HostService.
+     *
+     * The first three ports have an interface configured on VPLS 1,
+     * the other three on VPLS 2. Two ports are defined for VPLS 3, while
+     * the two remaining ports are configured on VPLS 4.
+     *
+     * The number of intents expected is 10: three for VPLS 1, three for VPLS 2,
+     * two for VPLS 3, two for VPLS 4. Eight MP2SP intents.
+     * Checks if the number of intents submitted to the intent framework is
+     * equal to the number of intents expected and if all intents are equivalent.
+     */
+    @Test
+    public void activateNoHosts() {
+        List<Intent> expectedIntents = Lists.newArrayList();
+        Set<FilteredConnectPoint> fcPoints;
+        Set<Interface> interfaces;
+
+        interfaces = ImmutableSet.of(V100H1, V200H1, V300H1);
+        VplsData vplsData = createVplsData(VPLS1, VLAN, interfaces);
+        Set<Intent> brcIntents = VplsIntentUtility.buildBrcIntents(vplsData, APPID);
+        brcIntents.forEach(intentService::submit);
+        fcPoints = buildFCPoints(interfaces);
+        expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS1, VLAN));
+
+        checkIntents(expectedIntents);
+
+        interfaces = ImmutableSet.of(V100H2, V200H2, V300H2);
+        vplsData = createVplsData(VPLS2, NONE, interfaces);
+        brcIntents = VplsIntentUtility.buildBrcIntents(vplsData, APPID);
+        brcIntents.forEach(intentService::submit);
+        fcPoints = buildFCPoints(interfaces);
+        expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS2, NONE));
+
+        checkIntents(expectedIntents);
+
+        interfaces = ImmutableSet.of(VNONEH1, VNONEH2);
+        vplsData = createVplsData(VPLS3, NONE, interfaces);
+        brcIntents = VplsIntentUtility.buildBrcIntents(vplsData, APPID);
+        brcIntents.forEach(intentService::submit);
+        fcPoints = buildFCPoints(interfaces);
+        expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS3, NONE));
+
+        checkIntents(expectedIntents);
+
+        interfaces = ImmutableSet.of(V400H1, VNONEH3);
+        vplsData = createVplsData(VPLS4, NONE, interfaces);
+        brcIntents = VplsIntentUtility.buildBrcIntents(vplsData, APPID);
+        brcIntents.forEach(intentService::submit);
+        fcPoints = buildFCPoints(interfaces);
+        expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS4, NONE));
+
+        checkIntents(expectedIntents);
+    }
+
+
+
+    /**
+     * Ten ports are configured with VLANs and ten hosts are registered by the
+     * HostService.
+     *
+     * The first three ports have an interface configured on VPLS 1,
+     * the other three on VPLS 2, two on VPLS3 and two on VPLS4.
+     *
+     * The number of intents expected is twenty: six
+     * for VPLS 1, six for VPLS 2. four for VPLS 3, four for VPLS 4.
+     * That is ten sp2mp intents, ten mp2sp intents. For VPLS 1
+     * IPs are added to demonstrate this doesn't influence the number of intents
+     * created. Checks if the number of intents submitted to the intent
+     * framework is equal to the number of intents expected and if all intents
+     * are equivalent.
+     */
+    @Test
+    public void tenInterfacesConfiguredHostsPresent() {
+        hostsAvailable.addAll(AVAILABLE_HOSTS);
+
+        List<Intent> expectedIntents = Lists.newArrayList();
+        Set<FilteredConnectPoint> fcPoints;
+        Set<Host> hosts;
+        Set<Interface> interfaces;
+        VplsData vplsData;
+        Set<Intent> brcIntents;
+        Set<Intent> uniIntents;
+
+        interfaces = ImmutableSet.of(V100H1, V200H1, V300H1);
+        fcPoints = buildFCPoints(interfaces);
+        hosts = ImmutableSet.of(V100HOST1, V200HOST1, V300HOST1);
+        vplsData = createVplsData(VPLS1, VLAN, interfaces);
+        brcIntents = VplsIntentUtility.buildBrcIntents(vplsData, APPID);
+        uniIntents = VplsIntentUtility.buildUniIntents(vplsData, hosts, APPID);
+        brcIntents.forEach(intentService::submit);
+        uniIntents.forEach(intentService::submit);
+        expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS1, VLAN));
+        expectedIntents.addAll(generateVplsUni(fcPoints, hosts, VPLS1, VLAN));
+
+        interfaces = ImmutableSet.of(V100H2, V200H2, V300H2);
+        fcPoints = buildFCPoints(interfaces);
+        hosts = ImmutableSet.of(V100HOST2, V200HOST2, V300HOST2);
+        vplsData = createVplsData(VPLS2, NONE, interfaces);
+        brcIntents = VplsIntentUtility.buildBrcIntents(vplsData, APPID);
+        uniIntents = VplsIntentUtility.buildUniIntents(vplsData, hosts, APPID);
+        brcIntents.forEach(intentService::submit);
+        uniIntents.forEach(intentService::submit);
+        expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS2, NONE));
+        expectedIntents.addAll(generateVplsUni(fcPoints, hosts, VPLS2, NONE));
+
+        interfaces = ImmutableSet.of(VNONEH1, VNONEH2);
+        fcPoints = buildFCPoints(interfaces);
+        hosts = ImmutableSet.of(VNONEHOST1, VNONEHOST2);
+        vplsData = createVplsData(VPLS3, NONE, interfaces);
+        brcIntents = VplsIntentUtility.buildBrcIntents(vplsData, APPID);
+        uniIntents = VplsIntentUtility.buildUniIntents(vplsData, hosts, APPID);
+        brcIntents.forEach(intentService::submit);
+        uniIntents.forEach(intentService::submit);
+        expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS3, NONE));
+        expectedIntents.addAll(generateVplsUni(fcPoints, hosts, VPLS3, NONE));
+
+        interfaces = ImmutableSet.of(V400H1, VNONEH3);
+        fcPoints = buildFCPoints(interfaces);
+        hosts = ImmutableSet.of(V400HOST1, VNONEHOST3);
+        vplsData = createVplsData(VPLS4, NONE, interfaces);
+        brcIntents = VplsIntentUtility.buildBrcIntents(vplsData, APPID);
+        uniIntents = VplsIntentUtility.buildUniIntents(vplsData, hosts, APPID);
+        brcIntents.forEach(intentService::submit);
+        uniIntents.forEach(intentService::submit);
+        expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS4, NONE));
+        expectedIntents.addAll(generateVplsUni(fcPoints, hosts, VPLS4, NONE));
+
+        checkIntents(expectedIntents);
+    }
+
+    /**
+     * Generates a list of the expected sp2mp intents for a VPLS.
+     *
+     * @param fcPoints the filtered connect point
+     * @param name the name of the VPLS
+     * @param encap the encapsulation type
+     * @return the list of expected sp2mp intents for the given VPLS
+     */
+    private List<SinglePointToMultiPointIntent>
+    generateVplsBrc(Set<FilteredConnectPoint> fcPoints, String name, EncapsulationType encap) {
+        List<SinglePointToMultiPointIntent> intents = Lists.newArrayList();
+
+        fcPoints.forEach(point -> {
+            Set<FilteredConnectPoint> otherPoints =
+                    fcPoints.stream()
+                            .filter(fcp -> !fcp.equals(point))
+                            .collect(Collectors.toSet());
+
+            Key brckey = buildKey(VplsIntentUtility.PREFIX_BROADCAST,
+                                  point.connectPoint(),
+                                  name,
+                                  MacAddress.BROADCAST);
+
+            intents.add(buildBrcIntent(brckey, point, otherPoints, encap));
+        });
+
+        return intents;
+    }
+
+    /**
+     * Generates a list of expected mp2sp intents for a given VPLS.
+     *
+     * @param fcPoints the filtered connect point
+     * @param hosts the hosts
+     * @param name the name of the VPLS
+     * @param encap the encapsulation type
+     * @return the list of expected mp2sp intents for the given VPLS
+     */
+    private List<MultiPointToSinglePointIntent>
+    generateVplsUni(Set<FilteredConnectPoint> fcPoints, Set<Host> hosts,
+                    String name, EncapsulationType encap) {
+        List<MultiPointToSinglePointIntent> intents = Lists.newArrayList();
+
+        hosts.forEach(host -> {
+            FilteredConnectPoint hostPoint = getHostPoint(host, fcPoints);
+
+            Set<FilteredConnectPoint> otherPoints =
+                    fcPoints.stream()
+                            .filter(fcp -> !fcp.equals(hostPoint))
+                            .collect(Collectors.toSet());
+
+            Key uniKey = buildKey(VplsIntentUtility.PREFIX_UNICAST,
+                                  host.location(), name, host.mac());
+
+            intents.add(buildUniIntent(uniKey, otherPoints, hostPoint, host, encap));
+        });
+
+        return intents;
+    }
+
+    /**
+     * Checks if the number of intents submitted to the intent framework is equal
+     * to the number of intents expected and if all intents are equivalent.
+     *
+     * @param intents the list of intents expected
+     */
+    private void checkIntents(List<Intent> intents) {
+        assertEquals("The number of intents submitted differs from the number" +
+                             " of intents expected. ",
+                     intents.size(), intentService.getIntentCount());
+        for (Intent intentOne : intents) {
+            boolean found = false;
+            for (Intent intentTwo : intentService.getIntents()) {
+                if (intentOne.key().equals(intentTwo.key())) {
+                    found = true;
+                    assertTrue(format("The intent submitted is different from" +
+                                              " the intent expected. %s %s",
+                                      intentOne, intentTwo),
+                               IntentUtils.intentsAreEqual(intentOne, intentTwo));
+                    break;
+                }
+            }
+            assertTrue("The intent submitted is not equal to any of the expected" +
+                               " intents. ", found);
+        }
+    }
+
+    /**
+     * Builds a broadcast intent.
+     *
+     * @param key the key to identify the intent
+     * @param src the ingress connect point
+     * @param dsts the egress connect points
+     * @return the generated single-point to multi-point intent
+     */
+    private SinglePointToMultiPointIntent buildBrcIntent(Key key,
+                                                         FilteredConnectPoint src,
+                                                         Set<FilteredConnectPoint> dsts,
+                                                         EncapsulationType encap) {
+        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(VplsIntentUtility.PARTIAL_FAILURE_CONSTRAINT)
+                .priority(PRIORITY_OFFSET);
+        VplsIntentUtility.setEncap(intentBuilder,
+                                   VplsIntentUtility.PARTIAL_FAILURE_CONSTRAINT,
+                                   encap);
+
+        return intentBuilder.build();
+    }
+
+    /**
+     * Builds a unicast intent.
+     *
+     * @param key the key to identify the intent
+     * @param srcs the ingress connect points
+     * @param dst the egress connect point
+     * @param host the destination Host
+     * @return the generated multi-point to single-point intent
+     */
+    private MultiPointToSinglePointIntent buildUniIntent(Key key,
+                                                         Set<FilteredConnectPoint> srcs,
+                                                         FilteredConnectPoint dst,
+                                                         Host host,
+                                                         EncapsulationType encap) {
+        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(VplsIntentUtility.PARTIAL_FAILURE_CONSTRAINT)
+                .priority(PRIORITY_OFFSET);
+        VplsIntentUtility.setEncap(intentBuilder,
+                                   VplsIntentUtility.PARTIAL_FAILURE_CONSTRAINT,
+                                   encap);
+
+        return intentBuilder.build();
+    }
+
+    /**
+     * Returns the filtered connect point associated to a given host.
+     *
+     * @param host the target host
+     * @param fcps the filtered connected points
+     * @return the filtered connect point associated to the given host; null
+     * 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 VPLS
+        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());
+    }
+
+    /**
+     * Builds an intent Key either for a single-point to multi-point or
+     * multi-point to single-point intent, based on a prefix that defines
+     * the intent type, the connection point representing the source or the
+     * destination and the VLAN Id representing the VPLS.
+     *
+     * @param prefix the key prefix
+     * @param cPoint the ingress/egress connect point
+     * @param vplsName the VPLS name
+     * @param hostMac the ingress/egress MAC address
+     * @return the key to identify the intent
+     */
+    private Key buildKey(String prefix,
+                         ConnectPoint cPoint,
+                         String vplsName,
+                         MacAddress hostMac) {
+        String keyString = vplsName +
+                DASH +
+                prefix +
+                DASH +
+                cPoint.deviceId() +
+                DASH +
+                cPoint.port() +
+                DASH +
+                hostMac;
+
+        return Key.of(keyString, APPID);
+    }
+
+    /**
+     * Creates VPLS data by given name, encapsulation type and network
+     * interfaces.
+     *
+     * @param name the VPLS name
+     * @param encap the encapsulation type
+     * @param interfaces the network interfaces
+     * @return the VPLS data
+     */
+    private VplsData createVplsData(String name, EncapsulationType encap,
+                                    Set<Interface> interfaces) {
+        VplsData vplsData = VplsData.of(name, encap);
+        vplsData.addInterfaces(interfaces);
+        return vplsData;
+    }
+
+    /**
+     * Represents a fake IntentService class that allows to store and retrieve
+     * intents without implementing the IntentService logic.
+     */
+    private class TestIntentService extends IntentServiceAdapter {
+        private Map<Key, Intent> intents;
+
+        public TestIntentService() {
+            intents = Maps.newHashMap();
+        }
+
+        @Override
+        public void submit(Intent intent) {
+            intents.put(intent.key(), intent);
+        }
+
+        @Override
+        public long getIntentCount() {
+            return intents.size();
+        }
+
+        @Override
+        public Iterable<Intent> getIntents() {
+            return intents.values();
+        }
+
+        @Override
+        public Intent getIntent(Key intentKey) {
+            for (Intent intent : intents.values()) {
+                if (intent.key().equals(intentKey)) {
+                    return intent;
+                }
+            }
+            return null;
+        }
+    }
+
+}
diff --git a/apps/vpls/src/test/java/org/onosproject/vpls/VplsManagerTest.java b/apps/vpls/src/test/java/org/onosproject/vpls/VplsManagerTest.java
new file mode 100644
index 0000000..67128db
--- /dev/null
+++ b/apps/vpls/src/test/java/org/onosproject/vpls/VplsManagerTest.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;
+
+import com.google.common.collect.ImmutableSet;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.net.EncapsulationType;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.vpls.api.VplsData;
+import org.onosproject.vpls.api.VplsOperation;
+import org.onosproject.vpls.api.VplsOperationService;
+import org.onosproject.vpls.store.VplsStoreEvent;
+
+import java.util.Collection;
+
+import static org.junit.Assert.*;
+import static org.onosproject.net.EncapsulationType.*;
+import static org.onosproject.vpls.api.VplsData.VplsState.*;
+
+/**
+ * Test for {@link VplsManager}
+ */
+public class VplsManagerTest extends VplsTest {
+    private VplsManager vplsManager;
+    private TestVplsStore vplsStore = new TestVplsStore();
+    private TestHostService hostService = new TestHostService();
+    private TestInterfaceService interfaceService = new TestInterfaceService();
+    private TestVplsOperationService vplsOperationService = new TestVplsOperationService();
+
+    @Before
+    public void setup() {
+        vplsManager = new VplsManager();
+        vplsManager.hostService = hostService;
+        vplsManager.vplsStore = vplsStore;
+        vplsManager.operationService = vplsOperationService;
+        vplsManager.interfaceService = interfaceService;
+        vplsStore.clear();
+        vplsOperationService.clear();
+        vplsManager.activate();
+    }
+
+    @After
+    public void tearDown() {
+        vplsManager.deactivate();
+    }
+
+    /**
+     * Creates VPLS by given name and encapsulation type.
+     */
+    @Test
+    public void testCreateVpls() {
+        VplsData vplsData = vplsManager.createVpls(VPLS1, NONE);
+        assertEquals(VPLS1, vplsData.name());
+        assertEquals(NONE, vplsData.encapsulationType());
+
+        vplsData = vplsStore.getVpls(VPLS1);
+        assertEquals(vplsData.state(), ADDING);
+    }
+
+    /**
+     * Gets VPLS by VPLS name.
+     */
+    @Test
+    public void testGetVpls() {
+        VplsData vplsData = VplsData.of(VPLS1);
+        vplsData.state(ADDED);
+        vplsStore.addVpls(vplsData);
+
+        VplsData result = vplsManager.getVpls(VPLS1);
+        assertEquals(vplsData, result);
+
+        result = vplsManager.getVpls(VPLS2);
+        assertNull(result);
+    }
+
+    /**
+     * Removes a VPLS.
+     */
+    @Test
+    public void testRemoveVpls() {
+        VplsData vplsData = vplsManager.createVpls(VPLS1, NONE);
+        vplsManager.removeVpls(vplsData);
+        assertEquals(vplsData.state(), REMOVING);
+        vplsData = vplsStore.getVpls(VPLS1);
+        assertNull(vplsData);
+        Collection<VplsData> allVpls = vplsStore.getAllVpls();
+        assertEquals(0, allVpls.size());
+    }
+
+    /**
+     * Removes all VPLS.
+     */
+    @Test
+    public void testRemoveAllVpls() {
+        VplsData vplsData = VplsData.of(VPLS1);
+        vplsData.state(ADDED);
+        vplsStore.addVpls(vplsData);
+
+        vplsData = VplsData.of(VPLS2, VLAN);
+        vplsData.state(ADDED);
+        vplsStore.addVpls(vplsData);
+
+        vplsManager.removeAllVpls();
+        assertEquals(0, vplsStore.getAllVpls().size());
+    }
+
+    /**
+     * Adds network interfaces one by one to a VPLS.
+     */
+    @Test
+    public void testAddInterface() {
+        VplsData vplsData = vplsManager.createVpls(VPLS1, NONE);
+        vplsManager.addInterface(vplsData, V100H1);
+        vplsManager.addInterface(vplsData, V100H2);
+        vplsData = vplsStore.getVpls(VPLS1);
+        assertNotNull(vplsData);
+        assertEquals(vplsData.state(), UPDATING);
+        assertEquals(2, vplsData.interfaces().size());
+        assertTrue(vplsData.interfaces().contains(V100H1));
+        assertTrue(vplsData.interfaces().contains(V100H2));
+    }
+
+    /**
+     * Adds network interfaces to a VPLS.
+     */
+    @Test
+    public void testAddInterfaces() {
+        VplsData vplsData = vplsManager.createVpls(VPLS1, NONE);
+        vplsManager.addInterfaces(vplsData, ImmutableSet.of(V100H1, V100H2));
+        vplsData = vplsStore.getVpls(VPLS1);
+        assertNotNull(vplsData);
+        assertEquals(vplsData.state(), UPDATING);
+        assertEquals(2, vplsData.interfaces().size());
+        assertTrue(vplsData.interfaces().contains(V100H1));
+        assertTrue(vplsData.interfaces().contains(V100H2));
+    }
+
+    /**
+     * Removes network interfaces one by one from a VPLS.
+     */
+    @Test
+    public void testRemoveInterface() {
+        VplsData vplsData = vplsManager.createVpls(VPLS1, NONE);
+        vplsManager.addInterface(vplsData, V100H1);
+        vplsManager.addInterface(vplsData, V100H2);
+        vplsManager.removeInterface(vplsData, V100H1);
+        vplsData = vplsStore.getVpls(VPLS1);
+        assertNotNull(vplsData);
+        assertEquals(vplsData.state(), UPDATING);
+        assertEquals(1, vplsData.interfaces().size());
+        assertTrue(vplsData.interfaces().contains(V100H2));
+    }
+
+    /**
+     * Removes network interfaces from a VPLS.
+     */
+    @Test
+    public void testRemoveInterfaces() {
+        VplsData vplsData = vplsManager.createVpls(VPLS1, NONE);
+        vplsManager.addInterface(vplsData, V100H1);
+        vplsManager.addInterface(vplsData, V100H2);
+        vplsManager.removeInterfaces(vplsData, ImmutableSet.of(V100H1, V100H2));
+        vplsData = vplsStore.getVpls(VPLS1);
+        assertNotNull(vplsData);
+        assertEquals(vplsData.state(), UPDATING);
+        assertEquals(0, vplsData.interfaces().size());
+    }
+
+    /**
+     * Sets encapsulation type for a VPLS.
+     */
+    @Test
+    public void testSetEncapsulationType() {
+        VplsData vplsData = vplsManager.createVpls(VPLS1, NONE);
+        vplsManager.setEncapsulationType(vplsData, EncapsulationType.VLAN);
+        vplsData = vplsStore.getVpls(VPLS1);
+        assertNotNull(vplsData);
+        assertEquals(vplsData.state(), UPDATING);
+        assertEquals(vplsData.encapsulationType(), EncapsulationType.VLAN);
+    }
+
+    /**
+     * Adds hosts to a VPLS.
+     */
+    @Test
+    public void testAddHost() {
+        VplsData vplsData  = VplsData.of(VPLS1, NONE);
+        vplsData.addInterface(V100H1);
+        vplsData.state(ADDED);
+        vplsStore.addVpls(vplsData);
+
+        HostEvent hostEvent = new HostEvent(HostEvent.Type.HOST_ADDED, V100HOST1);
+        hostService.postHostEvent(hostEvent);
+
+        vplsData = vplsStore.getVpls(VPLS1);
+        assertNotNull(vplsData);
+
+        assertEquals(vplsData.state(), UPDATING);
+    }
+
+    /**
+     * Removes hosts from a VPLS.
+     */
+    @Test
+    public void testRemoveHost() {
+        VplsData vplsData  = VplsData.of(VPLS1, NONE);
+        vplsData.addInterface(V100H1);
+        vplsData.state(ADDED);
+        vplsStore.addVpls(vplsData);
+
+        HostEvent hostEvent = new HostEvent(HostEvent.Type.HOST_REMOVED, V100HOST1);
+        hostService.postHostEvent(hostEvent);
+        vplsData = vplsStore.getVpls(VPLS1);
+        assertNotNull(vplsData);
+
+        assertEquals(vplsData.state(), UPDATING);
+    }
+
+    /**
+     * Pass different VPLS store event to store delegate.
+     * Include these cases:
+     * <ul>
+     *     <li>VPLS added</li>
+     *     <li>VPLS updated</li>
+     *     <li>VPLS state updated</li>
+     *     <li>VPLS removed</li>
+     * </ul>
+     */
+    @Test
+    public void testStoreDelegate() {
+        // Add
+        VplsData vplsData = vplsManager.createVpls(VPLS1, NONE);
+        VplsStoreEvent event = new VplsStoreEvent(VplsStoreEvent.Type.ADD, vplsData);
+        vplsStore.delegate().notify(event);
+
+        VplsOperation vplsOperation = vplsOperationService.operation();
+        assertEquals(vplsOperation.op(), VplsOperation.Operation.ADD);
+        assertEquals(vplsOperation.vpls(), vplsData);
+        vplsOperationService.clear();
+
+        // Update info
+        vplsData.encapsulationType(EncapsulationType.VLAN);
+        vplsData.state(UPDATING);
+        event = new VplsStoreEvent(VplsStoreEvent.Type.UPDATE, vplsData);
+        vplsStore.delegate().notify(event);
+        vplsOperation = vplsOperationService.operation();
+        assertEquals(vplsOperation.op(), VplsOperation.Operation.UPDATE);
+        assertEquals(vplsOperation.vpls(), vplsData);
+        vplsOperationService.clear();
+
+        // Update state (no operation)
+        vplsData.state(VplsData.VplsState.ADDED);
+        event = new VplsStoreEvent(VplsStoreEvent.Type.UPDATE, vplsData);
+        vplsStore.delegate().notify(event);
+        vplsOperation = vplsOperationService.operation();
+        assertNull(vplsOperation);
+        vplsOperationService.clear();
+
+        // Remove
+        event = new VplsStoreEvent(VplsStoreEvent.Type.REMOVE, vplsData);
+        vplsStore.delegate().notify(event);
+        vplsOperation = vplsOperationService.operation();
+        assertEquals(vplsOperation.op(), VplsOperation.Operation.REMOVE);
+        assertEquals(vplsOperation.vpls(), vplsData);
+        vplsOperationService.clear();
+    }
+
+    /**
+     * Trigger host event listener by HOST_ADDED event.
+     */
+    @Test
+    public void hostAddEventTest() {
+        VplsData vplsData = vplsManager.createVpls(VPLS1, NONE);
+        vplsManager.addInterface(vplsData, V100H1);
+        HostEvent hostEvent = new HostEvent(HostEvent.Type.HOST_ADDED, V100HOST1);
+        hostService.postHostEvent(hostEvent);
+
+        vplsData = vplsStore.getVpls(VPLS1);
+        assertEquals(UPDATING, vplsData.state());
+    }
+
+    /**
+     * Trigger host event listener by HOST_REMOVED event.
+     */
+    @Test
+    public void hostRemoveEventTest() {
+        VplsData vplsData = vplsManager.createVpls(VPLS1, NONE);
+        vplsManager.addInterface(vplsData, V100H1);
+        HostEvent hostEvent = new HostEvent(HostEvent.Type.HOST_REMOVED, V100HOST1);
+        hostService.postHostEvent(hostEvent);
+
+        vplsData = vplsStore.getVpls(VPLS1);
+        assertEquals(UPDATING, vplsData.state());
+    }
+
+    /**
+     * Test VPLS operation service.
+     * Stores last operation submitted by VPLS manager.
+     */
+    class TestVplsOperationService implements VplsOperationService {
+        VplsOperation operation;
+
+        @Override
+        public void submit(VplsOperation vplsOperation) {
+            this.operation = vplsOperation;
+        }
+
+        /**
+         * Clears the VPLS operation.
+         */
+        public void clear() {
+            operation = null;
+        }
+
+        /**
+         * Gets the latest VPLS operation.
+         * @return the latest VPLS operation.
+         */
+        public VplsOperation operation() {
+            return operation;
+        }
+    }
+
+}
diff --git a/apps/vpls/src/test/java/org/onosproject/vpls/VplsNeighbourHandlerTest.java b/apps/vpls/src/test/java/org/onosproject/vpls/VplsNeighbourHandlerTest.java
index 412e937..2acc871 100644
--- a/apps/vpls/src/test/java/org/onosproject/vpls/VplsNeighbourHandlerTest.java
+++ b/apps/vpls/src/test/java/org/onosproject/vpls/VplsNeighbourHandlerTest.java
@@ -17,8 +17,6 @@
 
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSetMultimap;
-import com.google.common.collect.Maps;
 import com.google.common.collect.SetMultimap;
 import com.google.common.collect.Sets;
 import org.junit.After;
@@ -30,8 +28,6 @@
 import org.onlab.packet.VlanId;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.incubator.net.intf.Interface;
-import org.onosproject.incubator.net.intf.InterfaceListener;
-import org.onosproject.incubator.net.intf.InterfaceService;
 import org.onosproject.incubator.net.neighbour.NeighbourHandlerRegistration;
 import org.onosproject.incubator.net.neighbour.NeighbourMessageContext;
 import org.onosproject.incubator.net.neighbour.NeighbourMessageHandler;
@@ -39,122 +35,26 @@
 import org.onosproject.incubator.net.neighbour.NeighbourProtocol;
 import org.onosproject.incubator.net.neighbour.NeighbourResolutionService;
 import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DefaultHost;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.EncapsulationType;
 import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.HostLocation;
-import org.onosproject.net.PortNumber;
 import org.onosproject.net.host.HostService;
-import org.onosproject.net.host.HostServiceAdapter;
-import org.onosproject.net.provider.ProviderId;
+import org.onosproject.vpls.api.VplsData;
 
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
-import java.util.stream.Collectors;
 
 import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
 
 /**
- * Tests the the {@link VplsNeighbourHandler} class.
+ * Tests the the {@link VplsNeighbourHandler}.
  */
-public class VplsNeighbourHandlerTest {
+public class VplsNeighbourHandlerTest extends VplsTest {
 
     private static final String IFACES_NOT_EXPECTED =
             "The interfaces reached by the packet are not equal to the " +
                     "interfaces expected.";
-
-    private static final DeviceId DID1 = getDeviceId(1);
-    private static final DeviceId DID2 = getDeviceId(2);
-    private static final DeviceId DID3 = getDeviceId(3);
-    private static final DeviceId DID4 = getDeviceId(4);
-    private static final DeviceId DID5 = getDeviceId(5);
-
-    private static final PortNumber P1 = PortNumber.portNumber(1);
-    private static final PortNumber P2 = PortNumber.portNumber(2);
-    private static final PortNumber P3 = PortNumber.portNumber(3);
-
-    private static final ConnectPoint OF1P1 = new ConnectPoint(DID1, P1);
-    private static final ConnectPoint OF2P1 = new ConnectPoint(DID2, P1);
-    private static final ConnectPoint OF3P1 = new ConnectPoint(DID3, P1);
-    private static final ConnectPoint OF4P1 = new ConnectPoint(DID4, P1);
-    private static final ConnectPoint OF4P2 = new ConnectPoint(DID4, P2);
-    private static final ConnectPoint OF4P3 = new ConnectPoint(DID4, P3);
-    private static final ConnectPoint OF5P1 = new ConnectPoint(DID5, P1);
-    private static final ConnectPoint OF5P2 = new ConnectPoint(DID5, P2);
-    private static final ConnectPoint OF5P3 = new ConnectPoint(DID5, P3);
-
-    private static final String VPLS1 = "vpls1";
-    private static final String VPLS2 = "vpls2";
-    private static final String VPLS3 = "vpls3";
-    private static final String VPLS4 = "vpls4";
-
-    private static final VlanId VLAN100 = VlanId.vlanId("100");
-    private static final VlanId VLAN200 = VlanId.vlanId("200");
-    private static final VlanId VLAN300 = VlanId.vlanId("300");
-    private static final VlanId VLAN400 = VlanId.vlanId("400");
-    private static final VlanId VLAN_NONE = VlanId.NONE;
-
-    private static final Interface V100H1 =
-            new Interface("v100h1", OF1P1, null, null, VLAN100);
-    private static final Interface V100H2 =
-            new Interface("v100h2", OF4P1, null, null, VLAN100);
-    private static final Interface V200H1 =
-            new Interface("v200h1", OF4P2, null, null, VLAN200);
-    private static final Interface V200H2 =
-            new Interface("v200h2", OF2P1, null, null, VLAN200);
-    private static final Interface V300H1 =
-            new Interface("v300h1", OF3P1, null, null, VLAN300);
-    private static final Interface V400H1 =
-            new Interface("v400h1", OF5P1, null, null, VLAN400);
-    private static final Interface VNONEH1 =
-            new Interface("vNoneh1", OF5P2, null, null, VLAN_NONE);
-    private static final Interface VNONEH2 =
-            new Interface("vNoneh2", OF5P3, null, null, VLAN_NONE);
-    private static final Interface VNONEH3 =
-            new Interface("vNoneh3", OF4P3, null, null, VLAN_NONE);
-
-    private static final MacAddress MAC1 = MacAddress.valueOf("00:00:00:00:00:01");
-    private static final MacAddress MAC2 = MacAddress.valueOf("00:00:00:00:00:02");
-    private static final MacAddress MAC3 = MacAddress.valueOf("00:00:00:00:00:03");
-    private static final MacAddress MAC4 = MacAddress.valueOf("00:00:00:00:00:04");
-    private static final MacAddress MAC5 = MacAddress.valueOf("00:00:00:00:00:05");
-    private static final MacAddress MAC6 = MacAddress.valueOf("00:00:00:00:00:06");
-    private static final MacAddress MAC7 = MacAddress.valueOf("00:00:00:00:00:07");
-    private static final MacAddress MAC8 = MacAddress.valueOf("00:00:00:00:00:08");
-    private static final MacAddress MAC9 = MacAddress.valueOf("00:00:00:00:00:09");
-
-    private static final ProviderId PID = new ProviderId("of", "foo");
-
-    private final Host v100Host1 = makeHost(MAC1, VLAN100, OF1P1);
-    private final Host v100Host2 = makeHost(MAC2, VLAN100, OF4P1);
-    private final Host v200Host1 = makeHost(MAC3, VLAN200, OF4P2);
-    private final Host v200Host2 = makeHost(MAC5, VLAN200, OF2P1);
-    private final Host v300Host1 = makeHost(MAC4, VLAN300, OF3P1);
-    private final Host v400Host1 = makeHost(MAC6, VLAN400, OF5P1);
-    private final Host vNoneHost1 = makeHost(MAC7, VLAN_NONE, OF5P2);
-    private final Host vNoneHost2 = makeHost(MAC8, VLAN_NONE, OF5P3);
-    private final Host vNoneHost3 = makeHost(MAC9, VLAN_NONE, OF4P3);
-
-    private final Set<Host> availableHosts = ImmutableSet.of(v100Host1,
-                                                             v100Host2,
-                                                             v200Host1,
-                                                             v300Host1,
-                                                             v200Host2,
-                                                             v400Host1,
-                                                             vNoneHost1,
-                                                             vNoneHost2,
-                                                             vNoneHost3);
-
-    private final Set<Interface> availableInterfaces =
-            ImmutableSet.of(V100H1, V100H2, V200H1, V200H2, V300H1,
-                            V400H1, VNONEH1, VNONEH2, VNONEH3);
-
     private VplsNeighbourHandler vplsNeighbourHandler;
-
     private HostService hostService;
 
     /**
@@ -167,29 +67,46 @@
     @Before
     public void setUp() {
         vplsNeighbourHandler = new VplsNeighbourHandler();
-        SetMultimap<String, Interface> ifacesByVpls =
-                HashMultimap.create();
-        ifacesByVpls.put(VPLS1, V100H1);
-        ifacesByVpls.put(VPLS1, V200H1);
-        ifacesByVpls.put(VPLS1, V300H1);
-        ifacesByVpls.put(VPLS2, V100H2);
-        ifacesByVpls.put(VPLS2, V200H2);
-        ifacesByVpls.put(VPLS3, VNONEH1);
-        ifacesByVpls.put(VPLS3, VNONEH2);
-        ifacesByVpls.put(VPLS4, V400H1);
-        ifacesByVpls.put(VPLS4, VNONEH3);
-        HashMap<String, EncapsulationType> encap = Maps.newHashMap();
-        vplsNeighbourHandler.vplsConfigService =
-                new TestVplsConfigService(ifacesByVpls, encap);
-        vplsNeighbourHandler.interfaceService =
-                new TestInterfaceService();
-        vplsNeighbourHandler.neighbourService =
-                new TestNeighbourService();
         hostService = new TestHostService();
+        vplsNeighbourHandler.vplsStore = new TestVplsStore();
+        vplsNeighbourHandler.interfaceService = new TestInterfaceService();
+        vplsNeighbourHandler.neighbourService = new TestNeighbourService();
+        vplsNeighbourHandler.coreService = new TestCoreService();
+        vplsNeighbourHandler.configService = new TestConfigService();
+
+        // Init VPLS store
+        VplsData vplsData = VplsData.of(VPLS1);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1, V200H1, V300H1));
+        vplsNeighbourHandler.vplsStore.addVpls(vplsData);
+
+        vplsData = VplsData.of(VPLS2);
+        vplsData.addInterfaces(ImmutableSet.of(V100H2, V200H2));
+        vplsNeighbourHandler.vplsStore.addVpls(vplsData);
+
+        vplsData = VplsData.of(VPLS3);
+        vplsData.addInterfaces(ImmutableSet.of(VNONEH1, VNONEH2));
+        vplsNeighbourHandler.vplsStore.addVpls(vplsData);
+
+        vplsData = VplsData.of(VPLS4);
+        vplsData.addInterfaces(ImmutableSet.of(V400H1, VNONEH3));
+        vplsNeighbourHandler.vplsStore.addVpls(vplsData);
+
+        vplsNeighbourHandler.activate();
+
     }
 
     @After
     public void tearDown() {
+        vplsNeighbourHandler.deactivate();
+    }
+
+    /**
+     * Registers neighbour handler to all available interfaces.
+     */
+    @Test
+    public void testConfigNeighbourHandler() {
+        vplsNeighbourHandler.configNeighbourHandler();
+        assertEquals(9, vplsNeighbourHandler.neighbourService.getHandlerRegistrations().size());
     }
 
     /**
@@ -200,21 +117,21 @@
     public void vpls1RequestMessage() {
         // Request messages from v100h1 (VPLS 1) should be received by v200h1 and v300h1
         TestMessageContext requestMessage =
-                makeBroadcastRequestContext(v100Host1);
+                makeBroadcastRequestContext(V100HOST1);
         Set<Interface> expectInterfaces = ImmutableSet.of(V200H1, V300H1);
-        vplsNeighbourHandler.handleRequest(requestMessage);
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(requestMessage);
         assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, requestMessage.forwardResults);
 
         // Request messages from v200h1 (VPLS 1) should be received by v100h1 and v300h1
-        requestMessage = makeBroadcastRequestContext(v200Host1);
+        requestMessage = makeBroadcastRequestContext(V200HOST1);
         expectInterfaces = ImmutableSet.of(V100H1, V300H1);
-        vplsNeighbourHandler.handleRequest(requestMessage);
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(requestMessage);
         assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, requestMessage.forwardResults);
 
         // Request from v300h1 (VPLS 1) should be received by v100h1 and v200h1
-        requestMessage = makeBroadcastRequestContext(v300Host1);
+        requestMessage = makeBroadcastRequestContext(V300HOST1);
         expectInterfaces = ImmutableSet.of(V100H1, V200H1);
-        vplsNeighbourHandler.handleRequest(requestMessage);
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(requestMessage);
         assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, requestMessage.forwardResults);
     }
 
@@ -226,15 +143,15 @@
     public void vpls2RequestMessage() {
         // Request messages from v100h2 (VPLS 2) should be received by v200h2
         TestMessageContext requestMessage =
-                makeBroadcastRequestContext(v100Host2);
+                makeBroadcastRequestContext(V100HOST2);
         Set<Interface> expectInterfaces = ImmutableSet.of(V200H2);
-        vplsNeighbourHandler.handleRequest(requestMessage);
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(requestMessage);
         assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, requestMessage.forwardResults);
 
         // Request messages from v200h2 (VPLS 2) should be received by v100h2
-        requestMessage = makeBroadcastRequestContext(v200Host2);
+        requestMessage = makeBroadcastRequestContext(V200HOST2);
         expectInterfaces = ImmutableSet.of(V100H2);
-        vplsNeighbourHandler.handleRequest(requestMessage);
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(requestMessage);
         assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, requestMessage.forwardResults);
     }
 
@@ -246,17 +163,17 @@
      */
     @Test
     public void vpls3RequestMessage() {
-        // Request messages from vNoneHost1 (VPLS 3) should be received by vNoneHost2
+        // Request messages from VNONEHOST1 (VPLS 3) should be received by VNONEHOST2
         TestMessageContext requestMessage =
-                makeBroadcastRequestContext(vNoneHost1);
+                makeBroadcastRequestContext(VNONEHOST1);
         Set<Interface> expectInterfaces = ImmutableSet.of(VNONEH2);
-        vplsNeighbourHandler.handleRequest(requestMessage);
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(requestMessage);
         assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, requestMessage.forwardResults);
 
         // Request messages from vNoneh2 (VPLS 3) should be received by vNoneh1
-        requestMessage = makeBroadcastRequestContext(vNoneHost2);
+        requestMessage = makeBroadcastRequestContext(VNONEHOST2);
         expectInterfaces = ImmutableSet.of(VNONEH1);
-        vplsNeighbourHandler.handleRequest(requestMessage);
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(requestMessage);
         assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, requestMessage.forwardResults);
     }
 
@@ -268,17 +185,17 @@
      */
     @Test
     public void vpls4RequestMessage() {
-        // Request messages from v400Host1 (VPLS 4) should be received by vNoneHost3
+        // Request messages from V400HOST1 (VPLS 4) should be received by VNONEHOST3
         TestMessageContext requestMessage =
-                makeBroadcastRequestContext(v400Host1);
+                makeBroadcastRequestContext(V400HOST1);
         Set<Interface> expectInterfaces = ImmutableSet.of(VNONEH3);
-        vplsNeighbourHandler.handleRequest(requestMessage);
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(requestMessage);
         assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, requestMessage.forwardResults);
 
-        // Request messages from vNoneHost3 (VPLS 4) should be received by v400Host1
-        requestMessage = makeBroadcastRequestContext(vNoneHost3);
+        // Request messages from VNONEHOST3 (VPLS 4) should be received by V400HOST1
+        requestMessage = makeBroadcastRequestContext(VNONEHOST3);
         expectInterfaces = ImmutableSet.of(V400H1);
-        vplsNeighbourHandler.handleRequest(requestMessage);
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(requestMessage);
         assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, requestMessage.forwardResults);
     }
 
@@ -291,21 +208,21 @@
     public void vpls1ReplyMessage() {
         // Reply messages from v100h1 (VPLS 1) should be received by v200h1
         TestMessageContext replyMessage =
-                makeReplyContext(v100Host1, v200Host1);
+                makeReplyContext(V100HOST1, V200HOST1);
         Set<Interface> expectInterfaces = ImmutableSet.of(V200H1);
-        vplsNeighbourHandler.handleReply(replyMessage, hostService);
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(replyMessage);
         assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, replyMessage.forwardResults);
 
         // Reply messages from v200h1 (VPLS 1) should be received by v300h1
-        replyMessage = makeReplyContext(v200Host1, v300Host1);
+        replyMessage = makeReplyContext(V200HOST1, V300HOST1);
         expectInterfaces = ImmutableSet.of(V300H1);
-        vplsNeighbourHandler.handleReply(replyMessage, hostService);
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(replyMessage);
         assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, replyMessage.forwardResults);
 
         // Reply messages from v300h1 (VPLS 1) should be received by v100h1
-        replyMessage = makeReplyContext(v300Host1, v100Host1);
+        replyMessage = makeReplyContext(V300HOST1, V100HOST1);
         expectInterfaces = ImmutableSet.of(V100H1);
-        vplsNeighbourHandler.handleReply(replyMessage, hostService);
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(replyMessage);
         assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, replyMessage.forwardResults);
     }
 
@@ -318,15 +235,15 @@
     public void vpls2ReplyMessage() {
         // Reply messages from v100h2 (VPLS 2) should be received by v200h2
         TestMessageContext replyMessage =
-                makeReplyContext(v100Host2, v200Host2);
+                makeReplyContext(V100HOST2, V200HOST2);
         Set<Interface> expectInterfaces = ImmutableSet.of(V200H2);
-        vplsNeighbourHandler.handleReply(replyMessage, hostService);
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(replyMessage);
         assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, replyMessage.forwardResults);
 
         // Reply messages from v200h2 (VPLS 2) should be received by v100h2
-        replyMessage = makeReplyContext(v200Host2, v100Host2);
+        replyMessage = makeReplyContext(V200HOST2, V100HOST2);
         expectInterfaces = ImmutableSet.of(V100H2);
-        vplsNeighbourHandler.handleReply(replyMessage, hostService);
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(replyMessage);
         assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, replyMessage.forwardResults);
     }
 
@@ -339,15 +256,15 @@
     public void vpls3ReplyMessage() {
         // Reply messages from vNoneh1 (VPLS 3) should be received by vNoneh2
         TestMessageContext replyMessage =
-                makeReplyContext(vNoneHost1, vNoneHost2);
+                makeReplyContext(VNONEHOST1, VNONEHOST2);
         Set<Interface> expectInterfaces = ImmutableSet.of(VNONEH2);
-        vplsNeighbourHandler.handleReply(replyMessage, hostService);
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(replyMessage);
         assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, replyMessage.forwardResults);
 
         // Reply messages from vNoneh2 (VPLS 3) should be received by vNoneh1
-        replyMessage = makeReplyContext(vNoneHost2, vNoneHost1);
+        replyMessage = makeReplyContext(VNONEHOST2, VNONEHOST1);
         expectInterfaces = ImmutableSet.of(VNONEH1);
-        vplsNeighbourHandler.handleReply(replyMessage, hostService);
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(replyMessage);
         assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, replyMessage.forwardResults);
     }
 
@@ -360,15 +277,15 @@
     public void vpls4ReplyMessage() {
         // Reply messages from v400h1 (VPLS 4) should be received by vNoneh3
         TestMessageContext replyMessage =
-                makeReplyContext(v400Host1, vNoneHost3);
+                makeReplyContext(V400HOST1, VNONEHOST3);
         Set<Interface> expectInterfaces = ImmutableSet.of(VNONEH3);
-        vplsNeighbourHandler.handleReply(replyMessage, hostService);
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(replyMessage);
         assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, replyMessage.forwardResults);
 
         // Reply messages from vNoneh3 (VPLS 4) should be received by v400h1
-        replyMessage = makeReplyContext(vNoneHost3, v400Host1);
+        replyMessage = makeReplyContext(VNONEHOST3, V400HOST1);
         expectInterfaces = ImmutableSet.of(V400H1);
-        vplsNeighbourHandler.handleReply(replyMessage, hostService);
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(replyMessage);
         assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, replyMessage.forwardResults);
     }
 
@@ -381,52 +298,56 @@
     public void wrongReplyMessage() {
         // Reply message from v100h1 (VPLS 1) to v100h2 (VPLS 2).
         // Forward results should be empty
-        TestMessageContext replyMessage = makeReplyContext(v100Host1, v100Host2);
+        TestMessageContext replyMessage = makeReplyContext(V100HOST1, V100HOST2);
         Set<Interface> expectInterfaces = ImmutableSet.of();
-        vplsNeighbourHandler.handleReply(replyMessage, hostService);
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(replyMessage);
         assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, replyMessage.forwardResults);
 
         // Reply message from v200h2 (VPLS 2) to v300h1 (VPLS 1).
         // Forward results should be empty
-        replyMessage = makeReplyContext(v200Host2, v300Host1);
+        replyMessage = makeReplyContext(V200HOST2, V300HOST1);
         expectInterfaces = ImmutableSet.of();
-        vplsNeighbourHandler.handleReply(replyMessage, hostService);
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(replyMessage);
         assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, replyMessage.forwardResults);
 
         // Reply message from vNoneh1 (VPLS 3) to v400h1 (VPLS 4).
         // Forward results should be empty
-        replyMessage = makeReplyContext(vNoneHost1, v400Host1);
+        replyMessage = makeReplyContext(VNONEHOST1, V400HOST1);
         expectInterfaces = ImmutableSet.of();
-        vplsNeighbourHandler.handleReply(replyMessage, hostService);
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(replyMessage);
         assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, replyMessage.forwardResults);
 
         // Reply message from vNoneh3 (VPLS 4) to vNoneH2 (VPLS 3).
         // Forward results should be empty
-        replyMessage = makeReplyContext(vNoneHost3, vNoneHost2);
+        replyMessage = makeReplyContext(VNONEHOST3, VNONEHOST2);
         expectInterfaces = ImmutableSet.of();
-        vplsNeighbourHandler.handleReply(replyMessage, hostService);
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(replyMessage);
         assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, replyMessage.forwardResults);
     }
 
     /**
-     * Returns the device Id of the ith device.
-     *
-     * @param i the device to get the Id of
-     * @return the device Id
+     * Sends reply and request message from a host which not related to any VPLS.
      */
-    private static DeviceId getDeviceId(int i) {
-        return DeviceId.deviceId("" + i);
+    @Test
+    public void testVplsNotfound() {
+        TestMessageContext replyMessage = makeReplyContext(V300HOST2, V100HOST1);
+        Set<Interface> expectInterfaces = ImmutableSet.of();
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(replyMessage);
+        assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, replyMessage.forwardResults);
+        assertTrue(replyMessage.dropped());
+
+        TestMessageContext requestMessage = makeBroadcastRequestContext(V300HOST2);
+        ((TestNeighbourService) vplsNeighbourHandler.neighbourService).sendNeighourMessage(requestMessage);
+        assertEquals(IFACES_NOT_EXPECTED, expectInterfaces, requestMessage.forwardResults);
+        assertTrue(requestMessage.dropped());
     }
 
-    private Host makeHost(MacAddress mac, VlanId vlan, ConnectPoint cp) {
-        return new DefaultHost(PID,
-                               HostId.hostId(mac, vlan),
-                               mac,
-                               vlan,
-                               new HostLocation(cp, 0),
-                               Sets.newHashSet());
-    }
-
+    /**
+     * Generates broadcast request message context by given source host.
+     *
+     * @param host the source host
+     * @return the request message context
+     */
     private TestMessageContext makeBroadcastRequestContext(Host host) {
         return new TestMessageContext(host.location(),
                                       host.mac(),
@@ -435,6 +356,13 @@
                                       NeighbourMessageType.REQUEST);
     }
 
+    /**
+     * Generates reply message context by given source and destination host.
+     *
+     * @param src the source host
+     * @param dst the destination host
+     * @return the reply message context
+     */
     private TestMessageContext makeReplyContext(Host src, Host dst) {
         return new TestMessageContext(src.location(),
                                       src.mac(),
@@ -443,15 +371,16 @@
                                       NeighbourMessageType.REPLY);
     }
 
+    /**
+     * Test message context.
+     */
     private class TestMessageContext implements NeighbourMessageContext {
-
-
         private final NeighbourMessageType type;
         private final MacAddress srcMac;
         private final MacAddress dstMac;
         private final ConnectPoint inPort;
         private final VlanId vlanId;
-
+        private boolean dropped = false;
         public Set<Interface> forwardResults;
 
         /**
@@ -475,8 +404,8 @@
             this.dstMac = dstMac;
             this.vlanId = vlanId;
             this.type = type;
-
             this.forwardResults = Sets.newHashSet();
+            this.dropped = false;
         }
 
         @Override
@@ -518,6 +447,10 @@
         public void forward(ConnectPoint outPort) {
         }
 
+        /**
+         * Records all forward network interface information.
+         * @param outIntf output interface
+         */
         @Override
         public void forward(Interface outIntf) {
             forwardResults.add(outIntf);
@@ -533,6 +466,7 @@
 
         @Override
         public void drop() {
+            this.dropped = true;
         }
 
         @Override
@@ -544,141 +478,16 @@
         public NeighbourProtocol protocol() {
             return null;
         }
-    }
 
-    private class TestVplsConfigService extends VplsConfigServiceAdapter {
-
-        private final SetMultimap<String, Interface> ifacesByVplsName;
-
-        public TestVplsConfigService(SetMultimap<String, Interface> ifacesByVplsName,
-                                     HashMap<String, EncapsulationType> encapByVplsName) {
-            this.ifacesByVplsName = ifacesByVplsName;
-        }
-
-        @Override
-        public void addVpls(String vplsName, Set<String> ifaceNames, String encap) {
-            if (!ifacesByVplsName.containsKey(vplsName)) {
-                ifaceNames.forEach(ifaceName -> {
-                    availableInterfaces.forEach(iface -> {
-                        if (iface.name().equals(ifaceName)) {
-                            ifacesByVplsName.put(vplsName, iface);
-                        }
-                    });
-                });
-            }
-        }
-
-        @Override
-        public void removeVpls(String vplsName) {
-            if (ifacesByVplsName.containsKey(vplsName)) {
-                ifacesByVplsName.removeAll(vplsName);
-            }
-        }
-
-        @Override
-        public void addIface(String vplsName, String ifaceName) {
-            availableInterfaces.forEach(intf -> {
-                if (intf.name().equals(ifaceName)) {
-                    ifacesByVplsName.put(vplsName, intf);
-                }
-            });
-        }
-
-        @Override
-        public void removeIface(String ifaceName) {
-            SetMultimap<String, Interface> toBeRemoved = HashMultimap.create();
-            ifacesByVplsName.entries().forEach(e -> {
-                if (e.getValue().name().equals(ifaceName)) {
-                    toBeRemoved.put(e.getKey(), e.getValue());
-                }
-            });
-            toBeRemoved.entries()
-                    .forEach(e -> ifacesByVplsName.remove(e.getKey(),
-                                                          e.getValue()));
-        }
-
-        @Override
-        public void cleanVplsConfig() {
-            ifacesByVplsName.clear();
-        }
-
-        @Override
-        public Set<Interface> allIfaces() {
-            return ImmutableSet.copyOf(ifacesByVplsName.values());
-        }
-
-        @Override
-        public Set<Interface> ifaces(String name) {
-            return ifacesByVplsName.get(name)
-                    .stream()
-                    .collect(Collectors.toSet());
-        }
-
-        @Override
-        public Set<String> vplsNames() {
-            return ifacesByVplsName.keySet();
-        }
-
-        @Override
-        public SetMultimap<String, Interface> ifacesByVplsName() {
-            return ImmutableSetMultimap.copyOf(ifacesByVplsName);
-        }
-
-        @Override
-        public SetMultimap<String, Interface> ifacesByVplsName(VlanId vlan,
-                                                               ConnectPoint connectPoint) {
-            String vplsName =
-                    ifacesByVplsName.entries().stream()
-                            .filter(e -> e.getValue().connectPoint().equals(connectPoint))
-                            .filter(e -> e.getValue().vlan().equals(vlan))
-                            .map(Map.Entry::getKey)
-                            .findFirst()
-                            .orElse(null);
-            SetMultimap<String, Interface> result = HashMultimap.create();
-            if (vplsName != null &&
-                    ifacesByVplsName.containsKey(vplsName)) {
-                ifacesByVplsName.get(vplsName)
-                        .forEach(intf -> result.put(vplsName, intf));
-                return result;
-            }
-            return null;
+        public boolean dropped() {
+            return dropped;
         }
     }
 
-    class TestHostService extends HostServiceAdapter {
-        @Override
-        public Set<Host> getHostsByMac(MacAddress mac) {
-            return availableHosts.stream()
-                    .filter(host -> host.mac().equals(mac))
-                    .collect(Collectors.toSet());
-        }
-
-        @Override
-        public Iterable<Host> getHosts() {
-            return availableHosts;
-        }
-
-        @Override
-        public Set<Host> getHostsByVlan(VlanId vlanId) {
-            return availableHosts.stream()
-                    .filter(host -> host.vlan().equals(vlanId))
-                    .collect(Collectors.toSet());
-        }
-
-        @Override
-        public int getHostCount() {
-            return availableHosts.size();
-        }
-
-        @Override
-        public Host getHost(HostId hostId) {
-            return availableHosts.stream()
-                    .filter(host -> host.id().equals(hostId))
-                    .findFirst()
-                    .orElse(null);
-        }
-    }
-
+    /**
+     * Test neighbour service; records all registrations between neighbour
+     * message handler and interfaces.
+     */
     private class TestNeighbourService implements NeighbourResolutionService {
         private SetMultimap<ConnectPoint, NeighbourHandlerRegistration> handlerRegs;
 
@@ -732,6 +541,26 @@
             return handlerRegs.asMap();
         }
 
+        /**
+         * Sends neighbour message context to all handler which related to the
+         * context.
+         *
+         * @param context the neighbour message context
+         */
+        public void sendNeighourMessage(NeighbourMessageContext context) {
+            ConnectPoint connectPoint = context.inPort();
+            VlanId vlanId = context.vlan();
+            Collection<NeighbourHandlerRegistration> registrations = handlerRegs.get(connectPoint);
+            registrations.forEach(reg -> {
+                if (reg.intf().vlan().equals(vlanId)) {
+                    reg.handler().handleMessage(context, hostService);
+                }
+            });
+        }
+
+        /**
+         * Test handler registration.
+         */
         private class HandlerRegistration implements NeighbourHandlerRegistration {
             private final Interface intf;
             private final NeighbourMessageHandler handler;
@@ -767,65 +596,4 @@
             }
         }
     }
-
-    class TestInterfaceService implements InterfaceService {
-
-        @Override
-        public void addListener(InterfaceListener listener) {
-        }
-
-        @Override
-        public void removeListener(InterfaceListener listener) {
-        }
-
-        @Override
-        public Set<Interface> getInterfaces() {
-            return availableInterfaces;
-        }
-
-        @Override
-        public Interface getInterfaceByName(ConnectPoint connectPoint,
-                                            String name) {
-            return availableInterfaces.stream()
-                    .filter(intf -> intf.name().equals(name))
-                    .findFirst()
-                    .orElse(null);
-        }
-
-        @Override
-        public Set<Interface> getInterfacesByPort(ConnectPoint port) {
-            return availableInterfaces.stream()
-                    .filter(intf -> intf.connectPoint().equals(port))
-                    .collect(Collectors.toSet());
-        }
-
-        @Override
-        public Set<Interface> getInterfacesByIp(IpAddress ip) {
-            return availableInterfaces.stream()
-                    .filter(intf -> intf.ipAddressesList().contains(ip))
-                    .collect(Collectors.toSet());
-        }
-
-        @Override
-        public Set<Interface> getInterfacesByVlan(VlanId vlan) {
-            return availableInterfaces.stream()
-                    .filter(intf -> intf.vlan().equals(vlan))
-                    .collect(Collectors.toSet());
-        }
-
-        @Override
-        public Interface getMatchingInterface(IpAddress ip) {
-            return availableInterfaces.stream()
-                    .filter(intf -> intf.ipAddressesList().contains(ip))
-                    .findFirst()
-                    .orElse(null);
-        }
-
-        @Override
-        public Set<Interface> getMatchingInterfaces(IpAddress ip) {
-            return availableInterfaces.stream()
-                    .filter(intf -> intf.ipAddressesList().contains(ip))
-                    .collect(Collectors.toSet());
-        }
-    }
 }
diff --git a/apps/vpls/src/test/java/org/onosproject/vpls/VplsOperationManagerTest.java b/apps/vpls/src/test/java/org/onosproject/vpls/VplsOperationManagerTest.java
new file mode 100644
index 0000000..36ffda9
--- /dev/null
+++ b/apps/vpls/src/test/java/org/onosproject/vpls/VplsOperationManagerTest.java
@@ -0,0 +1,543 @@
+/*
+ * 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;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.cluster.ClusterServiceAdapter;
+import org.onosproject.cluster.Leader;
+import org.onosproject.cluster.Leadership;
+import org.onosproject.cluster.LeadershipEvent;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.EncapsulationType;
+import org.onosproject.net.Host;
+import org.onosproject.net.host.HostServiceAdapter;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentEvent;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.store.service.WallClockTimestamp;
+import org.onosproject.vpls.api.VplsData;
+import org.onosproject.vpls.api.VplsOperation;
+
+import java.util.ArrayDeque;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.Set;
+
+import static org.junit.Assert.*;
+import static org.onlab.junit.TestTools.assertAfter;
+import static org.onlab.junit.TestTools.delay;
+
+/**
+ * Tests for {@link VplsOperationManager}.
+ */
+public class VplsOperationManagerTest extends VplsTest {
+
+    VplsOperationManager vplsOperationManager;
+    private static final int OPERATION_DELAY = 1000;
+    private static final int OPERATION_DURATION = 1500;
+
+    @Before
+    public void setup() {
+        if (idGenerator == null) {
+            idGenerator = new TestIdGenerator();
+        }
+        Intent.unbindIdGenerator(idGenerator);
+        Intent.bindIdGenerator(idGenerator);
+        vplsOperationManager = new VplsOperationManager();
+        vplsOperationManager.coreService = new TestCoreService();
+        vplsOperationManager.intentService = new TestIntentService();
+        vplsOperationManager.leadershipService = new TestLeadershipService();
+        vplsOperationManager.clusterService = new ClusterServiceAdapter();
+        vplsOperationManager.hostService = new TestHostService();
+        vplsOperationManager.vplsStore = new TestVplsStore();
+        vplsOperationManager.isLeader = true;
+        vplsOperationManager.activate();
+    }
+
+    @After
+    public void tearDown() {
+        vplsOperationManager.deactivate();
+    }
+
+    /**
+     * Sends leadership event to the manager and checks if the manager is
+     * leader or not.
+     */
+    @Test
+    public void testLeadershipEvent() {
+        vplsOperationManager.isLeader = false;
+        vplsOperationManager.localNodeId = NODE_ID_1;
+
+        // leader changed to self
+        Leader leader = new Leader(NODE_ID_1, 0, 0);
+        Leadership leadership = new Leadership(APP_NAME, leader, ImmutableList.of());
+        LeadershipEvent event = new LeadershipEvent(LeadershipEvent.Type.LEADER_CHANGED, leadership);
+        ((TestLeadershipService) vplsOperationManager.leadershipService).sendEvent(event);
+        assertTrue(vplsOperationManager.isLeader);
+
+        // leader changed to other
+        leader = new Leader(NODE_ID_2, 0, 0);
+        leadership = new Leadership(APP_NAME, leader, ImmutableList.of());
+        event = new LeadershipEvent(LeadershipEvent.Type.LEADER_CHANGED, leadership);
+        ((TestLeadershipService) vplsOperationManager.leadershipService).sendEvent(event);
+        assertFalse(vplsOperationManager.isLeader);
+    }
+
+    /**
+     * Submits an ADD operation to the operation manager; check if the VPLS
+     * store changed after a period.
+     */
+    @Test
+    public void testSubmitAddOperation() {
+        VplsData vplsData = VplsData.of(VPLS1);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2));
+
+        VplsOperation vplsOperation = VplsOperation.of(vplsData,
+                                                       VplsOperation.Operation.ADD);
+
+        vplsOperationManager.submit(vplsOperation);
+        assertAfter(OPERATION_DELAY, OPERATION_DURATION, () -> {
+            Collection<VplsData> vplss = vplsOperationManager.vplsStore.getAllVpls();
+            assertEquals(1, vplss.size());
+            VplsData result = vplss.iterator().next();
+
+            assertEquals(vplsData, result);
+            assertEquals(VplsData.VplsState.ADDED, result.state());
+
+            Set<Intent> intentsInstalled =
+                    Sets.newHashSet(vplsOperationManager.intentService.getIntents());
+            assertEquals(4, intentsInstalled.size());
+        });
+    }
+
+    /**
+     * Submits an ADD operation to the operation manager; check the VPLS state
+     * from store if Intent install failed.
+     */
+    @Test
+    public void testSubmitAddOperationFail() {
+        vplsOperationManager.intentService = new AlwaysFailureIntentService();
+        VplsData vplsData = VplsData.of(VPLS1);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2));
+
+        VplsOperation vplsOperation = VplsOperation.of(vplsData,
+                                                       VplsOperation.Operation.ADD);
+        vplsOperationManager.submit(vplsOperation);
+        assertAfter(OPERATION_DELAY, OPERATION_DURATION, () -> {
+            Collection<VplsData> vplss = vplsOperationManager.vplsStore.getAllVpls();
+            assertEquals(1, vplss.size());
+            VplsData result = vplss.iterator().next();
+
+            assertEquals(vplsData, result);
+            assertEquals(VplsData.VplsState.FAILED, result.state());
+        });
+    }
+
+    /**
+     * Submits an REMOVE operation to the operation manager; check if the VPLS
+     * store changed after a period.
+     */
+    @Test
+    public void testSubmitRemoveOperation() {
+        VplsData vplsData = VplsData.of(VPLS1);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2));
+        vplsData.state(VplsData.VplsState.REMOVING);
+
+        VplsOperation vplsOperation = VplsOperation.of(vplsData,
+                                                       VplsOperation.Operation.REMOVE);
+
+        vplsOperationManager.submit(vplsOperation);
+
+        assertAfter(OPERATION_DELAY, OPERATION_DURATION, () -> {
+            Collection<VplsData> vplss = vplsOperationManager.vplsStore.getAllVpls();
+            assertEquals(0, vplss.size());
+        });
+    }
+
+    /**
+     * Submits an UPDATE operation with VPLS interface update to the operation manager; check if the VPLS
+     * store changed after a period.
+     */
+    @Test
+    public void testSubmitUpdateOperation() {
+        VplsData vplsData = VplsData.of(VPLS1);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1));
+        vplsData.state(VplsData.VplsState.ADDED);
+        vplsOperationManager.vplsStore.addVpls(vplsData);
+
+        vplsData = VplsData.of(VPLS1, EncapsulationType.VLAN);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2));
+        vplsData.state(VplsData.VplsState.UPDATING);
+
+        VplsOperation vplsOperation = VplsOperation.of(vplsData,
+                                                       VplsOperation.Operation.UPDATE);
+
+        vplsOperationManager.submit(vplsOperation);
+
+        assertAfter(OPERATION_DELAY, OPERATION_DURATION, () -> {
+            Collection<VplsData> vplss = vplsOperationManager.vplsStore.getAllVpls();
+            VplsData result = vplss.iterator().next();
+            VplsData expected = VplsData.of(VPLS1, EncapsulationType.VLAN);
+            expected.addInterfaces(ImmutableSet.of(V100H1, V100H2));
+            expected.state(VplsData.VplsState.ADDED);
+
+            assertEquals(1, vplss.size());
+            assertEquals(expected, result);
+
+            Set<Intent> intentsInstalled =
+                    Sets.newHashSet(vplsOperationManager.intentService.getIntents());
+            assertEquals(4, intentsInstalled.size());
+        });
+    }
+
+    /**
+     * Submits an UPDATE operation with VPLS host update to the operation manager; check if the VPLS
+     * store changed after a period.
+     */
+    @Test
+    public void testSubmitUpdateHostOperation() {
+        vplsOperationManager.hostService = new EmptyHostService();
+        VplsData vplsData = VplsData.of(VPLS1);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2));
+
+        VplsOperation vplsOperation = VplsOperation.of(vplsData,
+                                                       VplsOperation.Operation.ADD);
+        vplsOperationManager.submit(vplsOperation);
+        delay(1000);
+        vplsOperationManager.hostService = new TestHostService();
+
+        vplsData = VplsData.of(VPLS1);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2));
+        vplsData.state(VplsData.VplsState.UPDATING);
+
+        vplsOperation = VplsOperation.of(vplsData,
+                                                       VplsOperation.Operation.UPDATE);
+
+        vplsOperationManager.submit(vplsOperation);
+
+        assertAfter(OPERATION_DELAY, OPERATION_DURATION, () -> {
+            Collection<VplsData> vplss = vplsOperationManager.vplsStore.getAllVpls();
+            VplsData result = vplss.iterator().next();
+            VplsData expected = VplsData.of(VPLS1);
+            expected.addInterfaces(ImmutableSet.of(V100H1, V100H2));
+            expected.state(VplsData.VplsState.ADDED);
+            assertEquals(1, vplss.size());
+            assertEquals(expected, result);
+
+            assertEquals(4, vplsOperationManager.intentService.getIntentCount());
+        });
+    }
+
+    /**
+     * Submits same operation twice to the manager; the manager should ignore
+     * duplicated operation.
+     */
+    @Test
+    public void testDuplicateOperationInQueue() {
+        VplsData vplsData = VplsData.of(VPLS1);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2));
+
+        VplsOperation vplsOperation = VplsOperation.of(vplsData,
+                                                       VplsOperation.Operation.ADD);
+
+        vplsOperationManager.submit(vplsOperation);
+        vplsOperationManager.submit(vplsOperation);
+        Deque<VplsOperation> opQueue = vplsOperationManager.pendingVplsOperations.get(VPLS1);
+        assertEquals(1, opQueue.size());
+
+        // Clear operation queue before scheduler process it
+        opQueue.clear();
+    }
+
+    /**
+     * Submits REMOVE operation after submits ADD operation; there should be no
+     * pending or running operation in the manager.
+     */
+    @Test
+    public void testDoNothingOperation() {
+        VplsData vplsData = VplsData.of(VPLS1);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2));
+
+        VplsOperation vplsOperation = VplsOperation.of(vplsData,
+                                                       VplsOperation.Operation.ADD);
+        vplsOperationManager.submit(vplsOperation);
+        vplsOperation = VplsOperation.of(vplsData,
+                                         VplsOperation.Operation.REMOVE);
+        vplsOperationManager.submit(vplsOperation);
+        assertAfter(OPERATION_DELAY, OPERATION_DURATION, () -> {
+            assertEquals(0, vplsOperationManager.pendingVplsOperations.size());
+
+            // Should not have any running operation
+            assertEquals(0, vplsOperationManager.runningOperations.size());
+        });
+    }
+
+    /**
+     * Optimize operations which don't need to be optimized.
+     */
+    @Test
+    public void testOptimizeOperationsNoOptimize() {
+        // empty queue
+        Deque<VplsOperation> operations = new ArrayDeque<>();
+        VplsOperation vplsOperation =
+                VplsOperationManager.getOptimizedVplsOperation(operations);
+        assertNull(vplsOperation);
+
+        // one operation
+        VplsData vplsData = VplsData.of(VPLS1);
+        vplsOperation = VplsOperation.of(vplsData, VplsOperation.Operation.ADD);
+        operations.add(vplsOperation);
+        VplsOperation result =
+                VplsOperationManager.getOptimizedVplsOperation(operations);
+        assertEquals(vplsOperation, result);
+
+    }
+
+    /**
+     * Optimize operations with first is ADD operation and last is also ADD
+     * operation.
+     */
+    @Test
+    public void testOptimizeOperationsAToA() {
+        Deque<VplsOperation> operations = new ArrayDeque<>();
+        VplsData vplsData = VplsData.of(VPLS1);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1));
+        VplsOperation vplsOperation = VplsOperation.of(vplsData,
+                                                       VplsOperation.Operation.ADD);
+        operations.add(vplsOperation);
+        vplsData = VplsData.of(VPLS1, EncapsulationType.VLAN);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2));
+        vplsOperation = VplsOperation.of(vplsData,
+                                         VplsOperation.Operation.ADD);
+        operations.add(vplsOperation);
+        vplsOperation = VplsOperationManager.getOptimizedVplsOperation(operations);
+        assertEquals(VplsOperation.of(vplsData, VplsOperation.Operation.ADD), vplsOperation);
+    }
+
+    /**
+     * Optimize operations with first is ADD operation and last is REMOVE
+     * operation.
+     */
+    @Test
+    public void testOptimizeOperationsAToR() {
+        Deque<VplsOperation> operations = new ArrayDeque<>();
+        VplsData vplsData = VplsData.of(VPLS1);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1));
+        VplsOperation vplsOperation = VplsOperation.of(vplsData,
+                                                       VplsOperation.Operation.ADD);
+        operations.add(vplsOperation);
+        vplsOperation = VplsOperation.of(vplsData,
+                                         VplsOperation.Operation.REMOVE);
+        operations.add(vplsOperation);
+        vplsOperation = VplsOperationManager.getOptimizedVplsOperation(operations);
+        assertNull(vplsOperation);
+    }
+
+    /**
+     * Optimize operations with first is ADD operation and last is UPDATE
+     * operation.
+     */
+    @Test
+    public void testOptimizeOperationsAToU() {
+        Deque<VplsOperation> operations = new ArrayDeque<>();
+        VplsData vplsData = VplsData.of(VPLS1);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1));
+        VplsOperation vplsOperation = VplsOperation.of(vplsData,
+                                                       VplsOperation.Operation.ADD);
+        operations.add(vplsOperation);
+        vplsData = VplsData.of(VPLS1, EncapsulationType.VLAN);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2));
+        vplsOperation = VplsOperation.of(vplsData,
+                                         VplsOperation.Operation.UPDATE);
+        operations.add(vplsOperation);
+        vplsOperation = VplsOperationManager.getOptimizedVplsOperation(operations);
+        assertEquals(VplsOperation.of(vplsData, VplsOperation.Operation.ADD), vplsOperation);
+    }
+
+    /**
+     * Optimize operations with first is REMOVE operation and last is ADD
+     * operation.
+     */
+    @Test
+    public void testOptimizeOperationsRToA() {
+        Deque<VplsOperation> operations = new ArrayDeque<>();
+        VplsData vplsData = VplsData.of(VPLS1);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1));
+        VplsOperation vplsOperation = VplsOperation.of(vplsData,
+                                                       VplsOperation.Operation.REMOVE);
+        operations.add(vplsOperation);
+        vplsData = VplsData.of(VPLS1, EncapsulationType.VLAN);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2));
+        vplsOperation = VplsOperation.of(vplsData,
+                                         VplsOperation.Operation.ADD);
+        operations.add(vplsOperation);
+        vplsOperation = VplsOperationManager.getOptimizedVplsOperation(operations);
+        assertEquals(VplsOperation.of(vplsData, VplsOperation.Operation.UPDATE), vplsOperation);
+    }
+
+    /**
+     * Optimize operations with first is REMOVE operation and last is also
+     * REMOVE operation.
+     */
+    @Test
+    public void testOptimizeOperationsRToR() {
+        Deque<VplsOperation> operations = new ArrayDeque<>();
+        VplsData vplsData = VplsData.of(VPLS1);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1));
+        VplsOperation vplsOperation = VplsOperation.of(vplsData,
+                                                       VplsOperation.Operation.REMOVE);
+        operations.add(vplsOperation);
+        vplsData = VplsData.of(VPLS1, EncapsulationType.VLAN);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2));
+        vplsOperation = VplsOperation.of(vplsData,
+                                         VplsOperation.Operation.REMOVE);
+        operations.add(vplsOperation);
+        vplsOperation = VplsOperationManager.getOptimizedVplsOperation(operations);
+        vplsData = VplsData.of(VPLS1);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1));
+        assertEquals(VplsOperation.of(vplsData, VplsOperation.Operation.REMOVE), vplsOperation);
+    }
+
+    /**
+     * Optimize operations with first is REMOVE operation and last is UPDATE
+     * operation.
+     */
+    @Test
+    public void testOptimizeOperationsRToU() {
+        Deque<VplsOperation> operations = new ArrayDeque<>();
+        VplsData vplsData = VplsData.of(VPLS1);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1));
+        VplsOperation vplsOperation = VplsOperation.of(vplsData,
+                                                       VplsOperation.Operation.REMOVE);
+        operations.add(vplsOperation);
+        vplsData = VplsData.of(VPLS1, EncapsulationType.VLAN);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2));
+        vplsOperation = VplsOperation.of(vplsData,
+                                         VplsOperation.Operation.UPDATE);
+        operations.add(vplsOperation);
+        vplsOperation = VplsOperationManager.getOptimizedVplsOperation(operations);
+        assertEquals(VplsOperation.of(vplsData, VplsOperation.Operation.UPDATE), vplsOperation);
+    }
+
+    /**
+     * Optimize operations with first is UPDATE operation and last is ADD
+     * operation.
+     */
+    @Test
+    public void testOptimizeOperationsUToA() {
+        Deque<VplsOperation> operations = new ArrayDeque<>();
+        VplsData vplsData = VplsData.of(VPLS1);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1));
+        VplsOperation vplsOperation = VplsOperation.of(vplsData,
+                                                       VplsOperation.Operation.UPDATE);
+        operations.add(vplsOperation);
+        vplsData = VplsData.of(VPLS1, EncapsulationType.VLAN);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2));
+        vplsOperation = VplsOperation.of(vplsData,
+                                         VplsOperation.Operation.ADD);
+        operations.add(vplsOperation);
+        vplsOperation = VplsOperationManager.getOptimizedVplsOperation(operations);
+        assertEquals(VplsOperation.of(vplsData, VplsOperation.Operation.UPDATE), vplsOperation);
+    }
+
+    /**
+     * Optimize operations with first is UPDATE operation and last is REMOVE
+     * operation.
+     */
+    @Test
+    public void testOptimizeOperationsUToR() {
+        Deque<VplsOperation> operations = new ArrayDeque<>();
+        VplsData vplsData = VplsData.of(VPLS1);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1));
+        VplsOperation vplsOperation = VplsOperation.of(vplsData,
+                                                       VplsOperation.Operation.UPDATE);
+        operations.add(vplsOperation);
+        vplsData = VplsData.of(VPLS1, EncapsulationType.VLAN);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2));
+        vplsOperation = VplsOperation.of(vplsData,
+                                         VplsOperation.Operation.REMOVE);
+        operations.add(vplsOperation);
+        vplsOperation = VplsOperationManager.getOptimizedVplsOperation(operations);
+        assertEquals(VplsOperation.of(vplsData, VplsOperation.Operation.REMOVE), vplsOperation);
+    }
+
+    /**
+     * Optimize operations with first is UPDATE operation and last is also
+     * UPDATE operation.
+     */
+    @Test
+    public void testOptimizeOperationsUToU() {
+        Deque<VplsOperation> operations = new ArrayDeque<>();
+        VplsData vplsData = VplsData.of(VPLS1);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1));
+        VplsOperation vplsOperation = VplsOperation.of(vplsData,
+                                                       VplsOperation.Operation.UPDATE);
+        operations.add(vplsOperation);
+        vplsData = VplsData.of(VPLS1, EncapsulationType.VLAN);
+        vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2));
+        vplsOperation = VplsOperation.of(vplsData,
+                                         VplsOperation.Operation.UPDATE);
+        operations.add(vplsOperation);
+        vplsOperation = VplsOperationManager.getOptimizedVplsOperation(operations);
+        assertEquals(VplsOperation.of(vplsData, VplsOperation.Operation.UPDATE), vplsOperation);
+    }
+
+    /**
+     * Test Intent service which always fail when submit or withdraw Intents.
+     */
+    class AlwaysFailureIntentService extends TestIntentService {
+        @Override
+        public void submit(Intent intent) {
+            intents.add(new IntentData(intent, IntentState.FAILED, new WallClockTimestamp()));
+            if (listener != null) {
+                IntentEvent event = IntentEvent.getEvent(IntentState.FAILED, intent).get();
+                listener.event(event);
+            }
+        }
+
+        @Override
+        public void withdraw(Intent intent) {
+            intents.forEach(intentData -> {
+                if (intentData.intent().key().equals(intent.key())) {
+                    intentData.setState(IntentState.FAILED);
+
+                    if (listener != null) {
+                        IntentEvent event = IntentEvent.getEvent(IntentState.FAILED, intent).get();
+                        listener.event(event);
+                    }
+                }
+            });
+        }
+    }
+
+    /**
+     * Test host service without any hosts.
+     */
+    class EmptyHostService extends HostServiceAdapter {
+        @Override
+        public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
+            return ImmutableSet.of();
+        }
+    }
+
+}
diff --git a/apps/vpls/src/test/java/org/onosproject/vpls/VplsTest.java b/apps/vpls/src/test/java/org/onosproject/vpls/VplsTest.java
index 6dfcea5..716b331 100644
--- a/apps/vpls/src/test/java/org/onosproject/vpls/VplsTest.java
+++ b/apps/vpls/src/test/java/org/onosproject/vpls/VplsTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016-present Open Networking Laboratory
+ * 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.
@@ -13,704 +13,213 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.onosproject.vpls;
 
-import com.google.common.collect.HashMultimap;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSetMultimap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import com.google.common.collect.SetMultimap;
 import com.google.common.collect.Sets;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
 import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onosproject.TestApplicationId;
-import org.onosproject.app.ApplicationService;
+import org.onosproject.cluster.LeadershipEvent;
+import org.onosproject.cluster.LeadershipEventListener;
+import org.onosproject.cluster.LeadershipServiceAdapter;
+import org.onosproject.cluster.NodeId;
 import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
+import org.onosproject.core.CoreServiceAdapter;
 import org.onosproject.core.IdGenerator;
 import org.onosproject.incubator.net.intf.Interface;
 import org.onosproject.incubator.net.intf.InterfaceListener;
 import org.onosproject.incubator.net.intf.InterfaceService;
-import org.onosproject.intentsync.IntentSynchronizationService;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DefaultHost;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.EncapsulationType;
-import org.onosproject.net.FilteredConnectPoint;
 import org.onosproject.net.Host;
 import org.onosproject.net.HostId;
 import org.onosproject.net.HostLocation;
 import org.onosproject.net.PortNumber;
-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.config.Config;
+import org.onosproject.net.config.ConfigApplyDelegate;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigServiceAdapter;
 import org.onosproject.net.host.HostEvent;
 import org.onosproject.net.host.HostListener;
-import org.onosproject.net.host.HostService;
 import org.onosproject.net.host.HostServiceAdapter;
 import org.onosproject.net.intent.Intent;
-import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentEvent;
+import org.onosproject.net.intent.IntentListener;
 import org.onosproject.net.intent.IntentServiceAdapter;
-import org.onosproject.net.intent.IntentUtils;
-import org.onosproject.net.intent.Key;
-import org.onosproject.net.intent.MultiPointToSinglePointIntent;
-import org.onosproject.net.intent.SinglePointToMultiPointIntent;
+import org.onosproject.net.intent.IntentState;
 import org.onosproject.net.provider.ProviderId;
-import org.onosproject.vpls.config.VplsConfigService;
+import org.onosproject.store.StoreDelegate;
+import org.onosproject.store.service.WallClockTimestamp;
+import org.onosproject.vpls.api.Vpls;
+import org.onosproject.vpls.api.VplsData;
+import org.onosproject.vpls.config.VplsAppConfig;
+import org.onosproject.vpls.config.VplsAppConfigTest;
+import org.onosproject.vpls.config.VplsConfig;
+import org.onosproject.vpls.store.VplsStoreAdapter;
+import org.onosproject.vpls.store.VplsStoreEvent;
 
+import java.io.IOException;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.stream.Collectors;
 
-import static java.lang.String.format;
-import static org.easymock.EasyMock.anyObject;
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.expectLastCall;
-import static org.easymock.EasyMock.replay;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.onosproject.net.EncapsulationType.NONE;
-import static org.onosproject.net.EncapsulationType.VLAN;
-import static org.onosproject.net.EncapsulationType.valueOf;
-import static org.onosproject.vpls.IntentInstaller.PARTIAL_FAILURE_CONSTRAINT;
-import static org.onosproject.vpls.IntentInstaller.PREFIX_BROADCAST;
-import static org.onosproject.vpls.IntentInstaller.PREFIX_UNICAST;
-import static org.onosproject.vpls.IntentInstaller.setEncap;
-
 /**
- * Tests for the {@link Vpls} class.
+ * Class provides data for VPLS testing.
  */
-public class VplsTest {
-    private static final String APP_NAME = "org.onosproject.vpls";
-    private static final ApplicationId APPID = TestApplicationId.create(APP_NAME);
-    private static final String DASH = "-";
-    private static final int PRIORITY_OFFSET = 1000;
-    private static final String VPLS1 = "vpls1";
-    private static final String VPLS2 = "vpls2";
-    private static final String VPLS3 = "vpls3";
-    private static final String VPLS4 = "vpls4";
+public abstract class VplsTest {
+    protected static final String APP_NAME = "org.onosproject.vpls";
+    protected static final ApplicationId APPID = TestApplicationId.create(APP_NAME);
+    protected static final String DASH = "-";
+    protected static final int PRIORITY_OFFSET = 1000;
+    protected static final String VPLS1 = "vpls1";
+    protected static final String VPLS2 = "vpls2";
+    protected static final String VPLS3 = "vpls3";
+    protected static final String VPLS4 = "vpls4";
 
-    private static final PortNumber P1 = PortNumber.portNumber(1);
-    private static final PortNumber P2 = PortNumber.portNumber(2);
+    protected static final PortNumber P1 = PortNumber.portNumber(1);
+    protected static final PortNumber P2 = PortNumber.portNumber(2);
 
-    private static final DeviceId DID1 = getDeviceId(1);
-    private static final DeviceId DID2 = getDeviceId(2);
-    private static final DeviceId DID3 = getDeviceId(3);
-    private static final DeviceId DID4 = getDeviceId(4);
-    private static final DeviceId DID5 = getDeviceId(5);
-    private static final DeviceId DID6 = getDeviceId(6);
+    protected static final DeviceId DID1 = getDeviceId(1);
+    protected static final DeviceId DID2 = getDeviceId(2);
+    protected static final DeviceId DID3 = getDeviceId(3);
+    protected static final DeviceId DID4 = getDeviceId(4);
+    protected static final DeviceId DID5 = getDeviceId(5);
+    protected static final DeviceId DID6 = getDeviceId(6);
 
-    private static final ConnectPoint CP1 = new ConnectPoint(DID1, P1);
-    private static final ConnectPoint CP2 = new ConnectPoint(DID2, P1);
-    private static final ConnectPoint CP3 = new ConnectPoint(DID3, P1);
-    private static final ConnectPoint CP4 = new ConnectPoint(DID4, P1);
-    private static final ConnectPoint CP5 = new ConnectPoint(DID5, P1);
-    private static final ConnectPoint CP6 = new ConnectPoint(DID6, P1);
-    private static final ConnectPoint CP7 = new ConnectPoint(DID4, P2);
-    private static final ConnectPoint CP8 = new ConnectPoint(DID3, P2);
-    private static final ConnectPoint CP9 = new ConnectPoint(DID5, P1);
-    private static final ConnectPoint CP10 = new ConnectPoint(DID5, P2);
+    protected static final ConnectPoint CP1 = new ConnectPoint(DID1, P1);
+    protected static final ConnectPoint CP2 = new ConnectPoint(DID2, P1);
+    protected static final ConnectPoint CP3 = new ConnectPoint(DID3, P1);
+    protected static final ConnectPoint CP4 = new ConnectPoint(DID4, P1);
+    protected static final ConnectPoint CP5 = new ConnectPoint(DID5, P1);
+    protected static final ConnectPoint CP6 = new ConnectPoint(DID6, P1);
+    protected static final ConnectPoint CP7 = new ConnectPoint(DID4, P2);
+    protected static final ConnectPoint CP8 = new ConnectPoint(DID3, P2);
+    protected static final ConnectPoint CP9 = new ConnectPoint(DID5, P1);
+    protected static final ConnectPoint CP10 = new ConnectPoint(DID5, P2);
 
-    private static final VlanId VLAN100 = VlanId.vlanId((short) 100);
-    private static final VlanId VLAN200 = VlanId.vlanId((short) 200);
-    private static final VlanId VLAN300 = VlanId.vlanId((short) 300);
-    private static final VlanId VLAN400 = VlanId.vlanId((short) 400);
-    private static final VlanId VLAN_NONE = VlanId.NONE;
+    protected static final VlanId VLAN100 = VlanId.vlanId((short) 100);
+    protected static final VlanId VLAN200 = VlanId.vlanId((short) 200);
+    protected static final VlanId VLAN300 = VlanId.vlanId((short) 300);
+    protected static final VlanId VLAN400 = VlanId.vlanId((short) 400);
+    protected static final VlanId VLAN_NONE = VlanId.NONE;
 
-    private static final MacAddress MAC1 = getMac(1);
-    private static final MacAddress MAC2 = getMac(2);
-    private static final MacAddress MAC3 = getMac(3);
-    private static final MacAddress MAC4 = getMac(4);
-    private static final MacAddress MAC5 = getMac(5);
-    private static final MacAddress MAC6 = getMac(6);
-    private static final MacAddress MAC7 = getMac(7);
-    private static final MacAddress MAC8 = getMac(8);
-    private static final MacAddress MAC9 = getMac(9);
-    private static final MacAddress MAC10 = getMac(10);
-    private static final MacAddress MAC11 = getMac(11);
+    protected static final MacAddress MAC1 = getMac(1);
+    protected static final MacAddress MAC2 = getMac(2);
+    protected static final MacAddress MAC3 = getMac(3);
+    protected static final MacAddress MAC4 = getMac(4);
+    protected static final MacAddress MAC5 = getMac(5);
+    protected static final MacAddress MAC6 = getMac(6);
+    protected static final MacAddress MAC7 = getMac(7);
+    protected static final MacAddress MAC8 = getMac(8);
+    protected static final MacAddress MAC9 = getMac(9);
+    protected static final MacAddress MAC10 = getMac(10);
+    protected static final MacAddress MAC11 = getMac(11);
 
-    private static final Ip4Address IP1 = Ip4Address.valueOf("192.168.1.1");
-    private static final Ip4Address IP2 = Ip4Address.valueOf("192.168.1.2");
+    protected static final Ip4Address IP1 = Ip4Address.valueOf("192.168.1.1");
+    protected static final Ip4Address IP2 = Ip4Address.valueOf("192.168.1.2");
 
-    private static final HostId HID1 = HostId.hostId(MAC1, VLAN100);
-    private static final HostId HID2 = HostId.hostId(MAC2, VLAN100);
-    private static final HostId HID3 = HostId.hostId(MAC3, VLAN200);
-    private static final HostId HID4 = HostId.hostId(MAC4, VLAN200);
-    private static final HostId HID5 = HostId.hostId(MAC5, VLAN300);
-    private static final HostId HID6 = HostId.hostId(MAC6, VLAN300);
-    private static final HostId HID7 = HostId.hostId(MAC7, VLAN300);
-    private static final HostId HID8 = HostId.hostId(MAC8, VLAN400);
-    private static final HostId HID9 = HostId.hostId(MAC9);
-    private static final HostId HID10 = HostId.hostId(MAC10);
-    private static final HostId HID11 = HostId.hostId(MAC11);
+    protected static final HostId HID1 = HostId.hostId(MAC1, VLAN100);
+    protected static final HostId HID2 = HostId.hostId(MAC2, VLAN100);
+    protected static final HostId HID3 = HostId.hostId(MAC3, VLAN200);
+    protected static final HostId HID4 = HostId.hostId(MAC4, VLAN200);
+    protected static final HostId HID5 = HostId.hostId(MAC5, VLAN300);
+    protected static final HostId HID6 = HostId.hostId(MAC6, VLAN300);
+    protected static final HostId HID7 = HostId.hostId(MAC7, VLAN300);
+    protected static final HostId HID8 = HostId.hostId(MAC8, VLAN400);
+    protected static final HostId HID9 = HostId.hostId(MAC9);
+    protected static final HostId HID10 = HostId.hostId(MAC10);
+    protected static final HostId HID11 = HostId.hostId(MAC11);
 
-    private static final ProviderId PID = new ProviderId("of", "foo");
+    protected static final ProviderId PID = new ProviderId("of", "foo");
 
-    private static IdGenerator idGenerator;
+    protected static final NodeId NODE_ID_1 = new NodeId("Node1");
+    protected static final NodeId NODE_ID_2 = new NodeId("Node2");
 
-    private static final Interface V100H1 =
+    protected static IdGenerator idGenerator;
+
+    protected static final Interface V100H1 =
             new Interface("v100h1", CP1, null, null, VLAN100);
-    private static final Interface V100H2 =
+    protected static final Interface V100H2 =
             new Interface("v100h2", CP2, null, null, VLAN100);
-    private static final Interface V200H1 =
+    protected static final Interface V200H1 =
             new Interface("v200h1", CP3, null, null, VLAN200);
-    private static final Interface V200H2 =
+    protected static final Interface V200H2 =
             new Interface("v200h2", CP4, null, null, VLAN200);
-    private static final Interface V300H1 =
+    protected static final Interface V300H1 =
             new Interface("v300h1", CP5, null, null, VLAN300);
-    private static final Interface V300H2 =
+    protected static final Interface V300H2 =
             new Interface("v300h2", CP6, null, null, VLAN300);
-    private static final Interface V400H1 =
+    protected static final Interface V400H1 =
             new Interface("v400h1", CP7, null, null, VLAN400);
 
-    private static final Interface VNONEH1 =
+    protected static final Interface VNONEH1 =
             new Interface("vNoneh1", CP8, null, null, VLAN_NONE);
-    private static final Interface VNONEH2 =
+    protected static final Interface VNONEH2 =
             new Interface("vNoneh2", CP9, null, null, VLAN_NONE);
-    private static final Interface VNONEH3 =
+    protected static final Interface VNONEH3 =
             new Interface("vNoneh3", CP10, null, null, VLAN_NONE);
 
-    private static final Host V100HOST1 =
+    protected static final Host V100HOST1 =
             new DefaultHost(PID, HID1, MAC1, VLAN100,
                             getLocation(1), Collections.singleton(IP1));
-    private static final Host V100HOST2 =
+    protected static final Host V100HOST2 =
             new DefaultHost(PID, HID2, MAC2, VLAN100,
                             getLocation(2), Sets.newHashSet());
-    private static final Host V200HOST1 =
+    protected static final Host V200HOST1 =
             new DefaultHost(PID, HID3, MAC3, VLAN200,
                             getLocation(3), Collections.singleton(IP2));
-    private static final Host V200HOST2 =
+    protected static final Host V200HOST2 =
             new DefaultHost(PID, HID4, MAC4, VLAN200,
                             getLocation(4), Sets.newHashSet());
-    private static final Host V300HOST1 =
+    protected static final Host V300HOST1 =
             new DefaultHost(PID, HID5, MAC5, VLAN300,
                             getLocation(5), Sets.newHashSet());
-    private static final Host V300HOST2 =
+    protected static final Host V300HOST2 =
             new DefaultHost(PID, HID6, MAC6, VLAN300,
                             getLocation(6), Sets.newHashSet());
-    private static final Host V300HOST3 =
+    protected static final Host V300HOST3 =
             new DefaultHost(PID, HID7, MAC7, VLAN300,
                             getLocation(7), Sets.newHashSet());
-    private static final Host V400HOST1 =
+    protected static final Host V400HOST1 =
             new DefaultHost(PID, HID8, MAC8, VLAN400,
                             getLocation(4, 2), Sets.newHashSet());
 
-    private static final Host VNONEHOST1 =
+    protected static final Host VNONEHOST1 =
             new DefaultHost(PID, HID9, MAC9, VlanId.NONE,
                             getLocation(3, 2), Sets.newHashSet());
-    private static final Host VNONEHOST2 =
+    protected static final Host VNONEHOST2 =
             new DefaultHost(PID, HID10, MAC10, VlanId.NONE,
                             getLocation(5, 1), Sets.newHashSet());
-    private static final Host VNONEHOST3 =
+    protected static final Host VNONEHOST3 =
             new DefaultHost(PID, HID11, MAC11, VlanId.NONE,
                             getLocation(5, 2), Sets.newHashSet());
 
-    private static final Set<Interface> AVAILABLE_INTERFACES =
+    protected static final Set<Interface> AVAILABLE_INTERFACES =
             ImmutableSet.of(V100H1, V100H2, V200H1, V200H2, V300H1, V300H2,
-                            V400H1, VNONEH1, VNONEH2);
+                            V400H1, VNONEH1, VNONEH2, VNONEH3);
 
-    private static final Set<Host> AVAILABLE_HOSTS =
+    protected static final Set<Host> AVAILABLE_HOSTS =
             ImmutableSet.of(V100HOST1, V100HOST2, V200HOST1,
                             V200HOST2, V300HOST1, V300HOST2, V300HOST3,
                             VNONEHOST1, VNONEHOST2,
                             V400HOST1, VNONEHOST3);
 
-    private SetMultimap<String, Interface> interfacesByVpls = HashMultimap.create();
-
-    private ApplicationService applicationService;
-    private CoreService coreService;
-    private HostListener hostListener;
-    private NetworkConfigService configService;
-    private Set<Host> hostsAvailable;
-    private HostService hostService;
-    private IntentService intentService;
-    private InterfaceService interfaceService;
-    private VplsConfigService vplsConfigService;
-    private Vpls vpls;
-
-    @Before
-    public void setUp() throws Exception {
-        idGenerator = new TestIdGenerator();
-        Intent.unbindIdGenerator(idGenerator);
-        Intent.bindIdGenerator(idGenerator);
-
-        applicationService = createMock(ApplicationService.class);
-
-        configService = createMock(NetworkConfigService.class);
-
-        coreService = createMock(CoreService.class);
-        expect(coreService.registerApplication(APP_NAME))
-                .andReturn(APPID);
-        replay(coreService);
-
-        hostsAvailable = Sets.newHashSet();
-        hostService = new TestHostService(hostsAvailable);
-
-        intentService = new TestIntentService();
-
-        TestIntentSynchronizer intentSynchronizer =
-                new TestIntentSynchronizer(intentService);
-
-        interfaceService = createMock(InterfaceService.class);
-        interfaceService.addListener(anyObject(InterfaceListener.class));
-        expectLastCall().anyTimes();
-        addIfaceConfig();
-
-        interfacesByVpls.put(VPLS1, V100H1);
-        interfacesByVpls.put(VPLS1, V200H1);
-        interfacesByVpls.put(VPLS1, V300H1);
-        interfacesByVpls.put(VPLS2, V100H2);
-        interfacesByVpls.put(VPLS2, V200H2);
-        interfacesByVpls.put(VPLS2, V300H2);
-        interfacesByVpls.put(VPLS3, VNONEH1);
-        interfacesByVpls.put(VPLS3, VNONEH2);
-        interfacesByVpls.put(VPLS4, V400H1);
-        interfacesByVpls.put(VPLS4, VNONEH3);
-
-        Map<String, EncapsulationType> encapByVpls = new HashMap<>();
-        encapByVpls.put(VPLS1, VLAN);
-        encapByVpls.put(VPLS2, NONE);
-        encapByVpls.put(VPLS3, NONE);
-        encapByVpls.put(VPLS4, NONE);
-
-        vplsConfigService = new TestVplsConfigService(interfacesByVpls, encapByVpls);
-
-        vpls = new Vpls();
-        vpls.applicationService = applicationService;
-        vpls.coreService = coreService;
-        vpls.hostService = hostService;
-        vpls.vplsConfigService = vplsConfigService;
-        vpls.intentService = intentService;
-        vpls.interfaceService = interfaceService;
-        vpls.configService = configService;
-        vpls.intentSynchronizer = intentSynchronizer;
-
-    }
-
-    @After
-    public void tearDown() {
-        Intent.unbindIdGenerator(idGenerator);
-    }
-
-    /**
-     * Creates the interface configuration:
-     *  On devices 1 and 2 is configured an interface on port 1 with vlan 100.
-     *  On device 3 is configured an interface on port 3 with no vlan.
-     *  On devices 3 and 4 is configured an interface on port 1 with vlan 200.
-     *  On device 4 is an interface configured on port 2 with vlan 400.
-     *  On device 5 are configured two interfaces on port 1 and 2 with no vlan.
-     *  On device 5 and 6 is configured an interface on port 1 with vlan 300.
-     */
-    private void addIfaceConfig() {
-        Set<Interface> interfaces = ImmutableSet.copyOf(AVAILABLE_INTERFACES);
-        Set<Interface> vlanOneSet = ImmutableSet.of(V100H1, V100H2);
-        Set<Interface> vlanTwoSet = ImmutableSet.of(V200H1, V200H2);
-        Set<Interface> vlanThreeSet = ImmutableSet.of(VNONEH1, VNONEH2);
-        Set<Interface> vlanFourSet = ImmutableSet.of(V400H1, VNONEH3);
-
-        AVAILABLE_INTERFACES.forEach(intf -> {
-            expect(interfaceService.getInterfacesByPort(intf.connectPoint()))
-                    .andReturn(Sets.newHashSet(intf)).anyTimes();
-        });
-        expect(interfaceService.getInterfacesByVlan(VLAN100))
-                .andReturn(vlanOneSet).anyTimes();
-        expect(interfaceService.getInterfacesByVlan(VLAN200))
-                .andReturn(vlanTwoSet).anyTimes();
-        expect(interfaceService.getInterfacesByVlan(VLAN300))
-                .andReturn(vlanThreeSet).anyTimes();
-        expect(interfaceService.getInterfacesByVlan(VLAN400))
-                .andReturn(vlanFourSet).anyTimes();
-        expect(interfaceService.getInterfacesByVlan(VlanId.NONE))
-                .andReturn(vlanFourSet).anyTimes();
-        expect(interfaceService.getInterfaces()).andReturn(interfaces).anyTimes();
-
-        replay(interfaceService);
-    }
-
-    /**
-     * Seven ports are configured with VLANs, while three ports are not. No hosts are
-     * registered by the HostService.
-     *
-     * The first three ports have an interface configured on VPLS 1,
-     * the other three on VPLS 2. Two ports are defined for VPLS 3, while
-     * the two remaining ports are configured on VPLS 4.
-     *
-     * The number of intents expected is 10: three for VPLS 1, three for VPLS 2,
-     * two for VPLS 3, two for VPLS 4. Eight MP2SP intents.
-     * Checks if the number of intents submitted to the intent framework is
-     * equal to the number of intents expected and if all intents are equivalent.
-     */
-    @Test
-    public void activateNoHosts() {
-        vpls.activate();
-
-        List<Intent> expectedIntents = Lists.newArrayList();
-        Set<FilteredConnectPoint> fcPoints;
-
-        fcPoints = buildFCPoints(ImmutableSet.of(V100H1, V200H1, V300H1));
-        expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS1, VLAN));
-
-        fcPoints = buildFCPoints(ImmutableSet.of(V100H2, V200H2, V300H2));
-        expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS2, NONE));
-
-        fcPoints = buildFCPoints(ImmutableSet.of(VNONEH1, VNONEH2));
-        expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS3, NONE));
-
-        fcPoints = buildFCPoints(ImmutableSet.of(V400H1, VNONEH3));
-        expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS4, NONE));
-
-        checkIntents(expectedIntents);
-    }
-
-    /**
-     * Ten ports are configured with VLANs and ten hosts are registered by the
-     * HostService.
-     *
-     * The first three ports have an interface configured on VPLS 1,
-     * the other three on VPLS 2, two on VPLS3 and two on VPLS4.
-     *
-     * The number of intents expected is twenty: six
-     * for VPLS 1, six for VPLS 2. four for VPLS 3, four for VPLS 4.
-     * That is ten sp2mp intents, ten mp2sp intents. For VPLS 1
-     * IPs are added to demonstrate this doesn't influence the number of intents
-     * created. Checks if the number of intents submitted to the intent
-     * framework is equal to the number of intents expected and if all intents
-     * are equivalent.
-     */
-    @Test
-    public void tenInterfacesConfiguredHostsPresent() {
-        hostsAvailable.addAll(AVAILABLE_HOSTS);
-
-        vpls.activate();
-
-        List<Intent> expectedIntents = Lists.newArrayList();
-        Set<FilteredConnectPoint> fcPoints;
-        Set<Host> hosts;
-
-        fcPoints = buildFCPoints(ImmutableSet.of(V100H1, V200H1, V300H1));
-        hosts = ImmutableSet.of(V100HOST1, V200HOST1, V300HOST1);
-        expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS1, VLAN));
-        expectedIntents.addAll(generateVplsUni(fcPoints, hosts, VPLS1, VLAN));
-
-        fcPoints = buildFCPoints(ImmutableSet.of(V100H2, V200H2, V300H2));
-        hosts = ImmutableSet.of(V100HOST2, V200HOST2, V300HOST2);
-        expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS2, NONE));
-        expectedIntents.addAll(generateVplsUni(fcPoints, hosts, VPLS2, NONE));
-
-        fcPoints = buildFCPoints(ImmutableSet.of(VNONEH1, VNONEH2));
-        hosts = ImmutableSet.of(VNONEHOST1, VNONEHOST2);
-        expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS3, NONE));
-        expectedIntents.addAll(generateVplsUni(fcPoints, hosts, VPLS3, NONE));
-
-        fcPoints = buildFCPoints(ImmutableSet.of(V400H1, VNONEH3));
-        hosts = ImmutableSet.of(V400HOST1, VNONEHOST3);
-        expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS4, NONE));
-        expectedIntents.addAll(generateVplsUni(fcPoints, hosts, VPLS4, NONE));
-
-        checkIntents(expectedIntents);
-    }
-
-    /**
-     * Ten ports are configured; seven have VLANs and three do not.
-     * Initially, no hosts are registered by the HostService.
-     *
-     * The first three ports have an interface configured on
-     * VPLS 1, three have an interface configured on VPLS 2, two have an
-     * interface configured on VPLS 3 and two have an interface configured
-     * on VPLS 4, three have an interface configure. When the
-     * module starts up, three hosts attached to device one, two and three -
-     * port 1, are registered by the HostService and events are sent to the
-     * application. sp2mp intents are created for all interfaces configured and
-     * mp2sp intents are created only for the hosts attached.
-     *
-     * The number of intents expected is seventeen: six for VPLS 1,
-     * three for VPLS 2, four for VPLS3 and four for VPLS4.
-     * Ten sp2mp intents, seven mp2sp intents. IPs are added on the first two
-     * hosts only to demonstrate this doesn't influence the number of intents
-     * created.
-     * An additional host is added on device seven - port 1, to demonstrate that
-     * the application does not generate intents, even if the interface uses the
-     * same VLAN Id of the other interfaces configured for the specifc VPLS.
-     * Checks if the number of intents submitted to the intent framework is equal
-     * to the number of intents expected and if all intents are equivalent.
-     */
-    @Test
-    public void tenInterfacesThreeHostEventsSameVpls() {
-        vpls.activate();
-
-        List<Intent> expectedIntents = Lists.newArrayList();
-        Set<FilteredConnectPoint> fcPoints;
-        Set<Host> hosts;
-
-        hostsAvailable.addAll(Sets.newHashSet(V100HOST1, V200HOST1,
-                                              V300HOST1, V300HOST3,
-                                              VNONEHOST1, VNONEHOST2,
-                                              V400HOST1, VNONEHOST3));
-        hostsAvailable.forEach(host ->
-                                       hostListener.event(new HostEvent(HostEvent.Type.HOST_ADDED, host)));
-
-        fcPoints = buildFCPoints(ImmutableSet.of(V100H1, V200H1, V300H1));
-        hosts = ImmutableSet.of(V100HOST1, V200HOST1, V300HOST1);
-        expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS1, VLAN));
-        expectedIntents.addAll(generateVplsUni(fcPoints, hosts, VPLS1, VLAN));
-
-        fcPoints = buildFCPoints(ImmutableSet.of(V100H2, V200H2, V300H2));
-        expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS2, NONE));
-
-        fcPoints = buildFCPoints(ImmutableSet.of(VNONEH1, VNONEH2));
-        hosts = ImmutableSet.of(VNONEHOST1, VNONEHOST2);
-        expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS3, NONE));
-        expectedIntents.addAll(generateVplsUni(fcPoints, hosts, VPLS3, NONE));
-
-        fcPoints = buildFCPoints(ImmutableSet.of(V400H1, VNONEH3));
-        hosts = ImmutableSet.of(V400HOST1, VNONEHOST3);
-        expectedIntents.addAll(generateVplsBrc(fcPoints, VPLS4, NONE));
-        expectedIntents.addAll(generateVplsUni(fcPoints, hosts, VPLS4, NONE));
-
-        checkIntents(expectedIntents);
-    }
-
-    /**
-     * Generates a list of the expected sp2mp intents for a VPLS.
-     *
-     * @param fcPoints the filtered connect point
-     * @param name the name of the VPLS
-     * @param encap the encapsulation type
-     * @return the list of expected sp2mp intents for the given VPLS
-     */
-    private List<SinglePointToMultiPointIntent>
-    generateVplsBrc(Set<FilteredConnectPoint> fcPoints, String name, EncapsulationType encap) {
-        List<SinglePointToMultiPointIntent> intents = Lists.newArrayList();
-
-        fcPoints.forEach(point -> {
-            Set<FilteredConnectPoint> otherPoints =
-                    fcPoints.stream()
-                            .filter(fcp -> !fcp.equals(point))
-                            .collect(Collectors.toSet());
-
-            Key brckey = buildKey(PREFIX_BROADCAST,
-                                  point.connectPoint(),
-                                  name,
-                                  MacAddress.BROADCAST);
-
-            intents.add(buildBrcIntent(brckey, point, otherPoints, encap));
-        });
-
-        return intents;
-    }
-
-    /**
-     * Generates a list of expected mp2sp intents for a given VPLS.
-     *
-     * @param fcPoints the filtered connect point
-     * @param hosts the hosts
-     * @param name the name of the VPLS
-     * @param encap the encapsulation type
-     * @return the list of expected mp2sp intents for the given VPLS
-     */
-    private List<MultiPointToSinglePointIntent>
-    generateVplsUni(Set<FilteredConnectPoint> fcPoints, Set<Host> hosts,
-                    String name, EncapsulationType encap) {
-        List<MultiPointToSinglePointIntent> intents = Lists.newArrayList();
-
-        hosts.forEach(host -> {
-            FilteredConnectPoint hostPoint = getHostPoint(host, fcPoints);
-
-            Set<FilteredConnectPoint> otherPoints =
-                    fcPoints.stream()
-                            .filter(fcp -> !fcp.equals(hostPoint))
-                            .collect(Collectors.toSet());
-
-            Key uniKey = buildKey(PREFIX_UNICAST,
-                                  host.location(), name, host.mac());
-
-            intents.add(buildUniIntent(uniKey, otherPoints, hostPoint, host, encap));
-        });
-
-        return intents;
-    }
-
-    /**
-     * Checks if the number of intents submitted to the intent framework is equal
-     * to the number of intents expected and if all intents are equivalent.
-     *
-     * @param intents the list of intents expected
-     */
-    private void checkIntents(List<Intent> intents) {
-        assertEquals("The number of intents submitted differs from the number" +
-                             " of intents expected. ",
-                     intents.size(), intentService.getIntentCount());
-
-        for (Intent intentOne : intents) {
-            boolean found = false;
-            for (Intent intentTwo : intentService.getIntents()) {
-                if (intentOne.key().equals(intentTwo.key())) {
-                    found = true;
-                    assertTrue(format("The intent submitted is different from" +
-                                              " the intent expected. %s %s",
-                                      intentOne, intentTwo),
-                               IntentUtils.intentsAreEqual(intentOne, intentTwo));
-                    break;
-                }
-            }
-            assertTrue("The intent submitted is not equal to any of the expected" +
-                               " intents. ", found);
-        }
-    }
-
-    /**
-     * Builds a broadcast intent.
-     *
-     * @param key the key to identify the intent
-     * @param src the ingress connect point
-     * @param dsts the egress connect points
-     * @return the generated single-point to multi-point intent
-     */
-    private SinglePointToMultiPointIntent buildBrcIntent(Key key,
-                                                         FilteredConnectPoint src,
-                                                         Set<FilteredConnectPoint> dsts,
-                                                         EncapsulationType encap) {
-        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);
-
-        setEncap(intentBuilder, PARTIAL_FAILURE_CONSTRAINT, encap);
-
-        return intentBuilder.build();
-    }
-
-    /**
-     * Builds a unicast intent.
-     *
-     * @param key the key to identify the intent
-     * @param srcs the ingress connect points
-     * @param dst the egress connect point
-     * @param host the destination Host
-     * @return the generated multi-point to single-point intent
-     */
-    private MultiPointToSinglePointIntent buildUniIntent(Key key,
-                                                         Set<FilteredConnectPoint> srcs,
-                                                         FilteredConnectPoint dst,
-                                                         Host host,
-                                                         EncapsulationType encap) {
-        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);
-
-        setEncap(intentBuilder, PARTIAL_FAILURE_CONSTRAINT, encap);
-
-        return intentBuilder.build();
-    }
-
-    /**
-     * Returns the filtered connect point associated to a given host.
-     *
-     * @param host the target host
-     * @param fcps the filtered connected points
-     * @return the filtered connect point associated to the given host; null
-     * 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 VPLS
-        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());
-    }
-
-    /**
-     * Builds an intent Key either for a single-point to multi-point or
-     * multi-point to single-point intent, based on a prefix that defines
-     * the intent type, the connection point representing the source or the
-     * destination and the VLAN Id representing the VPLS.
-     *
-     * @param prefix the key prefix
-     * @param cPoint the ingress/egress connect point
-     * @param vplsName the VPLS name
-     * @param hostMac the ingress/egress MAC address
-     * @return the key to identify the intent
-     */
-    private Key buildKey(String prefix,
-                         ConnectPoint cPoint,
-                         String vplsName,
-                         MacAddress hostMac) {
-        String keyString = vplsName +
-                DASH +
-                prefix +
-                DASH +
-                cPoint.deviceId() +
-                DASH +
-                cPoint.port() +
-                DASH +
-                hostMac;
-
-        return Key.of(keyString, APPID);
-    }
 
     /**
      * Returns the device Id of the ith device.
@@ -718,38 +227,91 @@
      * @param i the device to get the Id of
      * @return the device Id
      */
-    private static DeviceId getDeviceId(int i) {
+    protected static DeviceId getDeviceId(int i) {
         return DeviceId.deviceId("" + i);
     }
 
-    private static MacAddress getMac(int n) {
+    /**
+     * Generates a mac address by given number.
+     *
+     * @param n the number to generate mac address
+     * @return the mac address
+     */
+    protected static MacAddress getMac(int n) {
         return MacAddress.valueOf(String.format("00:00:00:00:00:%s", n));
     }
 
-    private static HostLocation getLocation(int i) {
+    /**
+     * Generates a host location by given device number.
+     *
+     * @param i the given number
+     * @return the host location
+     */
+    protected static HostLocation getLocation(int i) {
         return new HostLocation(new ConnectPoint(getDeviceId(i), P1), 123L);
     }
 
-    private static HostLocation getLocation(int d, int p) {
+    /**
+     * Generates host location by given device number and port number.
+     *
+     * @param d the device number
+     * @param p the port number
+     * @return the host location
+     */
+    protected static HostLocation getLocation(int d, int p) {
         return new HostLocation(new ConnectPoint(getDeviceId(d),
                                                  PortNumber.portNumber(p)), 123L);
     }
 
     /**
-     * Represents a fake IntentService class that allows to store and retrieve
-     * intents without implementing the IntentService logic.
+     * Test core service; For generate test application ID.
      */
-    private class TestIntentService extends IntentServiceAdapter {
+    public class TestCoreService extends CoreServiceAdapter {
+        @Override
+        public ApplicationId registerApplication(String name) {
+            return TestApplicationId.create(name);
+        }
+    }
 
-        private Map<Key, Intent> intents;
+    /**
+     * Test intent service.
+     * Always install or withdraw success for any Intents.
+     */
+    public class TestIntentService extends IntentServiceAdapter {
+        IntentListener listener;
+        List<IntentData> intents;
 
         public TestIntentService() {
-            intents = Maps.newHashMap();
+            intents = Lists.newArrayList();
         }
 
         @Override
         public void submit(Intent intent) {
-            intents.put(intent.key(), intent);
+            intents.add(new IntentData(intent, IntentState.INSTALLED, new WallClockTimestamp()));
+            if (listener != null) {
+                IntentEvent.getEvent(IntentState.INSTALLED, intent).ifPresent(listener::event);
+
+            }
+        }
+
+        @Override
+        public void withdraw(Intent intent) {
+            intents.forEach(intentData -> {
+                if (intentData.intent().key().equals(intent.key())) {
+                    intentData.setState(IntentState.WITHDRAWN);
+
+                    if (listener != null) {
+                        IntentEvent.getEvent(IntentState.WITHDRAWN, intent).ifPresent(listener::event);
+                    }
+                }
+            });
+        }
+
+        @Override
+        public Iterable<Intent> getIntents() {
+            return intents.stream()
+                    .map(IntentData::intent)
+                    .collect(Collectors.toList());
         }
 
         @Override
@@ -758,52 +320,227 @@
         }
 
         @Override
-        public Iterable<Intent> getIntents() {
-            return intents.values();
+        public void addListener(IntentListener listener) {
+            this.listener = listener;
         }
 
         @Override
-        public Intent getIntent(Key intentKey) {
-            for (Intent intent : intents.values()) {
-                if (intent.key().equals(intentKey)) {
-                    return intent;
-                }
-            }
-            return null;
+        public void removeListener(IntentListener listener) {
+            this.listener = null;
         }
     }
 
     /**
-     * Represents a fake HostService class which allows to add hosts manually
-     * in each test, when needed.
+     * Test leadership service.
      */
-    private class TestHostService extends HostServiceAdapter {
+    public class TestLeadershipService extends LeadershipServiceAdapter {
+        LeadershipEventListener listener;
 
-        private Set<Host> hosts;
-
-        public TestHostService(Set<Host> hosts) {
-            this.hosts = hosts;
+        @Override
+        public void addListener(LeadershipEventListener listener) {
+            this.listener = listener;
         }
 
         @Override
-        public void addListener(HostListener listener) {
-            VplsTest.this.hostListener = listener;
+        public void removeListener(LeadershipEventListener listener) {
+            this.listener = null;
+        }
+
+        /**
+         * Sends the leadership event to the listener.
+         *
+         * @param event the Intent event
+         */
+        public void sendEvent(LeadershipEvent event) {
+            if (listener != null && listener.isRelevant(event)) {
+                listener.event(event);
+            }
         }
 
         @Override
-        public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
-            return hosts.stream()
-                    .filter(h -> h.location().equals(connectPoint))
+        public NodeId getLeader(String path) {
+            return NODE_ID_1;
+        }
+    }
+
+    /**
+     * Test interface service; contains all interfaces which already generated.
+     */
+    public class TestInterfaceService implements InterfaceService {
+
+        @Override
+        public void addListener(InterfaceListener listener) {
+        }
+
+        @Override
+        public void removeListener(InterfaceListener listener) {
+        }
+
+        @Override
+        public Set<Interface> getInterfaces() {
+            return AVAILABLE_INTERFACES;
+        }
+
+        @Override
+        public Interface getInterfaceByName(ConnectPoint connectPoint,
+                                            String name) {
+            return AVAILABLE_INTERFACES.stream()
+                    .filter(intf -> intf.name().equals(name))
+                    .findFirst()
+                    .orElse(null);
+        }
+
+        @Override
+        public Set<Interface> getInterfacesByPort(ConnectPoint port) {
+            return AVAILABLE_INTERFACES.stream()
+                    .filter(intf -> intf.connectPoint().equals(port))
                     .collect(Collectors.toSet());
         }
 
+        @Override
+        public Set<Interface> getInterfacesByIp(IpAddress ip) {
+            return AVAILABLE_INTERFACES.stream()
+                    .filter(intf -> intf.ipAddressesList().contains(ip))
+                    .collect(Collectors.toSet());
+        }
+
+        @Override
+        public Set<Interface> getInterfacesByVlan(VlanId vlan) {
+            return AVAILABLE_INTERFACES.stream()
+                    .filter(intf -> intf.vlan().equals(vlan))
+                    .collect(Collectors.toSet());
+        }
+
+        @Override
+        public Interface getMatchingInterface(IpAddress ip) {
+            return AVAILABLE_INTERFACES.stream()
+                    .filter(intf -> intf.ipAddressesList().contains(ip))
+                    .findFirst()
+                    .orElse(null);
+        }
+
+        @Override
+        public Set<Interface> getMatchingInterfaces(IpAddress ip) {
+            return AVAILABLE_INTERFACES.stream()
+                    .filter(intf -> intf.ipAddressesList().contains(ip))
+                    .collect(Collectors.toSet());
+        }
+    }
+
+    /**
+     * Test VPLS store.
+     */
+    public class TestVplsStore extends VplsStoreAdapter {
+        /**
+         * Clears the store.
+         */
+        public void clear() {
+            vplsDataMap.clear();
+        }
+
+        /**
+         * Gets the store delegate.
+         *
+         * @return the store delegate
+         */
+        public StoreDelegate<VplsStoreEvent> delegate() {
+            return this.delegate;
+        }
+    }
+
+    /**
+     * Test VPLS.
+     * Provides basic VPLS functionality and stores VPLS information.
+     */
+    public class TestVpls implements Vpls {
+        public Map<String, VplsData> testData;
+
+        public TestVpls() {
+            testData = Maps.newHashMap();
+        }
+
+        public void initSampleData() {
+            testData.clear();
+            VplsData vplsData = VplsData.of(VPLS1);
+            vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2));
+            vplsData.state(VplsData.VplsState.ADDED);
+            testData.put(VPLS1, vplsData);
+
+            vplsData = VplsData.of(VPLS2);
+            vplsData.addInterfaces(ImmutableSet.of(V200H1, V200H2));
+            vplsData.state(VplsData.VplsState.ADDED);
+            testData.put(VPLS2, vplsData);
+        }
+
+        @Override
+        public VplsData createVpls(String vplsName, EncapsulationType encapsulationType) {
+            VplsData vplsData = VplsData.of(vplsName, encapsulationType);
+            testData.put(vplsName, vplsData);
+            return vplsData;
+        }
+
+        @Override
+        public VplsData removeVpls(VplsData vplsData) {
+            if (!testData.containsKey(vplsData.name())) {
+                return null;
+            }
+
+            testData.remove(vplsData.name());
+            return vplsData;
+        }
+
+        @Override
+        public void addInterfaces(VplsData vplsData, Collection<Interface> interfaces) {
+            vplsData.addInterfaces(interfaces);
+            testData.put(vplsData.name(), vplsData);
+        }
+
+        @Override
+        public void addInterface(VplsData vplsData, Interface iface) {
+            vplsData.addInterface(iface);
+            testData.put(vplsData.name(), vplsData);
+        }
+
+        @Override
+        public void setEncapsulationType(VplsData vplsData, EncapsulationType encapsulationType) {
+            vplsData.encapsulationType(encapsulationType);
+            testData.put(vplsData.name(), vplsData);
+        }
+
+        @Override
+        public VplsData getVpls(String vplsName) {
+            return testData.get(vplsName);
+        }
+
+        @Override
+        public Collection<VplsData> getAllVpls() {
+            return testData.values();
+        }
+
+        @Override
+        public Collection<Interface> removeInterfaces(VplsData vplsData, Collection<Interface> interfaces) {
+            vplsData.removeInterfaces(interfaces);
+            testData.put(vplsData.name(), vplsData);
+            return interfaces;
+        }
+
+        @Override
+        public Interface removeInterface(VplsData vplsData, Interface iface) {
+            vplsData.removeInterface(iface);
+            testData.put(vplsData.name(), vplsData);
+            return iface;
+        }
+
+        @Override
+        public void removeAllVpls() {
+            testData.clear();
+        }
     }
 
     /**
      * Represents a fake IdGenerator class for intents.
      */
-    private static class TestIdGenerator implements IdGenerator {
-
+    protected static class TestIdGenerator implements IdGenerator {
         private final AtomicLong id = new AtomicLong(0);
 
         @Override
@@ -814,170 +551,135 @@
     }
 
     /**
-     * Test IntentSynchronizer that passes all intents straight through to the
-     * intent service.
+     * Test host service; contains all hosts which already generated.
+     *
      */
-    private class TestIntentSynchronizer implements IntentSynchronizationService {
+    public class TestHostService extends HostServiceAdapter {
 
-        private final IntentService intentService;
+        private HostListener listener;
 
-        /**
-         * Creates a new intent test synchronizer.
-         *
-         * @param intentService intent service
-         */
-        public TestIntentSynchronizer(IntentService intentService) {
-            this.intentService = intentService;
+        @Override
+        public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
+            return AVAILABLE_HOSTS.stream()
+                    .filter(host -> host.location().equals(connectPoint))
+                    .collect(Collectors.toSet());
         }
 
         @Override
-        public void submit(Intent intent) {
-            intentService.submit(intent);
+        public Set<Host> getHostsByMac(MacAddress mac) {
+            return AVAILABLE_HOSTS.stream()
+                    .filter(host -> host.mac().equals(mac))
+                    .collect(Collectors.toSet());
         }
 
         @Override
-        public void withdraw(Intent intent) {
-            intentService.withdraw(intent);
+        public Iterable<Host> getHosts() {
+            return AVAILABLE_HOSTS;
         }
 
         @Override
-        public void removeIntentsByAppId(ApplicationId applicationId) {
+        public Set<Host> getHostsByVlan(VlanId vlanId) {
+            return AVAILABLE_HOSTS.stream()
+                    .filter(host -> host.vlan().equals(vlanId))
+                    .collect(Collectors.toSet());
+        }
+
+        @Override
+        public int getHostCount() {
+            return AVAILABLE_HOSTS.size();
+        }
+
+        @Override
+        public Host getHost(HostId hostId) {
+            return AVAILABLE_HOSTS.stream()
+                    .filter(host -> host.id().equals(hostId))
+                    .findFirst()
+                    .orElse(null);
+        }
+
+        @Override
+        public void addListener(HostListener listener) {
+            this.listener = listener;
+        }
+
+        public void postHostEvent(HostEvent hostEvent) {
+            this.listener.event(hostEvent);
         }
     }
 
     /**
-     * Represents a fake VplsConfigService class which is needed for testing.
+     * Test network configuration service.
      */
-    private class TestVplsConfigService extends VplsConfigServiceAdapter {
+    public class TestConfigService extends NetworkConfigServiceAdapter {
+        public static final String EMPTY_JSON_TREE = "{}";
+        NetworkConfigListener listener;
+        VplsAppConfig vplsAppConfig;
 
-        private final SetMultimap<String, Interface> ifacesByVplsName;
-        private final Map<String, EncapsulationType> encapsByVplsName;
-
-        private Set<String> vplsAffectByApi = new HashSet<>();
-
-        TestVplsConfigService(SetMultimap<String, Interface> ifacesByVplsName,
-                              Map<String, EncapsulationType> encapsByVplsName) {
-            this.ifacesByVplsName = ifacesByVplsName;
-            this.encapsByVplsName = encapsByVplsName;
+        @Override
+        public void addListener(NetworkConfigListener listener) {
+            this.listener = listener;
         }
 
         @Override
-        public void addVpls(String vplsName, Set<String> ifaceNames, String encap) {
-            if (!ifacesByVplsName.containsKey(vplsName)) {
-                ifaceNames.forEach(ifaceName -> {
-                    AVAILABLE_INTERFACES.forEach(iface -> {
-                        if (iface.name().equals(ifaceName)) {
-                            ifacesByVplsName.put(vplsName, iface);
-                        }
-                    });
-                });
-            }
-            if (!ifacesByVplsName.containsKey(vplsName)) {
-                encapsByVplsName.put(vplsName, valueOf(encap));
+        public void removeListener(NetworkConfigListener listener) {
+            this.listener = null;
+        }
+
+        /**
+         * Sends network config event to listener.
+         *
+         * @param event the network config event
+         */
+        public void sendEvent(NetworkConfigEvent event) {
+            if (listener != null) {
+                listener.event(event);
             }
         }
 
-        @Override
-        public void removeVpls(String vplsName) {
-            if (ifacesByVplsName.containsKey(vplsName)) {
-                ifacesByVplsName.removeAll(vplsName);
+        /**
+         * Constructs test config service.
+         * Generates an VPLS configuration with sample VPLS configs.
+         */
+        public TestConfigService() {
+            vplsAppConfig = new VplsAppConfig();
+            final ObjectMapper mapper = new ObjectMapper();
+            final ConfigApplyDelegate delegate = new VplsAppConfigTest.MockCfgDelegate();
+            JsonNode tree = null;
+            try {
+                tree = new ObjectMapper().readTree(EMPTY_JSON_TREE);
+            } catch (IOException e) {
+                e.printStackTrace();
             }
+            vplsAppConfig.init(APPID, APP_NAME, tree, mapper, delegate);
+            VplsConfig vplsConfig = new VplsConfig(VPLS1,
+                                                   ImmutableSet.of(V100H1.name(), V100H2.name()),
+                                                   EncapsulationType.NONE);
+            vplsAppConfig.addVpls(vplsConfig);
+            vplsConfig = new VplsConfig(VPLS2,
+                                        ImmutableSet.of(V200H1.name(), V200H2.name()),
+                                        EncapsulationType.VLAN);
+            vplsAppConfig.addVpls(vplsConfig);
+
+        }
+
+        /**
+         * Overrides VPLS config to the config service.
+         *
+         * @param vplsAppConfig the new VPLS config
+         */
+        public void setConfig(VplsAppConfig vplsAppConfig) {
+            this.vplsAppConfig = vplsAppConfig;
         }
 
         @Override
-        public void addIface(String vplsName, String iface) {
-            if (!ifacesByVplsName.containsKey(vplsName)) {
-                AVAILABLE_INTERFACES.forEach(intf -> {
-                    if (intf.name().equals(iface)) {
-                        ifacesByVplsName.put(vplsName, intf);
-                    }
-                });
-            }
+        public <S, C extends Config<S>> C getConfig(S subject, Class<C> configClass) {
+            return (C) vplsAppConfig;
         }
 
         @Override
-        public void setEncap(String vplsName, String encap) {
-            encapsByVplsName.put(vplsName, EncapsulationType.enumFromString(encap));
-        }
-
-        @Override
-        public void removeIface(String iface) {
-            SetMultimap<String, Interface> search = HashMultimap.create(ifacesByVplsName);
-            search.entries().forEach(e -> {
-                if (e.getValue().name().equals(iface)) {
-                    ifacesByVplsName.remove(e.getKey(), iface);
-                }
-            });
-        }
-
-        @Override
-        public void cleanVplsConfig() {
-            ifacesByVplsName.clear();
-        }
-
-        @Override
-        public EncapsulationType encap(String vplsName) {
-            EncapsulationType encap = null;
-            if (encapsByVplsName.containsKey(vplsName)) {
-                encap = encapsByVplsName.get(vplsName);
-            }
-            return encap;
-        }
-
-        @Override
-        public Set<String> vplsAffectedByApi() {
-            Set<String> vplsNames = ImmutableSet.copyOf(vplsAffectByApi);
-
-            vplsAffectByApi.clear();
-
-            return vplsNames;
-        }
-
-        @Override
-        public Set<Interface> allIfaces() {
-            return ifacesByVplsName.values()
-                    .stream()
-                    .collect(Collectors.toSet());
-        }
-
-        @Override
-        public Set<Interface> ifaces(String name) {
-            return ifacesByVplsName.get(name)
-                    .stream()
-                    .collect(Collectors.toSet());
-        }
-
-        @Override
-        public Set<String> vplsNames() {
-            return ifacesByVplsName.keySet();
-        }
-
-        @Override
-        public Set<String> vplsNamesOld() {
-            return ifacesByVplsName.keySet();
-        }
-
-        public SetMultimap<String, Interface> ifacesByVplsName() {
-            return ImmutableSetMultimap.copyOf(ifacesByVplsName);
-        }
-
-        @Override
-        public SetMultimap<String, Interface> ifacesByVplsName(VlanId vlan,
-                                                               ConnectPoint connectPoint) {
-            String vplsName =
-                    ifacesByVplsName.entries().stream()
-                            .filter(e -> e.getValue().connectPoint().equals(connectPoint))
-                            .filter(e -> e.getValue().vlan().equals(vlan))
-                            .map(Map.Entry::getKey)
-                            .findFirst()
-                            .orElse(null);
-            SetMultimap<String, Interface> result = HashMultimap.create();
-            if (vplsName != null && ifacesByVplsName.containsKey(vplsName)) {
-                ifacesByVplsName.get(vplsName)
-                        .forEach(intf -> result.put(vplsName, intf));
-                return result;
-            }
-            return null;
+        public <S, C extends Config<S>> C addConfig(S subject, Class<C> configClass) {
+            return (C) vplsAppConfig;
         }
     }
+
 }
diff --git a/apps/vpls/src/test/java/org/onosproject/vpls/cli/VplsCommandTest.java b/apps/vpls/src/test/java/org/onosproject/vpls/cli/VplsCommandTest.java
new file mode 100644
index 0000000..eeff5ef
--- /dev/null
+++ b/apps/vpls/src/test/java/org/onosproject/vpls/cli/VplsCommandTest.java
@@ -0,0 +1,247 @@
+/*
+ * 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.cli;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.net.EncapsulationType;
+import org.onosproject.vpls.VplsTest;
+import org.onosproject.vpls.api.VplsData;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.Collection;
+
+import static org.junit.Assert.*;
+
+/**
+ * Test for {@link VplsCommand}.
+ */
+public class VplsCommandTest extends VplsTest {
+    private static final String NEW_LINE = "\n";
+    private static final String SHOW_ALL_RES = "----------------\n" +
+            "VPLS name: \u001B[1mvpls2\u001B[0m\n" +
+            "Associated interfaces: [v200h2, v200h1]\n" +
+            "Encapsulation: NONE\n" +
+            "State: ADDED\n" +
+            "----------------\n" +
+            "VPLS name: \u001B[1mvpls1\u001B[0m\n" +
+            "Associated interfaces: [v100h1, v100h2]\n" +
+            "Encapsulation: NONE\n" +
+            "State: ADDED\n" +
+            "----------------\n";
+    private static final String SHOW_ONE_RES = "VPLS name: \u001B[1mvpls1\u001B[0m\n" +
+            "Associated interfaces: [v100h1, v100h2]\n" +
+            "Encapsulation: NONE\n" +
+            "State: ADDED\n";
+    private static final String IFACE_ALREADY_USED =
+            "\u001B[31mInterface \u001B[1mv200h1\u001B[0m\u001B[31m already associated " +
+            "to VPLS \u001B[1mvpls2\u001B[0m\u001B[31m\u001B[0m\n";
+    private static final String LIST_OUTPUT = VPLS1 + NEW_LINE + VPLS2 + NEW_LINE;
+    VplsCommand vplsCommand;
+
+    @Before
+    public void setup() {
+        vplsCommand = new VplsCommand();
+        vplsCommand.vpls = new TestVpls();
+        vplsCommand.interfaceService = new TestInterfaceService();
+    }
+
+    /**
+     * Creates a new VPLS.
+     */
+    @Test
+    public void testCreate() {
+        vplsCommand.command = VplsCommandEnum.CREATE.toString();
+        vplsCommand.vplsName = VPLS1;
+        vplsCommand.execute();
+        Collection<VplsData> vplss = vplsCommand.vpls.getAllVpls();
+        assertEquals(1, vplss.size());
+        VplsData result = vplss.iterator().next();
+        VplsData expected = VplsData.of(VPLS1, EncapsulationType.NONE);
+        assertEquals(expected, result);
+    }
+
+    /**
+     * Adds new network interface to a VPLS.
+     */
+    @Test
+    public void testAddIf() {
+        vplsCommand.create(VPLS1);
+
+        vplsCommand.command = VplsCommandEnum.ADD_IFACE.toString();
+        vplsCommand.vplsName = VPLS1;
+        vplsCommand.optArg = V100H1.name();
+        vplsCommand.execute();
+
+        vplsCommand.command = VplsCommandEnum.ADD_IFACE.toString();
+        vplsCommand.vplsName = VPLS1;
+        vplsCommand.optArg = V200H1.name();
+        vplsCommand.execute();
+
+        Collection<VplsData> vplss = vplsCommand.vpls.getAllVpls();
+        assertEquals(1, vplss.size());
+        VplsData result = vplss.iterator().next();
+        assertEquals(2, result.interfaces().size());
+        assertTrue(result.interfaces().contains(V100H1));
+        assertTrue(result.interfaces().contains(V200H1));
+    }
+
+    /**
+     * Removes network interface from a VPLS.
+     */
+    @Test
+    public void testRemIf() {
+        vplsCommand.create(VPLS1);
+        vplsCommand.addIface(VPLS1, V100H1.name());
+        vplsCommand.addIface(VPLS1, V200H1.name());
+
+        vplsCommand.command = VplsCommandEnum.REMOVE_IFACE.toString();
+        vplsCommand.vplsName = VPLS1;
+        vplsCommand.optArg = V200H1.name();
+        vplsCommand.execute();
+
+        Collection<VplsData> vplss = vplsCommand.vpls.getAllVpls();
+        assertEquals(1, vplss.size());
+        VplsData result = vplss.iterator().next();
+        assertEquals(1, result.interfaces().size());
+        assertTrue(result.interfaces().contains(V100H1));
+        assertFalse(result.interfaces().contains(V200H1));
+    }
+
+    /**
+     * Lists all VPLS names.
+     */
+    @Test
+    public void testList() {
+        ((TestVpls) vplsCommand.vpls).initSampleData();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        PrintStream ps = new PrintStream(baos);
+        System.setOut(ps);
+        vplsCommand.command = VplsCommandEnum.LIST.toString();
+        vplsCommand.execute();
+        String result = baos.toString();
+
+        assertEquals(LIST_OUTPUT, result);
+    }
+
+    /**
+     * Sets encapsulation to a VPLS.
+     */
+    @Test
+    public void testSetEncap() {
+        ((TestVpls) vplsCommand.vpls).initSampleData();
+
+        // Sets to NONE
+        vplsCommand.command = VplsCommandEnum.SET_ENCAP.toString();
+        vplsCommand.vplsName = VPLS1;
+        vplsCommand.optArg = EncapsulationType.NONE.name();
+        vplsCommand.execute();
+        VplsData result = vplsCommand.vpls.getVpls(VPLS1);
+        assertEquals(result.encapsulationType(), EncapsulationType.NONE);
+
+        // Sets to VLAN
+        vplsCommand.command = VplsCommandEnum.SET_ENCAP.toString();
+        vplsCommand.vplsName = VPLS1;
+        vplsCommand.optArg = EncapsulationType.VLAN.name();
+        vplsCommand.execute();
+        result = vplsCommand.vpls.getVpls(VPLS1);
+        assertEquals(result.encapsulationType(), EncapsulationType.VLAN);
+
+        // Sets to MPLS
+        vplsCommand.command = VplsCommandEnum.SET_ENCAP.toString();
+        vplsCommand.vplsName = VPLS1;
+        vplsCommand.optArg = EncapsulationType.MPLS.name();
+        vplsCommand.execute();
+        result = vplsCommand.vpls.getVpls(VPLS1);
+        assertEquals(result.encapsulationType(), EncapsulationType.MPLS);
+    }
+
+    /**
+     * Deletes a VPLS.
+     */
+    @Test
+    public void testDelete() {
+        ((TestVpls) vplsCommand.vpls).initSampleData();
+        vplsCommand.command = VplsCommandEnum.DELETE.toString();
+        vplsCommand.vplsName = VPLS1;
+        vplsCommand.execute();
+        Collection<VplsData> vplss = vplsCommand.vpls.getAllVpls();
+        assertEquals(1, vplss.size());
+    }
+
+    /**
+     * Shows all VPLS information.
+     */
+    @Test
+    public void testShowAll() {
+        ((TestVpls) vplsCommand.vpls).initSampleData();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        PrintStream ps = new PrintStream(baos);
+        System.setOut(ps);
+        vplsCommand.command = VplsCommandEnum.SHOW.toString();
+        vplsCommand.execute();
+        String result = baos.toString();
+        assertEquals(SHOW_ALL_RES, result);
+    }
+
+    /**
+     * Shows a VPLS information.
+     */
+    @Test
+    public void testShowOne() {
+        ((TestVpls) vplsCommand.vpls).initSampleData();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        PrintStream ps = new PrintStream(baos);
+        System.setOut(ps);
+        vplsCommand.command = VplsCommandEnum.SHOW.toString();
+        vplsCommand.vplsName = VPLS1;
+        vplsCommand.execute();
+        String result = baos.toString();
+        assertEquals(SHOW_ONE_RES, result);
+    }
+
+    /**
+     * Adds a network interface which already related to another VPLS to a VPLS.
+     */
+    @Test
+    public void testIfaceAssociated() {
+        ((TestVpls) vplsCommand.vpls).initSampleData();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        PrintStream ps = new PrintStream(baos);
+        System.setOut(ps);
+        vplsCommand.command = VplsCommandEnum.ADD_IFACE.toString();
+        vplsCommand.vplsName = VPLS1;
+        vplsCommand.optArg = V200H1.name();
+        vplsCommand.execute();
+
+        String result = baos.toString();
+        assertEquals(IFACE_ALREADY_USED, result);
+    }
+
+    /**
+     * Removes all VPLS.
+     */
+    @Test
+    public void testClean() {
+        ((TestVpls) vplsCommand.vpls).initSampleData();
+        vplsCommand.command = VplsCommandEnum.CLEAN.toString();
+        vplsCommand.execute();
+        Collection<VplsData> vplss = vplsCommand.vpls.getAllVpls();
+        assertEquals(0, vplss.size());
+    }
+}
diff --git a/apps/vpls/src/test/java/org/onosproject/vpls/cli/completer/VplsCommandCompleterTest.java b/apps/vpls/src/test/java/org/onosproject/vpls/cli/completer/VplsCommandCompleterTest.java
new file mode 100644
index 0000000..90d48df
--- /dev/null
+++ b/apps/vpls/src/test/java/org/onosproject/vpls/cli/completer/VplsCommandCompleterTest.java
@@ -0,0 +1,188 @@
+/*
+ * 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.cli.completer;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.console.CommandSessionHolder;
+import org.apache.karaf.shell.console.completer.ArgumentCompleter;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.net.EncapsulationType;
+import org.onosproject.vpls.VplsTest;
+import org.onosproject.vpls.cli.VplsCommandEnum;
+
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.junit.Assert.*;
+
+public class VplsCommandCompleterTest extends VplsTest {
+    private static final String VPLS_CMD = "vpls";
+    private TestCommandSession commandSession;
+
+    @Before
+    public void setup() {
+        commandSession = new TestCommandSession();
+        CommandSessionHolder.setSession(commandSession);
+    }
+
+    /**
+     * Test VPLS command completer.
+     */
+    @Test
+    public void testCommandCompleter() {
+        VplsCommandCompleter commandCompleter = new VplsCommandCompleter();
+        List<String> choices = commandCompleter.choices();
+        List<String> expected = VplsCommandEnum.toStringList();
+        assertEquals(expected, choices);
+    }
+
+    /**
+     * Test VPLS name completer.
+     */
+    @Test
+    public void testNameCompleter() {
+        VplsNameCompleter vplsNameCompleter = new VplsNameCompleter();
+        vplsNameCompleter.vpls = new TestVpls();
+        ((TestVpls) vplsNameCompleter.vpls).initSampleData();
+        List<String> choices = vplsNameCompleter.choices();
+        List<String> expected = ImmutableList.of(VPLS1, VPLS2);
+
+        // Can not ensure the order, use contains all instead of equals
+        assertEquals(choices.size(), expected.size());
+        assertTrue(choices.containsAll(expected));
+    }
+
+    /**
+     * Test VPLS option arguments completer.
+     */
+    @Test
+    public void testOptArgCompleter() {
+        VplsOptArgCompleter completer = new VplsOptArgCompleter();
+        completer.vpls = new TestVpls();
+        ((TestVpls) completer.vpls).initSampleData();
+        completer.interfaceService = new TestInterfaceService();
+
+        // Add interface to VPLS
+        commandSession.updateArguments(VPLS_CMD, VplsCommandEnum.ADD_IFACE.toString(), VPLS1);
+
+        List<String> choices = completer.choices();
+        List<String> expected = ImmutableList.of(V300H1.name(),
+                                                 V300H2.name(),
+                                                 V400H1.name(),
+                                                 VNONEH1.name(),
+                                                 VNONEH2.name(),
+                                                 VNONEH3.name());
+
+        // Can not ensure the order, use contains all instead of equals
+        assertEquals(choices.size(), expected.size());
+        assertTrue(choices.containsAll(expected));
+
+        // Removes interface from VPLS
+        commandSession.updateArguments(VPLS_CMD, VplsCommandEnum.REMOVE_IFACE.toString(), VPLS1);
+        choices = completer.choices();
+        expected = completer.vpls.getVpls(VPLS1).interfaces().stream()
+                .map(Interface::name)
+                .collect(Collectors.toList());
+
+        // Can not ensure the order, use contains all instead of equals
+        assertEquals(choices.size(), expected.size());
+        assertTrue(choices.containsAll(expected));
+
+        // Sets encapsulation
+        commandSession.updateArguments(VPLS_CMD, VplsCommandEnum.SET_ENCAP.toString(), VPLS1);
+        choices = completer.choices();
+        expected = Arrays.stream(EncapsulationType.values())
+                .map(Enum::toString)
+                .collect(Collectors.toList());
+
+        // Can not ensure the order, use contains all instead of equals
+        assertEquals(choices.size(), expected.size());
+        assertTrue(choices.containsAll(expected));
+    }
+
+    /**
+     * Test command session.
+     */
+    class TestCommandSession implements CommandSession {
+        ArgumentCompleter.ArgumentList argumentList;
+        public TestCommandSession() {
+            String[] emptyStringArr = new String[0];
+            argumentList = new ArgumentCompleter.ArgumentList(emptyStringArr,
+                                                              0,
+                                                              0,
+                                                              0);
+        }
+
+        /**
+         * Updates argument list for the command session.
+         *
+         * @param args new arguments
+         */
+        public void updateArguments(String... args) {
+            argumentList = new ArgumentCompleter.ArgumentList(args,
+                                                              0,
+                                                              0,
+                                                              0);
+        }
+
+        @Override
+        public Object execute(CharSequence charSequence) throws Exception {
+            return null;
+        }
+
+        @Override
+        public void close() {
+
+        }
+
+        @Override
+        public InputStream getKeyboard() {
+            return null;
+        }
+
+        @Override
+        public PrintStream getConsole() {
+            return null;
+        }
+
+        @Override
+        public Object get(String s) {
+            return argumentList;
+        }
+
+        @Override
+        public void put(String s, Object o) {
+
+        }
+
+        @Override
+        public CharSequence format(Object o, int i) {
+            return null;
+        }
+
+        @Override
+        public Object convert(Class<?> aClass, Object o) {
+            return null;
+        }
+    }
+}
diff --git a/apps/vpls/src/test/java/org/onosproject/vpls/config/VplsAppConfigTest.java b/apps/vpls/src/test/java/org/onosproject/vpls/config/VplsAppConfigTest.java
index b3ca89d..37e7598 100644
--- a/apps/vpls/src/test/java/org/onosproject/vpls/config/VplsAppConfigTest.java
+++ b/apps/vpls/src/test/java/org/onosproject/vpls/config/VplsAppConfigTest.java
@@ -246,19 +246,28 @@
                      vplsAppConfig.getVplsWithName(VPLS1).encap());
     }
 
-    private class MockCfgDelegate implements ConfigApplyDelegate {
-
+    public static class MockCfgDelegate implements ConfigApplyDelegate {
         @Override
         public void onApply(@SuppressWarnings("rawtypes") Config config) {
             config.apply();
         }
     }
 
+    /**
+     * Creates a basic VPLS config.
+     *
+     * @return the VPLS config
+     */
     private VplsConfig createInitialVpls() {
         Set<String> ifaces = new HashSet<>(Arrays.asList(IF1, IF2, IF3));
         return new VplsConfig(VPLS1, ifaces, EncapsulationType.NONE);
     }
 
+    /**
+     * Creates another basic VPLS config.
+     *
+     * @return the VPLS config
+     */
     private VplsConfig createNewVpls() {
         Set<String> ifaces = new HashSet<>(Arrays.asList(IF4, IF5));
         return new VplsConfig(NEWVPLS, ifaces, EncapsulationType.NONE);
diff --git a/apps/vpls/src/test/java/org/onosproject/vpls/config/VplsConfigManagerTest.java b/apps/vpls/src/test/java/org/onosproject/vpls/config/VplsConfigManagerTest.java
new file mode 100644
index 0000000..8baae6c
--- /dev/null
+++ b/apps/vpls/src/test/java/org/onosproject/vpls/config/VplsConfigManagerTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.config;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.ImmutableSet;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.net.EncapsulationType;
+import org.onosproject.net.config.ConfigApplyDelegate;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigRegistryAdapter;
+import org.onosproject.vpls.VplsTest;
+import org.onosproject.vpls.api.VplsData;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import static org.junit.Assert.*;
+public class VplsConfigManagerTest extends VplsTest {
+    VplsConfigManager vplsConfigManager;
+
+    @Before
+    public void setup() {
+        vplsConfigManager = new VplsConfigManager();
+        vplsConfigManager.configService = new TestConfigService();
+        vplsConfigManager.coreService = new TestCoreService();
+        vplsConfigManager.interfaceService = new TestInterfaceService();
+        vplsConfigManager.registry = new NetworkConfigRegistryAdapter();
+        vplsConfigManager.vpls = new TestVpls();
+        vplsConfigManager.leadershipService = new TestLeadershipService();
+
+        vplsConfigManager.activate();
+    }
+
+    @After
+    public void tearDown() {
+        vplsConfigManager.deactivate();
+    }
+
+    /**
+     * Reloads network configuration by sending a network config event.
+     */
+    @Test
+    public void testReloadConfig() {
+        NetworkConfigEvent event = new NetworkConfigEvent(NetworkConfigEvent.Type.CONFIG_ADDED,
+                                                          null,
+                                                          VplsAppConfig.class);
+        ((TestConfigService) vplsConfigManager.configService).sendEvent(event);
+
+        Collection<VplsData> vplss = vplsConfigManager.vpls.getAllVpls();
+        assertEquals(2, vplss.size());
+
+        VplsData expect = VplsData.of(VPLS1);
+        expect.addInterfaces(ImmutableSet.of(V100H1, V100H2));
+        expect.state(VplsData.VplsState.ADDED);
+        assertTrue(vplss.contains(expect));
+
+        expect = VplsData.of(VPLS2, EncapsulationType.VLAN);
+        expect.addInterfaces(ImmutableSet.of(V200H1, V200H2));
+        expect.state(VplsData.VplsState.ADDED);
+        System.out.println(vplss);
+        assertTrue(vplss.contains(expect));
+    }
+
+    /**
+     * Sends a network config event with null network config.
+     */
+    @Test
+    public void testReloadNullConfig() {
+        ((TestConfigService) vplsConfigManager.configService).setConfig(null);
+        NetworkConfigEvent event = new NetworkConfigEvent(NetworkConfigEvent.Type.CONFIG_ADDED,
+                                                          null,
+                                                          VplsAppConfig.class);
+        ((TestConfigService) vplsConfigManager.configService).sendEvent(event);
+
+        Collection<VplsData> vplss = vplsConfigManager.vpls.getAllVpls();
+        assertEquals(0, vplss.size());
+    }
+
+    /**
+     * Updates VPLSs by sending new VPLS config.
+     */
+    @Test
+    public void testReloadConfigUpdateVpls() {
+        ((TestVpls) vplsConfigManager.vpls).initSampleData();
+
+        VplsAppConfig vplsAppConfig = new VplsAppConfig();
+        final ObjectMapper mapper = new ObjectMapper();
+        final ConfigApplyDelegate delegate = new VplsAppConfigTest.MockCfgDelegate();
+        JsonNode tree = null;
+        try {
+            tree = new ObjectMapper().readTree(TestConfigService.EMPTY_JSON_TREE);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        vplsAppConfig.init(APPID, APP_NAME, tree, mapper, delegate);
+        VplsConfig vplsConfig = new VplsConfig(VPLS1,
+                                               ImmutableSet.of(V100H1.name()),
+                                               EncapsulationType.MPLS);
+        vplsAppConfig.addVpls(vplsConfig);
+        ((TestConfigService) vplsConfigManager.configService).setConfig(vplsAppConfig);
+        NetworkConfigEvent event = new NetworkConfigEvent(NetworkConfigEvent.Type.CONFIG_ADDED,
+                                                          null,
+                                                          VplsAppConfig.class);
+        ((TestConfigService) vplsConfigManager.configService).sendEvent(event);
+
+        Collection<VplsData> vplss = vplsConfigManager.vpls.getAllVpls();
+        assertEquals(1, vplss.size());
+
+        VplsData expect = VplsData.of(VPLS1, EncapsulationType.MPLS);
+        expect.addInterfaces(ImmutableSet.of(V100H1));
+        expect.state(VplsData.VplsState.ADDED);
+        assertTrue(vplss.contains(expect));
+    }
+
+    /**
+     * Remvoes all VPLS by sending CONFIG_REMOVED event.
+     */
+    @Test
+    public void testRemoveConfig() {
+        NetworkConfigEvent event = new NetworkConfigEvent(NetworkConfigEvent.Type.CONFIG_REMOVED,
+                                                          null,
+                                                          VplsAppConfig.class);
+        ((TestConfigService) vplsConfigManager.configService).sendEvent(event);
+
+        Collection<VplsData> vplss = vplsConfigManager.vpls.getAllVpls();
+        assertEquals(0, vplss.size());
+    }
+}
diff --git a/apps/vpls/src/test/java/org/onosproject/vpls/store/VplsStoreAdapter.java b/apps/vpls/src/test/java/org/onosproject/vpls/store/VplsStoreAdapter.java
new file mode 100644
index 0000000..bcc70af
--- /dev/null
+++ b/apps/vpls/src/test/java/org/onosproject/vpls/store/VplsStoreAdapter.java
@@ -0,0 +1,76 @@
+/*
+ * 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.store;
+
+import com.google.common.collect.Maps;
+import org.onosproject.store.StoreDelegate;
+import org.onosproject.vpls.api.VplsData;
+import org.onosproject.vpls.api.VplsStore;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Test adapter for VPLS store.
+ */
+public class VplsStoreAdapter implements VplsStore {
+    protected Map<String, VplsData> vplsDataMap;
+    protected StoreDelegate<VplsStoreEvent> delegate;
+
+    public VplsStoreAdapter() {
+        vplsDataMap = Maps.newHashMap();
+    }
+
+    @Override
+    public void setDelegate(StoreDelegate<VplsStoreEvent> delegate) {
+        this.delegate = delegate;
+    }
+
+    @Override
+    public void unsetDelegate(StoreDelegate<VplsStoreEvent> delegate) {
+        this.delegate = null;
+    }
+
+    @Override
+    public boolean hasDelegate() {
+        return this.delegate != null;
+    }
+
+    @Override
+    public void addVpls(VplsData vplsData) {
+        vplsDataMap.put(vplsData.name(), vplsData);
+    }
+
+    @Override
+    public void removeVpls(VplsData vplsData) {
+        vplsDataMap.remove(vplsData.name());
+    }
+
+    @Override
+    public void updateVpls(VplsData vplsData) {
+        vplsDataMap.put(vplsData.name(), vplsData);
+    }
+
+    @Override
+    public VplsData getVpls(String vplsName) {
+        return vplsDataMap.get(vplsName);
+    }
+
+    @Override
+    public Collection<VplsData> getAllVpls() {
+        return vplsDataMap.values();
+    }
+}
diff --git a/apps/vpls/src/test/java/org/onosproject/vpls/store/VplsStoreTest.java b/apps/vpls/src/test/java/org/onosproject/vpls/store/VplsStoreTest.java
new file mode 100644
index 0000000..31a2176
--- /dev/null
+++ b/apps/vpls/src/test/java/org/onosproject/vpls/store/VplsStoreTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.store;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.store.service.TestStorageService;
+import org.onosproject.vpls.VplsTest;
+import org.onosproject.vpls.api.VplsData;
+import org.onosproject.vpls.config.VplsAppConfig;
+import org.onosproject.vpls.config.VplsConfig;
+
+import java.util.Collection;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.onosproject.net.EncapsulationType.VLAN;
+
+public class VplsStoreTest extends VplsTest {
+    private DistributedVplsStore vplsStore;
+
+    @Before
+    public void setup() {
+        vplsStore = new DistributedVplsStore();
+        vplsStore.coreService = new TestCoreService();
+        vplsStore.storageService = new TestStorageService();
+        vplsStore.networkConfigService = new TestConfigService();
+        vplsStore.active();
+    }
+
+    @After
+    public void tearDown() {
+        vplsStore.deactive();
+    }
+
+    /**
+     * Adds a VPLS to the store; checks if config store is also updated.
+     */
+    @Test
+    public void testAddVpls() {
+        VplsData vplsData = VplsData.of(VPLS1, VLAN);
+        vplsStore.addVpls(vplsData);
+        Collection<VplsData> vplsDataCollection = vplsStore.getAllVpls();
+
+        assertEquals(1, vplsDataCollection.size());
+        assertTrue(vplsDataCollection.contains(vplsData));
+
+        VplsAppConfig storedConfig = vplsStore.networkConfigService
+                .getConfig(null, VplsAppConfig.class);
+        assertNotEquals(-1L, storedConfig.updateTime());
+
+        assertEquals(1, storedConfig.vplss().size());
+
+        VplsConfig vplsConfig = storedConfig.vplss().iterator().next();
+
+        assertEquals(VPLS1, vplsConfig.name());
+        assertEquals(0, vplsConfig.ifaces().size());
+        assertEquals(VLAN, vplsConfig.encap());
+    }
+
+    /**
+     * Removes a VPLS from store; checks if config store is also updated.
+     */
+    @Test
+    public void testRemoveVpls() {
+        VplsData vplsData = VplsData.of(VPLS1, VLAN);
+        vplsStore.addVpls(vplsData);
+        vplsStore.removeVpls(vplsData);
+
+        Collection<VplsData> vplsDataCollection = vplsStore.getAllVpls();
+        assertEquals(0, vplsDataCollection.size());
+
+        VplsAppConfig storedConfig = vplsStore.networkConfigService
+                .getConfig(null, VplsAppConfig.class);
+
+        assertNotEquals(-1L, storedConfig.updateTime());
+
+        assertEquals(0, storedConfig.vplss().size());
+    }
+
+    /**
+     * Updates a VPLS from store; checks if config store is also updated.
+     */
+    @Test
+    public void testUpdateVpls() {
+        VplsData vplsData = VplsData.of(VPLS1, VLAN);
+        vplsStore.addVpls(vplsData);
+        vplsData.addInterface(V100H1);
+        vplsData.addInterface(V100H2);
+        vplsStore.updateVpls(vplsData);
+
+        Collection<VplsData> vplsDataCollection = vplsStore.getAllVpls();
+        assertEquals(1, vplsDataCollection.size());
+        VplsData newVplsData = vplsDataCollection.iterator().next();
+
+        assertEquals(vplsData, newVplsData);
+
+        VplsAppConfig storedConfig = vplsStore.networkConfigService
+                .getConfig(null, VplsAppConfig.class);
+
+        assertNotEquals(-1L, storedConfig.updateTime());
+
+        assertEquals(1, storedConfig.vplss().size());
+
+        VplsConfig vplsConfig = storedConfig.vplss().iterator().next();
+
+        assertEquals(VPLS1, vplsConfig.name());
+        assertEquals(2, vplsConfig.ifaces().size());
+        assertTrue(vplsConfig.ifaces().contains(V100H1.name()));
+        assertTrue(vplsConfig.ifaces().contains(V100H2.name()));
+        assertEquals(VLAN, vplsConfig.encap());
+    }
+}