Added basic pseudowire support for Trellis.

- Configurable pseudowires supporting untagged-untagged, single-single, double-double tagged traffic.
- Commands for listing, adding and removing pseudowires.
- Initial support for link failures.
- Pseudowires also configurable by network configuration.
- Tested with ofdpa_3.0.5.

Known limitations that I plan to fix soon :
	- Adding pseudowires from configuration is inconvenient because we need to ammend new pws to the
	  existing configuration. We should create a REST API for adding/removing/listing pws and abandond the
	  network configuration.
	- Spine fabric switches have rules matching a special mpls tag used for pw traffic for a specific leaf.
	  However, this rules redirect to an "indirect" group for forwarding traffic. If the resulting port there
	  is no mechanism as of now to handle this. We should use the MPLS ECMP Groups of ofdpa, however they are
	  not functional. Thus, we need to inject logic into the application to handle this case.

Change-Id: Ia85cf4514ebab627fc6ed5a19ad9f6cdc67dc24c
diff --git a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
index 85d6e1e..f820bcb 100644
--- a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
+++ b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -589,15 +589,20 @@
             return false;
         }
         fwdObjs.addAll(fwdObjsMpls);
-        // Generates the transit rules used by the MPLS Pwaas. For now it is
-        // the only case !BoS supported.
-        /*
-        fwdObjsMpls = handleMpls(targetSwId, destSwId, nextHops, segmentId, routerIp, false);
+
+        // Generates the transit rules used by the MPLS Pwaas.
+        int pwSrLabel;
+        try {
+            pwSrLabel = config.getPWRoutingLabel(destSwId);
+        } catch (DeviceConfigNotFoundException e) {
+            log.warn(e.getMessage() + " Aborting populateMplsRule. No label for PseudoWire traffic.");
+            return false;
+        }
+        fwdObjsMpls = handleMpls(targetSwId, destSwId, nextHops, pwSrLabel, routerIp, false);
         if (fwdObjsMpls.isEmpty()) {
             return false;
         }
         fwdObjs.addAll(fwdObjsMpls);
-        */
 
         for (ForwardingObjective fwdObj : fwdObjs) {
             log.debug("Sending MPLS fwd obj {} for SID {}-> next {} in sw: {}",
diff --git a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index cd34844..6092716 100644
--- a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -31,6 +31,7 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.Collectors;
 
+import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.collect.Sets;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -105,6 +106,8 @@
 import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler;
 import org.onosproject.segmentrouting.grouphandler.DestinationSet;
 import org.onosproject.segmentrouting.grouphandler.NextNeighbors;
+import org.onosproject.segmentrouting.pwaas.DefaultL2Tunnel;
+import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelPolicy;
 import org.onosproject.segmentrouting.pwaas.L2TunnelHandler;
 import org.onosproject.segmentrouting.storekey.DestinationSetNextObjectiveStoreKey;
 import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
@@ -429,7 +432,9 @@
                         TunnelPolicy.class,
                         Policy.Type.class,
                         PortNextObjectiveStoreKey.class,
-                        XConnectStoreKey.class
+                        XConnectStoreKey.class,
+                        DefaultL2Tunnel.class,
+                        DefaultL2TunnelPolicy.class
                 );
     }
 
@@ -504,6 +509,67 @@
     }
 
     @Override
+    public List<DefaultL2Tunnel> getL2Tunnels() {
+        return l2TunnelHandler.getL2Tunnels();
+    }
+
+    @Override
+    public List<DefaultL2TunnelPolicy> getL2Policies() {
+        return l2TunnelHandler.getL2Policies();
+    }
+
+    @Override
+    public L2TunnelHandler.Result addPseudowire(String tunnelId, String pwLabel, String cP1,
+                                         String cP1InnerVlan, String cP1OuterVlan, String cP2,
+                                         String cP2InnerVlan, String cP2OuterVlan,
+                                         String mode, String sdTag) {
+
+        PwaasConfig config = cfgService.getConfig(appId(), PwaasConfig.class);
+        if (config == null) {
+            log.warn("Configuration for Pwaas class could not be found!");
+            return L2TunnelHandler.Result.CONFIG_NOT_FOUND;
+        }
+
+        ObjectNode object = config.addPseudowire(tunnelId, pwLabel,
+                                                 cP1, cP1InnerVlan, cP1OuterVlan,
+                                                 cP2, cP2InnerVlan, cP2OuterVlan,
+                                                 mode, sdTag);
+        if (object == null) {
+            log.warn("Could not add pseudowire to the configuration!");
+            return L2TunnelHandler.Result.ADDITION_ERROR;
+        }
+
+        // inform everyone about the valid change in the pw configuration
+        cfgService.applyConfig(appId(), PwaasConfig.class, object);
+        return L2TunnelHandler.Result.SUCCESS;
+    }
+
+    @Override
+    public L2TunnelHandler.Result removePseudowire(String pwId) {
+
+        PwaasConfig config = cfgService.getConfig(appId(), PwaasConfig.class);
+        if (config == null) {
+            log.warn("Configuration for Pwaas class could not be found!");
+            return L2TunnelHandler.Result.CONFIG_NOT_FOUND;
+        }
+
+        ObjectNode object = config.removePseudowire(pwId);
+        if (object == null) {
+            log.warn("Could not delete pseudowire from configuration!");
+            return L2TunnelHandler.Result.REMOVAL_ERROR;
+        }
+
+        // sanity check, this should never fail since we removed a pw
+        // and we always check when we update the configuration
+        config.isValid();
+
+        // inform everyone
+        cfgService.applyConfig(appId(), PwaasConfig.class, object);
+
+        return L2TunnelHandler.Result.SUCCESS;
+    }
+
+    @Override
     public void rerouteNetwork() {
         cfgListener.configureNetwork();
     }
@@ -711,7 +777,7 @@
      * @param cp connect point
      * @return true if current instance is the master of given connect point
      */
-    boolean isMasterOf(ConnectPoint cp) {
+    public boolean isMasterOf(ConnectPoint cp) {
         boolean isMaster = mastershipService.isLocalMaster(cp.deviceId());
         if (!isMaster) {
             log.debug(NOT_MASTER, cp);
@@ -1244,6 +1310,7 @@
         }
 
         mcastHandler.processLinkDown(link);
+        l2TunnelHandler.processLinkDown(link);
     }
 
     private void processDeviceAdded(Device device) {
diff --git a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
index 3668ba2..4ef6079 100644
--- a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
+++ b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
@@ -18,6 +18,9 @@
 import org.onlab.packet.IpPrefix;
 import org.onosproject.net.DeviceId;
 import org.onosproject.segmentrouting.grouphandler.NextNeighbors;
+import org.onosproject.segmentrouting.pwaas.DefaultL2Tunnel;
+import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelPolicy;
+import org.onosproject.segmentrouting.pwaas.L2TunnelHandler;
 import org.onosproject.segmentrouting.storekey.DestinationSetNextObjectiveStoreKey;
 
 import com.google.common.collect.ImmutableMap;
@@ -79,6 +82,49 @@
     List<Policy> getPolicies();
 
     /**
+     * Returns all l2 tunnels of pseudowires.
+     *
+     * @return list of l2 tunnels
+     */
+    List<DefaultL2Tunnel> getL2Tunnels();
+
+    /**
+     * Returns all l2 policie of pseudowires.
+     *
+     * @return list of l2 policies.
+     */
+    List<DefaultL2TunnelPolicy> getL2Policies();
+
+    /**
+     * Removes pw. Essentially updates configuration for PwaasConfig
+     * and sends event for removal. The rest are handled by L2TunnelHandler
+     *
+     * @param pwId The pseudowire id
+     * @return SUCCESS if operation successful or a descriptive error otherwise.
+     */
+    L2TunnelHandler.Result removePseudowire(String pwId);
+
+    /**
+     * Adds a Pseudowire to the configuration.
+     *
+     * @param tunnelId The pseudowire id
+     * @param pwLabel Pw label
+     * @param cP1 Connection Point 1 of pw
+     * @param cP1InnerVlan Outer vlan of cp2
+     * @param cP1OuterVlan Outer vlan of cp1
+     * @param cP2 Connection Point 2 of pw
+     * @param cP2InnerVlan Inner vlan of cp2
+     * @param cP2OuterVlan Outer vlan of cp1
+     * @param mode Mode of pw
+     * @param sdTag Service Delimiting tag of pw
+     * @return SUCCESS if operation is successful or a descriptive error otherwise.
+     */
+    L2TunnelHandler.Result addPseudowire(String tunnelId, String pwLabel, String cP1,
+                                         String cP1InnerVlan, String cP1OuterVlan, String cP2,
+                                         String cP2InnerVlan, String cP2OuterVlan,
+                                         String mode, String sdTag);
+
+    /**
      * Creates a policy.
      *
      * @param policy policy reference to create
diff --git a/src/main/java/org/onosproject/segmentrouting/cli/PseudowireAddCommand.java b/src/main/java/org/onosproject/segmentrouting/cli/PseudowireAddCommand.java
new file mode 100644
index 0000000..d612475
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/cli/PseudowireAddCommand.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.segmentrouting.cli;
+
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.segmentrouting.SegmentRoutingService;
+import org.onosproject.segmentrouting.pwaas.L2TunnelHandler;
+
+
+/**
+ * Command to add a pseuwodire.
+ */
+@Command(scope = "onos", name = "pseudowire-add",
+        description = "Add a pseudowire to the network configuration, if it already exists update it.")
+public class PseudowireAddCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "pwId",
+            description = "Pseudowire ID",
+            required = true, multiValued = false)
+    String pwId;
+
+    @Argument(index = 1, name = "pwLabel",
+            description = "Pseudowire Label",
+            required = true, multiValued = false)
+    String pwLabel;
+
+    @Argument(index = 2, name = "mode",
+            description = "Mode used for pseudowire",
+            required = true, multiValued = false)
+    String mode;
+
+    @Argument(index = 3, name = "sDTag",
+            description = "Service delimiting tag",
+            required = true, multiValued = false)
+    String sDTag;
+
+    @Argument(index = 4, name = "cP1",
+            description = "Connection Point 1",
+            required = true, multiValued = false)
+    String cP1;
+
+    @Argument(index = 5, name = "cP1InnerVlan",
+            description = "Inner Vlan of Connection Point 1",
+            required = true, multiValued = false)
+    String cP1InnerVlan;
+
+    @Argument(index = 6, name = "cP1OuterVlan",
+            description = "Outer Vlan of Connection Point 1",
+            required = true, multiValued = false)
+    String cP1OuterVlan;
+
+    @Argument(index = 7, name = "cP2",
+            description = "Connection Point 2",
+            required = true, multiValued = false)
+    String cP2;
+
+    @Argument(index = 8, name = "cP2InnerVlan",
+            description = "Inner Vlan of Connection Point 2",
+            required = true, multiValued = false)
+    String cP2InnerVlan;
+
+    @Argument(index = 9, name = "cP2OuterVlan",
+            description = "Outer Vlan of Connection Point 2",
+            required = true, multiValued = false)
+    String cP2OuterVlan;
+
+    @Override
+    protected void execute() {
+
+        SegmentRoutingService srService =
+                AbstractShellCommand.get(SegmentRoutingService.class);
+
+        L2TunnelHandler.Result res = srService.addPseudowire(pwId, pwLabel,
+                                                             cP1, cP1InnerVlan, cP1OuterVlan,
+                                                             cP2, cP2InnerVlan, cP2OuterVlan,
+                                                             mode, sDTag);
+        switch (res) {
+            case ADDITION_ERROR:
+                print("Pseudowire could not be added, error in configuration, please check logs for more details!");
+                break;
+            case CONFIG_NOT_FOUND:
+                print("Configuration for pwaas was not found! Initialize the configuration first through netcfg.");
+                break;
+            default:
+                print("Pseudowire was added to the configuration succesfully, please do check logs for any errors!");
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/onosproject/segmentrouting/cli/PseudowireIdCompleter.java b/src/main/java/org/onosproject/segmentrouting/cli/PseudowireIdCompleter.java
new file mode 100644
index 0000000..9022bec
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/cli/PseudowireIdCompleter.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014-present Open Networking Foundation
+ *
+ * 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.segmentrouting.cli;
+
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.segmentrouting.SegmentRoutingService;
+import org.onosproject.segmentrouting.pwaas.DefaultL2Tunnel;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.stream.Collectors;
+
+/**
+ * Device ID completer.
+ */
+public class PseudowireIdCompleter implements Completer {
+    @Override
+    public int complete(String buffer, int cursor, List<String> candidates) {
+        // Delegate string completer
+        StringsCompleter delegate = new StringsCompleter();
+
+        SegmentRoutingService srService =
+                AbstractShellCommand.get(SegmentRoutingService.class);
+
+
+        List<DefaultL2Tunnel> tunnels = srService.getL2Tunnels();
+
+        // combine polices and tunnels to pseudowires
+        Iterator<String> pseudowires = tunnels.stream()
+                .map(l2Tunnel -> Long.toString(l2Tunnel.tunnelId()))
+                .collect(Collectors.toList()).iterator();
+
+        SortedSet<String> strings = delegate.getStrings();
+        while (pseudowires.hasNext()) {
+            strings.add(pseudowires.next());
+        }
+
+        // Now let the completer do the work for figuring out what to offer.
+        return delegate.complete(buffer, cursor, candidates);
+    }
+
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/cli/PseudowireListCommand.java b/src/main/java/org/onosproject/segmentrouting/cli/PseudowireListCommand.java
new file mode 100644
index 0000000..18625db
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/cli/PseudowireListCommand.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.segmentrouting.cli;
+
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.segmentrouting.SegmentRoutingService;
+import org.onosproject.segmentrouting.pwaas.DefaultL2Tunnel;
+import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelDescription;
+import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelPolicy;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Command to show the pseudowires.
+ */
+@Command(scope = "onos", name = "pseudowires",
+        description = "Lists all pseudowires")
+public class PseudowireListCommand extends AbstractShellCommand {
+
+    private static final String FORMAT_PSEUDOWIRE =
+            "Pseudowire id = %s \n" +
+                    "   mode : %s, sdTag : %s, pwLabel : %s \n" +
+                    "   cP1 : %s , cP1OuterTag : %s, cP1InnerTag : %s \n" +
+                    "   cP2 : %s , cP2OuterTag : %s, cP2InnerTag : %s \n" /* +
+                    "   Path used : (%s - %s) <-> (%s - %s) \n" */;
+
+                    // TODO:  uncomment string when path failures are fixed also for the links
+                    // TODO:  used in spine for pw traffic.
+    @Override
+    protected void execute() {
+
+        SegmentRoutingService srService =
+                AbstractShellCommand.get(SegmentRoutingService.class);
+
+        List<DefaultL2Tunnel> tunnels = srService.getL2Tunnels();
+        List<DefaultL2TunnelPolicy> policies = srService.getL2Policies();
+
+        // combine polices and tunnels to pseudowires
+        List<DefaultL2TunnelDescription> pseudowires = tunnels.stream()
+                                    .map(l2Tunnel -> {
+                                            DefaultL2TunnelPolicy policy = null;
+                                            for (DefaultL2TunnelPolicy l2Policy : policies) {
+                                                if (l2Policy.tunnelId() == l2Tunnel.tunnelId()) {
+                                                    policy = l2Policy;
+                                                    break;
+                                                }
+                                            }
+
+                                            return new DefaultL2TunnelDescription(l2Tunnel, policy);
+                                    })
+                                    .collect(Collectors.toList());
+
+        pseudowires.forEach(pw -> printPseudowire(pw));
+    }
+
+    private void printPseudowire(DefaultL2TunnelDescription pseudowire) {
+
+
+        print(FORMAT_PSEUDOWIRE, pseudowire.l2Tunnel().tunnelId(), pseudowire.l2Tunnel().pwMode(),
+              pseudowire.l2Tunnel().sdTag(), pseudowire.l2Tunnel().pwLabel(),
+              pseudowire.l2TunnelPolicy().cP1(), pseudowire.l2TunnelPolicy().cP1OuterTag(),
+              pseudowire.l2TunnelPolicy().cP1InnerTag(), pseudowire.l2TunnelPolicy().cP2(),
+              pseudowire.l2TunnelPolicy().cP2OuterTag(), pseudowire.l2TunnelPolicy().cP2InnerTag()/*,
+              pseudowire.l2Tunnel().pathUsed().get(0).src(), pseudowire.l2Tunnel().pathUsed().get(0).dst(),
+              pseudowire.l2Tunnel().pathUsed().get(1).src(), pseudowire.l2Tunnel().pathUsed().get(1).dst()*/);
+
+        // TODO: uncomment arguments when path issue is fixed for spine switches
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/onosproject/segmentrouting/cli/PseudowireRemoveCommand.java b/src/main/java/org/onosproject/segmentrouting/cli/PseudowireRemoveCommand.java
new file mode 100644
index 0000000..782b339
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/cli/PseudowireRemoveCommand.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.segmentrouting.cli;
+
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.segmentrouting.SegmentRoutingManager;
+import org.onosproject.segmentrouting.SegmentRoutingService;
+import org.onosproject.segmentrouting.pwaas.L2TunnelHandler;
+
+
+/**
+ * Command to remove a pseudowire.
+ */
+@Command(scope = "onos", name = "pseudowire-remove",
+        description = "Remove a pseudowire")
+public class PseudowireRemoveCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "pwId",
+            description = "pseudowire ID",
+            required = true, multiValued = false)
+    String pwId;
+
+    @Override
+    protected void execute() {
+
+        SegmentRoutingService srService =
+                AbstractShellCommand.get(SegmentRoutingService.class);
+
+        // remove the pseudowire
+        SegmentRoutingManager mngr = (SegmentRoutingManager) srService;
+        L2TunnelHandler.Result res = mngr.removePseudowire(pwId);
+
+        switch (res) {
+            case REMOVAL_ERROR:
+                error("Error in deletion, pseudowire not found!");
+                break;
+            case CONFIG_NOT_FOUND:
+                error("Could not fetch pseudowire class configuration!");
+                break;
+            default:
+                return;
+            }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java b/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
index 18169bb..321cf40 100644
--- a/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
+++ b/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
@@ -77,6 +77,7 @@
         Map<Integer, Set<Integer>> adjacencySids;
         DeviceId pairDeviceId;
         PortNumber pairLocalPort;
+        int pwRoutingLabel;
 
         public SegmentRouterInfo() {
             gatewayIps = HashMultimap.create();
@@ -113,6 +114,7 @@
             info.adjacencySids = config.adjacencySids();
             info.pairDeviceId = config.pairDeviceId();
             info.pairLocalPort = config.pairLocalPort();
+            info.pwRoutingLabel = info.ipv4NodeSid + 1000;
             deviceConfigMap.put(info.deviceId, info);
             log.debug("Read device config for device: {}", info.deviceId);
             /*
@@ -188,6 +190,7 @@
     @Override
     public int getIPv4SegmentId(DeviceId deviceId) throws DeviceConfigNotFoundException {
         SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
+        log.info("DEVICE MAP IS {}", deviceConfigMap);
         if (srinfo != null) {
             log.trace("getIPv4SegmentId for device{} is {}", deviceId, srinfo.ipv4NodeSid);
             return srinfo.ipv4NodeSid;
@@ -209,6 +212,18 @@
         }
     }
 
+    @Override
+    public int getPWRoutingLabel(DeviceId deviceId) throws DeviceConfigNotFoundException {
+        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
+        if (srinfo != null) {
+            log.trace("pwRoutingLabel for device{} is {}", deviceId, srinfo.pwRoutingLabel);
+            return srinfo.pwRoutingLabel;
+        } else {
+            String message = "getPWRoutingLabel fails for device: " + deviceId + ".";
+            throw new DeviceConfigNotFoundException(message);
+        }
+    }
+
     /**
      * Returns the IPv4 Node segment id of a segment router given its Router mac address.
      *
diff --git a/src/main/java/org/onosproject/segmentrouting/config/DeviceProperties.java b/src/main/java/org/onosproject/segmentrouting/config/DeviceProperties.java
index 64a2df7..2d0bbd2 100644
--- a/src/main/java/org/onosproject/segmentrouting/config/DeviceProperties.java
+++ b/src/main/java/org/onosproject/segmentrouting/config/DeviceProperties.java
@@ -66,6 +66,14 @@
     MacAddress getDeviceMac(DeviceId deviceId) throws DeviceConfigNotFoundException;
 
     /**
+     *
+     * @param deviceId device identifier
+     * @throws DeviceConfigNotFoundException if the device configuration is not found
+     * @return the pseudowire routing label for a leaf node
+     */
+    int getPWRoutingLabel(DeviceId deviceId) throws DeviceConfigNotFoundException;
+
+    /**
      * Returns the router ipv4 address of a segment router.
      *
      * @param deviceId device identifier
diff --git a/src/main/java/org/onosproject/segmentrouting/config/PwaasConfig.java b/src/main/java/org/onosproject/segmentrouting/config/PwaasConfig.java
index 74541cd..ed2c067 100644
--- a/src/main/java/org/onosproject/segmentrouting/config/PwaasConfig.java
+++ b/src/main/java/org/onosproject/segmentrouting/config/PwaasConfig.java
@@ -17,13 +17,19 @@
 package org.onosproject.segmentrouting.config;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.collect.ImmutableSet;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onlab.packet.MplsLabel;
 import org.onlab.packet.VlanId;
+import org.onosproject.cli.AbstractShellCommand;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.config.Config;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.intf.InterfaceService;
 import org.onosproject.segmentrouting.pwaas.DefaultL2Tunnel;
 import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelDescription;
 import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelPolicy;
@@ -31,13 +37,23 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * App configuration object for Pwaas.
  */
 public class PwaasConfig extends Config<ApplicationId> {
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    public DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    public InterfaceService intfService;
+
     private static Logger log = LoggerFactory
             .getLogger(PwaasConfig.class);
 
@@ -48,10 +64,24 @@
     private static final String SRC_INNER_TAG = "cP1InnerTag";
     private static final String DST_INNER_TAG = "cP2InnerTag";
     private static final String MODE = "mode";
-    private static final String ALL_VLAN = "allVlan";
     private static final String SD_TAG = "sdTag";
     private static final String PW_LABEL = "pwLabel";
 
+    public PwaasConfig(DeviceService devS, InterfaceService intfS) {
+
+        super();
+
+        deviceService = devS;
+        intfService = intfS;
+    }
+
+    public PwaasConfig() {
+
+        super();
+
+        deviceService = AbstractShellCommand.get(DeviceService.class);
+        intfService = AbstractShellCommand.get(InterfaceService.class);
+    }
     /**
      * Error message for missing parameters.
      */
@@ -63,21 +93,377 @@
     private static final String INVALID_L2_MODE = "Invalid pseudo wire mode";
 
     /**
+     * Error message for invalid VLAN.
+     */
+    private static final String INVALID_VLAN = "Vlan should be either int or */-";
+
+    /**
+     * Error message for invalid PW label.
+     */
+    private static final String INVALID_PW_LABEL = "Pseudowire label should be an integer";
+
+    /**
      * Verify if the pwaas configuration block is valid.
      *
+     * Here we try to ensure that the provided pseudowires will get instantiated
+     * correctly in the network. We also check for any collisions with already used
+     * interfaces and also between different pseudowires. Most of the restrictions stem
+     * from the fact that all vlan matching is done in table 10 of ofdpa.
+     *
      * @return true, if the configuration block is valid.
      *         False otherwise.
      */
     @Override
     public boolean isValid() {
+
+        Set<DefaultL2TunnelDescription> pseudowires;
         try {
-            getPwIds().forEach(this::getPwDescription);
+            pseudowires = getPwIds().stream()
+                    .map(this::getPwDescription)
+                    .collect(Collectors.toSet());
+
         } catch (IllegalArgumentException e) {
             log.warn("{}", e.getMessage());
             return false;
         }
-        return true;
 
+        // check semantics now and return
+        return configurationValidity(pseudowires);
+    }
+
+    /**
+     * Helper method to verify if the tunnel is whether or not
+     * supported.
+     *
+     * @param l2Tunnel the tunnel to verify
+     * @return the result of the verification
+     */
+    private void verifyTunnel(DefaultL2Tunnel l2Tunnel) {
+
+        // Service delimiting tag not supported yet.
+        if (!l2Tunnel.sdTag().equals(VlanId.NONE)) {
+            throw new IllegalArgumentException(String.format("Service delimiting tag not supported yet for " +
+                                                             "pseudowire %d.", l2Tunnel.tunnelId()));
+        }
+
+        // Tag mode not supported yet.
+        if (l2Tunnel.pwMode() == L2Mode.TAGGED) {
+            throw new IllegalArgumentException(String.format("Tagged mode not supported yet for pseudowire %d.",
+                                                                            l2Tunnel.tunnelId()));
+        }
+
+        // Raw mode without service delimiting tag
+        // is the only mode supported for now.
+    }
+
+    /**
+     * Helper method to verify if the policy is whether or not
+     * supported and if policy will be successfully instantiated in the
+     * network.
+     *
+     * @param ingressInner the ingress inner tag
+     * @param ingressOuter the ingress outer tag
+     * @param egressInner the egress inner tag
+     * @param egressOuter the egress outer tag
+     * @return the result of verification
+     */
+    private void verifyPolicy(ConnectPoint cP1,
+                              ConnectPoint cP2,
+                              VlanId ingressInner,
+                              VlanId ingressOuter,
+                              VlanId egressInner,
+                              VlanId egressOuter,
+                              Long tunnelId) {
+
+        if (cP1.deviceId().equals(cP2.deviceId())) {
+            throw new IllegalArgumentException(String.format("Pseudowire connection points can not reside in the " +
+                                                             "same node, in pseudowire %d.", tunnelId));
+        }
+
+        // We can have multiple tags, all of them can be NONE,
+        // indicating untagged traffic, however, the outer tag can
+        // not have value if the inner tag is None
+        if (ingressInner.equals(VlanId.NONE) && !ingressOuter.equals(VlanId.NONE)) {
+            throw new IllegalArgumentException(String.format("Inner tag should not be empty when " +
+                                                             "outer tag is set for pseudowire %d for cP1.",
+                                                              tunnelId));
+        }
+
+        if (egressInner.equals(VlanId.NONE) && !egressOuter.equals(VlanId.NONE)) {
+            throw new IllegalArgumentException(String.valueOf(String.format("Inner tag should not be empty when" +
+                                                                            " outer tag is set for pseudowire %d " +
+                                                                            "for cP2.", tunnelId)));
+        }
+
+        if (ingressInner.equals(VlanId.ANY) ||
+                ingressOuter.equals(VlanId.ANY) ||
+                egressInner.equals(VlanId.ANY) ||
+                egressOuter.equals(VlanId.ANY)) {
+            throw new IllegalArgumentException(String.valueOf(String.format("Wildcard VLAN matching not yet " +
+                                                                            "supported for pseudowire %d.",
+                                                                            tunnelId)));
+        }
+
+        if (((!ingressOuter.equals(VlanId.NONE) && !ingressOuter.equals(VlanId.NONE)) &&
+                (egressOuter.equals(VlanId.NONE) && egressInner.equals(VlanId.NONE)))
+                || ((ingressOuter.equals(VlanId.NONE) && ingressOuter.equals(VlanId.NONE)) &&
+                (!egressOuter.equals(VlanId.NONE) && !egressInner.equals(VlanId.NONE)))) {
+            throw new IllegalArgumentException(String.valueOf(String.format("Support for double tag <-> untag is not" +
+                                                                                    "supported for pseudowire %d.",
+                                                                            tunnelId)));
+        }
+        if ((!ingressInner.equals(VlanId.NONE) &&
+                ingressOuter.equals(VlanId.NONE) &&
+                !egressOuter.equals(VlanId.NONE))
+           || (!ingressOuter.equals(VlanId.NONE) &&
+                egressOuter.equals(VlanId.NONE) &&
+                !ingressOuter.equals(VlanId.NONE))) {
+                throw new IllegalArgumentException(String.valueOf(String.format("Support for double-tag<->" +
+                                                                                "single-tag is not supported" +
+                                                                                " for pseudowire %d.", tunnelId)));
+        }
+
+        if ((ingressInner.equals(VlanId.NONE) && !egressInner.equals(VlanId.NONE))
+                || (!ingressInner.equals(VlanId.NONE) && egressInner.equals(VlanId.NONE))) {
+            throw new IllegalArgumentException(String.valueOf(String.format("single-tag <-> untag is not supported" +
+                                                                            " for pseudowire %d.", tunnelId)));
+        }
+
+
+        if (!ingressInner.equals(egressInner) && !ingressOuter.equals(egressOuter)) {
+            throw new IllegalArgumentException(String.valueOf(String.format("We do not support changing both tags " +
+                                                                             "in double tagged pws, only the outer," +
+                                                                             " for pseudowire %d.", tunnelId)));
+        }
+
+        // check if cp1 and port of cp1 exist
+        if (deviceService.getDevice(cP1.deviceId()) == null) {
+            throw new IllegalArgumentException(String.valueOf(String.format("cP1 device %s does not exist for" +
+                                                                            " pseudowire %d.", cP1.deviceId(),
+                                                                            tunnelId)));
+        }
+
+        if (deviceService.getPort(cP1) == null) {
+            throw new IllegalArgumentException(String.valueOf(String.format("Port %s for cP1 device %s does not" +
+                                                                            " exist for pseudowire %d.", cP1.port(),
+                                                                            cP1.deviceId(), tunnelId)));
+        }
+
+        // check if cp2 and port of cp2 exist
+        if (deviceService.getDevice(cP2.deviceId()) == null) {
+            throw new IllegalArgumentException(String.valueOf(String.format("cP2 device %s does not exist for" +
+                                                                            " pseudowire %d.", cP2.deviceId(),
+                                                                            tunnelId)));
+        }
+
+        if (deviceService.getPort(cP2) == null) {
+            throw new IllegalArgumentException(String.valueOf(String.format("Port %s for cP2 device %s does " +
+                                                                            "not exist for pseudowire %d.",
+                                                                            cP2.port(), cP2.deviceId(), tunnelId)));
+        }
+    }
+
+    /**
+     * Verifies that the pseudowires will not conflict with each other.
+     *
+     * Further, check if vlans for connect points are already used.
+     *
+     * @param tunnel Tunnel for pw
+     * @param policy Policy for pw
+     * @param labelSet Label set used so far with this configuration
+     * @param vlanSet Vlan set used with this configuration
+     * @param tunnelSet Tunnel set used with this configuration
+     */
+    private void verifyGlobalValidity(DefaultL2Tunnel tunnel,
+                                      DefaultL2TunnelPolicy policy,
+                                      Set<MplsLabel> labelSet,
+                                      Map<ConnectPoint, Set<VlanId>> vlanSet,
+                                      Set<Long> tunnelSet) {
+
+        if (tunnelSet.contains(tunnel.tunnelId())) {
+            throw new IllegalArgumentException(String.valueOf(String.format("Tunnel Id %d already used by" +
+                                                                           " another pseudowire, in " +
+                                                                           "pseudowire %d!", tunnel.tunnelId(),
+                                                                            tunnel.tunnelId())));
+        }
+        tunnelSet.add(tunnel.tunnelId());
+
+        // check if tunnel id is used again
+        ConnectPoint cP1 = policy.cP1();
+        ConnectPoint cP2 = policy.cP2();
+
+        // insert cps to hashmap if this is the first time seen
+        if (!vlanSet.containsKey(cP1)) {
+            vlanSet.put(cP1, new HashSet<VlanId>());
+        }
+        if (!vlanSet.containsKey(cP2)) {
+            vlanSet.put(cP2, new HashSet<VlanId>());
+        }
+
+        // if single tagged or untagged vlan is the inner
+        // if double tagged vlan is the outer
+        VlanId vlanToCheckCP1;
+        if (policy.cP1OuterTag().equals(VlanId.NONE)) {
+            vlanToCheckCP1 = policy.cP1InnerTag();
+        } else {
+            vlanToCheckCP1 = policy.cP1OuterTag();
+        }
+
+        VlanId vlanToCheckCP2;
+        if (policy.cP2OuterTag().equals(VlanId.NONE)) {
+            vlanToCheckCP2 = policy.cP2InnerTag();
+        } else {
+            vlanToCheckCP2 = policy.cP2OuterTag();
+        }
+
+        if (labelSet.contains(tunnel.pwLabel())) {
+            throw new IllegalArgumentException(String.valueOf(String.format("Label %s already used by another" +
+                                                                            " pseudowire, in pseudowire %d!",
+                                                                            tunnel.pwLabel(), tunnel.tunnelId())));
+        }
+        labelSet.add(tunnel.pwLabel());
+
+        if (vlanSet.get(cP1).contains(vlanToCheckCP1)) {
+            throw new IllegalArgumentException(String.valueOf(String.format("Vlan '%s' for cP1 %s already used " +
+                                                                            "by another pseudowire, in pseudowire" +
+                                                                            " %d!", vlanToCheckCP1,  cP1,
+                                                                            tunnel.tunnelId())));
+        }
+        vlanSet.get(cP1).add(vlanToCheckCP1);
+
+        if (vlanSet.get(cP2).contains(vlanToCheckCP2)) {
+            throw new IllegalArgumentException(String.valueOf(String.format("Vlan '%s' for cP2 %s already used" +
+                                                                            " by another pseudowire, in" +
+                                                                            " pseudowire %d!", vlanToCheckCP2, cP2,
+                                                                            tunnel.tunnelId())));
+        }
+        vlanSet.get(cP2).add(vlanToCheckCP2);
+
+        // check that vlans for the connect points are not used
+        intfService.getInterfacesByPort(cP1).stream()
+                .forEach(intf -> {
+
+                    // check if tagged pw affects tagged interface
+                    if (intf.vlanTagged().contains(vlanToCheckCP1)) {
+                        throw new IllegalArgumentException(String.valueOf(String.format("Vlan '%s' for cP1 %s already" +
+                                                                                        " used for this interface, in" +
+                                                                                        " pseudowire %d!",
+                                                                                        vlanToCheckCP1, cP1,
+                                                                                        tunnel.tunnelId())));
+                    }
+
+                    // if vlanNative != null this interface is configured with untagged traffic also
+                    // check if it collides with untagged interface
+                    if ((intf.vlanNative() != null) && vlanToCheckCP1.equals(VlanId.NONE)) {
+                        throw new IllegalArgumentException(String.valueOf(String.format("Untagged traffic for cP1 " +
+                                                                                         "%s already used for this " +
+                                                                                         "interface, in pseudowire " +
+                                                                                         "%d!", cP1,
+                                                                                         tunnel.tunnelId())));
+                    }
+
+                    // if vlanUntagged != null this interface is configured only with untagged traffic
+                    // check if it collides with untagged interface
+                    if ((intf.vlanUntagged() != null) && vlanToCheckCP1.equals(VlanId.NONE)) {
+                        throw new IllegalArgumentException(String.valueOf(String.format("Untagged traffic for " +
+                                                                                         "cP1 %s already" +
+                                                                                         " used for this interface," +
+                                                                                         " in pseudowire %d!",
+                                                                                         cP1, tunnel.tunnelId())));
+                    }
+                });
+
+        intfService.getInterfacesByPort(cP2).stream()
+                .forEach(intf -> {
+                    if (intf.vlanTagged().contains(vlanToCheckCP2)) {
+                        throw new IllegalArgumentException(String.valueOf(String.format("Vlan '%s' for cP2 %s already" +
+                                                                                        " used for  this interface, " +
+                                                                                        "in pseudowire %d!",
+                                                                                        vlanToCheckCP2, cP2,
+                                                                                        tunnel.tunnelId())));
+                    }
+
+                    // if vlanNative != null this interface is configured with untagged traffic also
+                    // check if it collides with untagged interface
+                    if ((intf.vlanNative() != null) && vlanToCheckCP2.equals(VlanId.NONE)) {
+                        throw new IllegalArgumentException(String.valueOf(String.format("Untagged traffic for cP2 %s " +
+                                                                                        "already used for this" +
+                                                                                        " interface, " +
+                                                                                        "in pseudowire %d!",
+                                                                                        cP2, tunnel.tunnelId())));
+                    }
+
+                    // if vlanUntagged != null this interface is configured only with untagged traffic
+                    // check if it collides with untagged interface
+                    if ((intf.vlanUntagged() != null) && vlanToCheckCP2.equals(VlanId.NONE)) {
+                        throw new IllegalArgumentException(String.valueOf(String.format("Untagged traffic for cP2 %s" +
+                                                                                        " already" +
+                                                                                        " used for this interface, " +
+                                                                                        "in pseudowire %d!",
+                                                                                        cP2, tunnel.tunnelId())));
+                    }
+                });
+
+    }
+
+    /**
+     * Helper method to verify the integrity of the pseudo wire.
+     *
+     * @param l2TunnelDescription the pseudo wire description
+     * @return the result of the check
+     */
+    private void verifyPseudoWire(DefaultL2TunnelDescription l2TunnelDescription,
+                                  Set<MplsLabel> labelSet,
+                                  Map<ConnectPoint, Set<VlanId>> vlanset,
+                                  Set<Long> tunnelSet) {
+
+        DefaultL2Tunnel l2Tunnel = l2TunnelDescription.l2Tunnel();
+        DefaultL2TunnelPolicy l2TunnelPolicy = l2TunnelDescription.l2TunnelPolicy();
+
+        verifyTunnel(l2Tunnel);
+
+        verifyPolicy(
+                l2TunnelPolicy.cP1(),
+                l2TunnelPolicy.cP2(),
+                l2TunnelPolicy.cP1InnerTag(),
+                l2TunnelPolicy.cP1OuterTag(),
+                l2TunnelPolicy.cP2InnerTag(),
+                l2TunnelPolicy.cP2OuterTag(),
+                l2Tunnel.tunnelId()
+        );
+
+        verifyGlobalValidity(l2Tunnel,
+                             l2TunnelPolicy,
+                             labelSet,
+                             vlanset,
+                             tunnelSet);
+
+    }
+
+    /**
+     * Checks if the configured pseudowires will create problems in the network.
+     * If yes, then no pseudowires is deployed from this configuration.
+     *
+     * @param pseudowires Set of pseudowries to validate
+     * @return returns true if everything goes well.
+     */
+    public boolean configurationValidity(Set<DefaultL2TunnelDescription> pseudowires) {
+
+        // structures to keep pw information
+        // in order to see if instantiating them will create
+        // problems
+        Set<Long> tunIds = new HashSet<>();
+        Set<MplsLabel> labelsUsed = new HashSet<>();
+        Map<ConnectPoint, Set<VlanId>> vlanIds = new HashMap<>();
+
+        // check that pseudowires can be instantiated in the network
+        // we try to guarantee that all the pws will work before
+        // instantiating any of them
+        for (DefaultL2TunnelDescription pw : pseudowires) {
+            verifyPseudoWire(pw, labelsUsed, vlanIds, tunIds);
+        }
+
+        return true;
     }
 
     /**
@@ -96,6 +482,61 @@
     }
 
     /**
+     * Parses a vlan as a string. Returns the VlanId if
+     * provided String can be parsed as an integer or is '' / '*'
+     *
+     * @param vlan string as read from configuration
+     * @return VlanId
+     * @throws IllegalArgumentException if wrong format of vlan
+     */
+    public VlanId parseVlan(String vlan) {
+
+        if (vlan.equals("*") || vlan.equals("Any")) {
+            return VlanId.vlanId("Any");
+        } else if (vlan.equals("") || vlan.equals("None")) {
+            return VlanId.vlanId("None");
+        } else {
+            try {
+                VlanId newVlan = VlanId.vlanId(vlan);
+                return newVlan;
+            } catch (IllegalArgumentException e) {
+                throw new IllegalArgumentException(INVALID_VLAN);
+            }
+        }
+    }
+
+    /**
+     *
+     * @param mode RAW or TAGGED
+     * @return the L2Mode if input is correct
+     * @throws  IllegalArgumentException if not supported mode
+     */
+    public L2Mode parseMode(String mode) {
+
+        if (!mode.equals("RAW") && !mode.equals("TAGGED")) {
+            throw  new IllegalArgumentException(INVALID_L2_MODE);
+        }
+
+        return L2Mode.valueOf(mode);
+    }
+
+    /**
+     *
+     * @param label the mpls label of the pseudowire
+     * @return the MplsLabel
+     * @throws IllegalArgumentException if label is invalid
+     */
+    public MplsLabel parsePWLabel(String label) {
+
+        try {
+            MplsLabel pwLabel = MplsLabel.mplsLabel(label);
+            return pwLabel;
+        } catch (Exception e) {
+            throw new IllegalArgumentException(INVALID_PW_LABEL);
+        }
+    }
+
+    /**
      * Returns pw description of given pseudo wire id.
      *
      * @param tunnelId pseudo wire key
@@ -107,7 +548,7 @@
         if (!hasFields((ObjectNode) pwDescription,
                       SRC_CP, SRC_INNER_TAG, SRC_OUTER_TAG,
                       DST_CP, DST_INNER_TAG, DST_OUTER_TAG,
-                      MODE, ALL_VLAN, SD_TAG, PW_LABEL)) {
+                      MODE, SD_TAG, PW_LABEL)) {
             throw new IllegalArgumentException(MISSING_PARAMS);
         }
         String tempString;
@@ -119,28 +560,25 @@
         ConnectPoint dstCp = ConnectPoint.deviceConnectPoint(tempString);
 
         tempString = pwDescription.get(SRC_INNER_TAG).asText();
-        VlanId srcInnerTag = VlanId.vlanId(tempString);
+        VlanId srcInnerTag = parseVlan(tempString);
 
         tempString = pwDescription.get(SRC_OUTER_TAG).asText();
-        VlanId srcOuterTag = VlanId.vlanId(tempString);
+        VlanId srcOuterTag = parseVlan(tempString);
 
         tempString = pwDescription.get(DST_INNER_TAG).asText();
-        VlanId dstInnerTag = VlanId.vlanId(tempString);
+        VlanId dstInnerTag = parseVlan(tempString);
 
         tempString = pwDescription.get(DST_OUTER_TAG).asText();
-        VlanId dstOuterTag = VlanId.vlanId(tempString);
+        VlanId dstOuterTag = parseVlan(tempString);
 
         tempString = pwDescription.get(MODE).asText();
-
-        L2Mode l2Mode = L2Mode.valueOf(tempString);
-
-        boolean allVlan = pwDescription.get(ALL_VLAN).asBoolean();
+        L2Mode l2Mode = parseMode(tempString);
 
         tempString = pwDescription.get(SD_TAG).asText();
-        VlanId sdTag = VlanId.vlanId(tempString);
+        VlanId sdTag = parseVlan(tempString);
 
         tempString = pwDescription.get(PW_LABEL).asText();
-        MplsLabel pwLabel = MplsLabel.mplsLabel(tempString);
+        MplsLabel pwLabel = parsePWLabel(tempString);
 
         DefaultL2Tunnel l2Tunnel = new DefaultL2Tunnel(
                 l2Mode,
@@ -156,11 +594,76 @@
                 srcOuterTag,
                 dstCp,
                 dstInnerTag,
-                dstOuterTag,
-                allVlan
+                dstOuterTag
         );
 
         return new DefaultL2TunnelDescription(l2Tunnel, l2TunnelPolicy);
     }
 
+    /**
+     * Removes a pseudowire from the configuration tree.
+     * @param pwId Pseudowire id
+     * @return null if pwId did not exist, or the object representing the
+     * udpated configuration tree
+     */
+    public ObjectNode removePseudowire(String pwId) {
+
+        JsonNode value = object.remove(pwId);
+        if (value == null) {
+            return (ObjectNode) value;
+        } else {
+            return object;
+        }
+    }
+
+    /**
+     * Adds a pseudowire to the configuration tree of pwwas. It also checks
+     * if the configuration is valid, if not return null and does not add the node,
+     * if yes return the new configuration. Caller will propagate update events.
+     *
+     * If the pseudowire already exists in the configuration it gets updated.
+     *
+     * @param tunnelId Id of tunnel
+     * @param pwLabel PW label of tunnel
+     * @param cP1 Connection point 1
+     * @param cP1InnerVlan Inner vlan of cp1
+     * @param cP1OuterVlan Outer vlan of cp2
+     * @param cP2 Connection point 2
+     * @param cP2InnerVlan Inner vlan of cp2
+     * @param cP2OuterVlan Outer vlan of cp2
+     * @param mode Mode for the pw
+     * @param sdTag Service delimiting tag for the pw
+     * @return The ObjectNode config if configuration is valid with the new pseudowire
+     * or null.
+     */
+    public ObjectNode addPseudowire(String tunnelId, String pwLabel, String cP1,
+                                    String cP1InnerVlan, String cP1OuterVlan, String cP2,
+                                    String cP2InnerVlan, String cP2OuterVlan,
+                                    String mode, String sdTag) {
+
+
+        ObjectNode newPw = new ObjectNode(JsonNodeFactory.instance);
+
+        // add fields for pseudowire
+        newPw.put(SRC_CP, cP1);
+        newPw.put(DST_CP, cP2);
+        newPw.put(PW_LABEL, pwLabel);
+        newPw.put(SRC_INNER_TAG, cP1InnerVlan);
+        newPw.put(SRC_OUTER_TAG, cP1OuterVlan);
+        newPw.put(DST_INNER_TAG, cP2InnerVlan);
+        newPw.put(DST_OUTER_TAG, cP2OuterVlan);
+        newPw.put(SD_TAG, sdTag);
+        newPw.put(MODE, mode);
+
+        object.set(tunnelId, newPw);
+        try {
+            isValid();
+        } catch (IllegalArgumentException e) {
+            log.info("Pseudowire could not be created : {}", e);
+            object.remove(tunnelId);
+            return null;
+        }
+
+        return object;
+    }
 }
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
index 848e2ca..71f5c15 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
@@ -910,6 +910,7 @@
                                                       index));
                     foundSingleNeighbor = true;
                 }
+
                 for (PortNumber sp : neighborPorts) {
                     TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
                             .builder();
diff --git a/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2Tunnel.java b/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2Tunnel.java
index 8855a21..bddc191 100644
--- a/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2Tunnel.java
+++ b/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2Tunnel.java
@@ -20,7 +20,10 @@
 import com.google.common.base.MoreObjects;
 import org.onlab.packet.MplsLabel;
 import org.onlab.packet.VlanId;
+import org.onosproject.net.Link;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 import static com.google.common.base.Preconditions.checkArgument;
@@ -52,21 +55,19 @@
      */
     private MplsLabel interCoLabel;
 
+    private List<Link> pathUsed;
+
     /**
      * Creates a inter-co l2 tunnel using the
      * supplied parameters.
      *
-     * @param mode the tunnel mode
-     * @param sdtag the service delimiting tag
-     * @param tunnelId the tunnel id
-     * @param pwLabel the pseudo wire label
+     * @param mode         the tunnel mode
+     * @param sdtag        the service delimiting tag
+     * @param tunnelId     the tunnel id
+     * @param pwLabel      the pseudo wire label
      * @param interCoLabel the inter central office label
      */
-    public DefaultL2Tunnel(L2Mode mode,
-                           VlanId sdtag,
-                           long tunnelId,
-                           MplsLabel pwLabel,
-                           MplsLabel interCoLabel) {
+    public DefaultL2Tunnel(L2Mode mode, VlanId sdtag, long tunnelId, MplsLabel pwLabel, MplsLabel interCoLabel) {
         checkNotNull(mode);
         checkArgument(tunnelId > 0);
         checkNotNull(pwLabel);
@@ -80,24 +81,36 @@
     }
 
     /**
-     * Creates a intra-co l2 tunnel using the
-     * supplied parameters.
+     * Creates a l2Tunnel from a given tunnel.
      *
-     * @param mode the tunnel mode
-     * @param sdtag the service delimiting tag
-     * @param tunnelId the tunnel id
-     * @param pwLabel the pseudo wire label
+     * @param l2Tunnel to replicate
      */
-    public DefaultL2Tunnel(L2Mode mode,
-                           VlanId sdtag,
-                           long tunnelId,
-                           MplsLabel pwLabel) {
-        this(mode, sdtag, tunnelId, pwLabel, MplsLabel.mplsLabel(MplsLabel.MAX_MPLS));
+    public DefaultL2Tunnel(DefaultL2Tunnel l2Tunnel) {
+
+        this.pwMode = l2Tunnel.pwMode();
+        this.sdTag = l2Tunnel.sdTag();
+        this.tunnelId = l2Tunnel.tunnelId();
+        this.pwLabel = l2Tunnel.pwLabel();
+        this.interCoLabel = l2Tunnel.interCoLabel();
+        this.pathUsed = l2Tunnel.pathUsed();
     }
 
     /**
-     * Creates an empty l2 tunnel.
+     * Creates a intra-co l2 tunnel using the
+     * supplied parameters.
      *
+     * @param mode     the tunnel mode
+     * @param sdtag    the service delimiting tag
+     * @param tunnelId the tunnel id
+     * @param pwLabel  the pseudo wire label
+     */
+    public DefaultL2Tunnel(L2Mode mode, VlanId sdtag, long tunnelId, MplsLabel pwLabel) {
+        this(mode, sdtag, tunnelId, pwLabel, MplsLabel.mplsLabel(MplsLabel.MAX_MPLS));
+    }
+
+
+    /**
+     * Creates an empty l2 tunnel.
      **/
     public DefaultL2Tunnel() {
         this.pwMode = null;
@@ -145,6 +158,24 @@
     }
 
     /**
+     * Set the path for the pseudowire.
+     *
+     * @param path The path to set
+     */
+    public void setPath(List<Link> path) {
+        pathUsed = new ArrayList<>(path);
+    }
+
+    /**
+     * Returns the used path of the pseudowire.
+     *
+     * @return pathUsed
+     */
+    public List<Link> pathUsed() {
+        return pathUsed;
+    }
+
+    /**
      * Returns the inter-co label.
      *
      * @return the mpls inter-co label
diff --git a/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelPolicy.java b/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelPolicy.java
index 6e797d2..ac52f3e 100644
--- a/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelPolicy.java
+++ b/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelPolicy.java
@@ -57,10 +57,6 @@
      * cP2 outer vlan tag.
      */
     private VlanId cP2OuterTag;
-    /**
-     * Boolean value to indicate if the pseudo wire is port based.
-     */
-    private boolean allVlan;
 
     /**
      * Creates a default l2 tunnel policy using
@@ -73,12 +69,10 @@
      * @param cP2 the second connect point
      * @param cP2InnerTag the cP2 inner tag
      * @param cP2OuterTag the cP2 outer tag
-     * @param allVlan if the tunnel is port based or not
      */
     public DefaultL2TunnelPolicy(long tunnelId,
                                  ConnectPoint cP1, VlanId cP1InnerTag, VlanId cP1OuterTag,
-                                 ConnectPoint cP2, VlanId cP2InnerTag, VlanId cP2OuterTag,
-                                 boolean allVlan) {
+                                 ConnectPoint cP2, VlanId cP2InnerTag, VlanId cP2OuterTag) {
         this.cP1 = checkNotNull(cP1);
         this.cP2 = checkNotNull(cP2);
         this.tunnelId = tunnelId;
@@ -86,7 +80,21 @@
         this.cP1OuterTag = cP1OuterTag;
         this.cP2InnerTag = cP2InnerTag;
         this.cP2OuterTag = cP2OuterTag;
-        this.allVlan = allVlan;
+    }
+
+    /**
+     * Creates a default l2 policy given the provided policy.
+     * @param policy L2policy to replicate
+     */
+    public DefaultL2TunnelPolicy(DefaultL2TunnelPolicy policy) {
+
+        this.cP1 = policy.cP1;
+        this.cP2 = policy.cP2;
+        this.tunnelId = policy.tunnelId;
+        this.cP1InnerTag = policy.cP1InnerTag;
+        this.cP1OuterTag = policy.cP1OuterTag;
+        this.cP2InnerTag = policy.cP2InnerTag;
+        this.cP2OuterTag = policy.cP2OuterTag;
     }
 
     /**
@@ -144,16 +152,6 @@
     }
 
     /**
-     * Return all vlan value.
-     *
-     * @return true, if the pw is port based. False if the traffic is sliced
-     *         through the inner and outer tags
-     */
-    public boolean isAllVlan() {
-        return allVlan;
-    }
-
-    /**
      * Returns the tunnel ID of the policy.
      *
      * @return Tunnel ID
@@ -170,8 +168,7 @@
                             cP1InnerTag,
                             cP1OuterTag,
                             cP2InnerTag,
-                            cP2OuterTag,
-                            allVlan
+                            cP2OuterTag
         );
     }
 
@@ -189,8 +186,7 @@
                     this.cP1InnerTag.equals(that.cP1InnerTag) &&
                     this.cP1OuterTag.equals(that.cP1OuterTag) &&
                     this.cP2InnerTag.equals(that.cP2InnerTag) &&
-                    this.cP2OuterTag.equals(that.cP2OuterTag) &&
-                    this.allVlan == that.allVlan) {
+                    this.cP2OuterTag.equals(that.cP2OuterTag)) {
                 return true;
             }
         }
@@ -208,7 +204,6 @@
                 .add("cP1OuterTag", cP1OuterTag())
                 .add("cP2InnerTag", cP2InnerTag())
                 .add("cP2OuterTag", cP2OuterTag())
-                .add("allVlan", isAllVlan())
                 .toString();
     }
 
diff --git a/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelHandler.java b/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelHandler.java
index 4c7e3d2..8b334ea 100644
--- a/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelHandler.java
@@ -25,6 +25,7 @@
 import org.onlab.packet.VlanId;
 import org.onlab.util.KryoNamespace;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultLink;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.DisjointPath;
 import org.onosproject.net.Link;
@@ -52,6 +53,7 @@
 import org.onosproject.store.serializers.KryoNamespaces;
 import org.onosproject.store.service.ConsistentMap;
 import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.Versioned;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -60,9 +62,7 @@
 import java.util.concurrent.CompletableFuture;
 import java.util.stream.Collectors;
 
-import static com.google.common.base.Preconditions.checkState;
 import static org.onosproject.net.flowobjective.ForwardingObjective.Flag.VERSATILE;
-import static org.onosproject.segmentrouting.pwaas.L2Mode.TAGGED;
 import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Pipeline.INITIATION;
 import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Pipeline.TERMINATION;
 import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Result.*;
@@ -75,11 +75,6 @@
 public class L2TunnelHandler {
 
     private static final Logger log = LoggerFactory.getLogger(L2TunnelHandler.class);
-    /**
-     * Error message for invalid paths.
-     */
-    private static final String WRONG_TOPOLOGY = "Path in leaf-spine topology" +
-            " should always be two hops: ";
 
     private final SegmentRoutingManager srManager;
     /**
@@ -90,16 +85,17 @@
      * To store the next objectives related to the termination.
      */
     private final ConsistentMap<String, NextObjective> l2TerminationNextObjStore;
+
     /**
-     * TODO a proper store is necessary to handle the policies, collisions and recovery.
-     * We should have a proper store for the policies and the tunnels. For several reasons:
-     * 1) We should avoid the overlapping of different policies;
-     * 2) We should avoid the overlapping of different tunnels;
-     * 3) We should have a proper mechanism for the protection;
-     * The most important one is 3). At least for 3.0 EA0 was not possible
-     * to remove the bucket, so we need a mapping between policies and tunnel
-     * in order to proper update the fwd objective for the recovery of a fault.
+     * To store policies.
      */
+    private final ConsistentMap<String, DefaultL2TunnelPolicy> l2PolicyStore;
+
+    /**
+     * To store tunnels.
+     */
+    private final ConsistentMap<String, DefaultL2Tunnel> l2TunnelStore;
+
     private final KryoNamespace.Builder l2TunnelKryo;
 
     /**
@@ -111,19 +107,105 @@
     public L2TunnelHandler(SegmentRoutingManager segmentRoutingManager) {
         srManager = segmentRoutingManager;
         l2TunnelKryo = new KryoNamespace.Builder()
-                .register(KryoNamespaces.API);
+                .register(KryoNamespaces.API)
+                .register(DefaultL2Tunnel.class,
+                          DefaultL2TunnelPolicy.class,
+                          L2Mode.class,
+                          MplsLabel.class,
+                          VlanId.class,
+                          ConnectPoint.class);
 
-        l2InitiationNextObjStore = srManager.storageService
-                .<String, NextObjective>consistentMapBuilder()
-                .withName("onos-l2initiation-nextobj-store")
-                .withSerializer(Serializer.using(l2TunnelKryo.build()))
-                .build();
+        l2InitiationNextObjStore = srManager.
+                storageService.
+                <String, NextObjective>consistentMapBuilder().
+                withName("onos-l2initiation-nextobj-store").
+                withSerializer(Serializer.using(l2TunnelKryo.build())).
+                build();
 
-        l2TerminationNextObjStore = srManager.storageService
-                .<String, NextObjective>consistentMapBuilder()
+        l2TerminationNextObjStore = srManager.storageService.
+                <String, NextObjective>consistentMapBuilder()
                 .withName("onos-l2termination-nextobj-store")
                 .withSerializer(Serializer.using(l2TunnelKryo.build()))
                 .build();
+
+        l2PolicyStore = srManager.storageService
+                .<String, DefaultL2TunnelPolicy>consistentMapBuilder()
+                .withName("onos-l2-policy-store")
+                .withSerializer(Serializer.using(l2TunnelKryo.build()))
+                .build();
+
+        l2TunnelStore = srManager.storageService
+                .<String, DefaultL2Tunnel>consistentMapBuilder()
+                .withName("onos-l2-tunnel-store")
+                .withSerializer(Serializer.using(l2TunnelKryo.build()))
+                .build();
+    }
+
+    /**
+     * Returns all L2 Policies.
+     *
+     * @return List of policies
+     */
+    public List<DefaultL2TunnelPolicy> getL2Policies() {
+
+        return l2PolicyStore
+                .values()
+                .stream()
+                .map(Versioned::value)
+                .map(DefaultL2TunnelPolicy::new)
+                .collect(Collectors.toList());
+
+    }
+
+    /**
+     * Returns all L2 Tunnels.
+     *
+     * @return List of tunnels.
+     */
+    public List<DefaultL2Tunnel> getL2Tunnels() {
+
+        return l2TunnelStore
+                .values()
+                .stream()
+                .map(Versioned::value)
+                .map(DefaultL2Tunnel::new)
+                .collect(Collectors.toList());
+
+    }
+
+    /**
+     * Processes a link removal. Finds affected pseudowires and rewires them.
+     * TODO: Make it also take into account failures of links that are used for pw
+     * traffic in the spine.
+     * @param link The link that failed
+     */
+    public void processLinkDown(Link link) {
+
+        List<DefaultL2Tunnel> tunnels = getL2Tunnels();
+        List<DefaultL2TunnelPolicy> policies = getL2Policies();
+
+        // determine affected pseudowires and update them at once
+        Set<DefaultL2TunnelDescription> pwToUpdate = tunnels
+                .stream()
+                .filter(tun -> tun.pathUsed().contains(link))
+                .map(l2Tunnel -> {
+                        DefaultL2TunnelPolicy policy = null;
+                        for (DefaultL2TunnelPolicy l2Policy : policies) {
+                            if (l2Policy.tunnelId() == l2Tunnel.tunnelId()) {
+                                policy = l2Policy;
+                                break;
+                            }
+                        }
+
+                        return new DefaultL2TunnelDescription(l2Tunnel, policy);
+                })
+                .collect(Collectors.toSet());
+
+
+        log.info("Pseudowires affected by link failure : {}, rerouting them...", pwToUpdate);
+
+        // update all pseudowires
+        pwToUpdate.forEach(tun -> updatePw(tun, tun));
     }
 
     /**
@@ -132,93 +214,209 @@
      * @param event network config add event
      */
     public void processPwaasConfigAdded(NetworkConfigEvent event) {
-        log.info("Processing Pwaas CONFIG_ADDED");
+
+        log.info("Network event : Pseudowire configuration added!");
         PwaasConfig config = (PwaasConfig) event.config().get();
-        Set<DefaultL2TunnelDescription> pwToAdd = config.getPwIds()
+
+        // gather pseudowires
+        Set<DefaultL2TunnelDescription> pwToAdd = config
+                .getPwIds()
                 .stream()
                 .map(config::getPwDescription)
                 .collect(Collectors.toSet());
-        // We deploy all the pseudo wire deployed
+
+        // deploy pseudowires
         deploy(pwToAdd);
     }
 
     /**
+     * Returns the new vlan id for an ingress point of a
+     * pseudowire. For double tagged, it is the outer,
+     * For single tagged it is the single tag, and for
+     * inner it is None.
+     *
+     * @param ingressOuter vlanid of ingress outer
+     * @param ingressInner vlanid of ingress inner
+     * @param egressOuter  vlanid of egress outer
+     * @param egressInner  vlanid of egress inner
+     * @return returns the vlan id which will be installed at vlan table 1.
+     */
+    public VlanId determineEgressVlan(VlanId ingressOuter, VlanId ingressInner,
+                                      VlanId egressOuter, VlanId egressInner) {
+
+        // validity of vlan combinations was checked at verifyPseudowire
+        if (!(ingressOuter.equals(VlanId.NONE))) {
+            return egressOuter;
+        } else if (!(ingressInner.equals(VlanId.NONE))) {
+            return egressInner;
+        } else {
+            return VlanId.vlanId("None");
+        }
+    }
+
+    /**
+     * Adds a single pseudowire. This method can be called from cli commands
+     * without configration updates, thus it does not check for mastership
+     * of the ingress pseudowire device.
+     *
+     * @param pw The pseudowire
+     * @return
+     */
+    private Result deployPseudowire(DefaultL2TunnelDescription pw) {
+
+        Result result;
+        long l2TunnelId;
+
+        l2TunnelId = pw.l2Tunnel().tunnelId();
+
+        // The tunnel id cannot be 0.
+        if (l2TunnelId == 0) {
+            log.warn("Tunnel id id must be > 0");
+            return Result.ADDITION_ERROR;
+        }
+
+        // get path here, need to use the same for fwd and rev direction
+        List<Link> path = getPath(pw.l2TunnelPolicy().cP1(),
+                                  pw.l2TunnelPolicy().cP2());
+        if (path == null) {
+            log.info("Deploying process : No path between the connection points for pseudowire {}", l2TunnelId);
+            return WRONG_PARAMETERS;
+        }
+
+        pw.l2Tunnel().setPath(path);
+
+        // next hops for next objectives
+        Link fwdNextHop = path.get(0);
+        Link revNextHop = reverseLink(path.get(1));
+
+        log.info("Deploying process : Establishing forward direction for pseudowire {}", l2TunnelId);
+
+        // We establish the tunnel.
+        // result.nextId will be used in fwd
+        result = deployPseudoWireInit(pw.l2Tunnel(),
+                                      pw.l2TunnelPolicy().cP1(),
+                                      pw.l2TunnelPolicy().cP2(),
+                                      FWD,
+                                      fwdNextHop);
+        if (result != SUCCESS) {
+            log.info("Deploying process : Error in deploying pseudowire initiation for CP1");
+            return Result.ADDITION_ERROR;
+        }
+
+        VlanId egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP1OuterTag(),
+                                                pw.l2TunnelPolicy().cP1InnerTag(),
+                                                pw.l2TunnelPolicy().cP2OuterTag(),
+                                                pw.l2TunnelPolicy().cP2InnerTag());
+
+        // We create the policy.
+        result = deployPolicy(l2TunnelId,
+                              pw.l2TunnelPolicy().cP1(),
+                              pw.l2TunnelPolicy().cP1InnerTag(),
+                              pw.l2TunnelPolicy().cP1OuterTag(),
+                              egressVlan,
+                              result.nextId);
+        if (result != SUCCESS) {
+            log.info("Deploying process : Error in deploying pseudowire policy for CP1");
+            return Result.ADDITION_ERROR;
+        }
+
+        // We terminate the tunnel
+        result = deployPseudoWireTerm(pw.l2Tunnel(),
+                                       pw.l2TunnelPolicy().cP2(),
+                                       pw.l2TunnelPolicy().cP2OuterTag(),
+                                       FWD);
+
+        if (result != SUCCESS) {
+            log.info("Deploying process : Error in deploying pseudowire termination for CP1");
+            return Result.ADDITION_ERROR;
+
+        }
+
+        log.info("Deploying process : Establishing reverse direction for pseudowire {}", l2TunnelId);
+
+        // We establish the reverse tunnel.
+        result = deployPseudoWireInit(pw.l2Tunnel(),
+                                       pw.l2TunnelPolicy().cP2(),
+                                       pw.l2TunnelPolicy().cP1(),
+                                       REV,
+                                       revNextHop);
+        if (result != SUCCESS) {
+            log.info("Deploying process : Error in deploying pseudowire initiation for CP2");
+            return Result.ADDITION_ERROR;
+        }
+
+        egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP2OuterTag(),
+                                          pw.l2TunnelPolicy().cP2InnerTag(),
+                                          pw.l2TunnelPolicy().cP1OuterTag(),
+                                          pw.l2TunnelPolicy().cP1InnerTag());
+        result = deployPolicy(l2TunnelId,
+                               pw.l2TunnelPolicy().cP2(),
+                               pw.l2TunnelPolicy().cP2InnerTag(),
+                               pw.l2TunnelPolicy().cP2OuterTag(),
+                               egressVlan,
+                               result.nextId);
+        if (result != SUCCESS) {
+            log.info("Deploying process : Error in deploying policy for CP2");
+            return Result.ADDITION_ERROR;
+        }
+
+        result = deployPseudoWireTerm(pw.l2Tunnel(),
+                                       pw.l2TunnelPolicy().cP1(),
+                                       pw.l2TunnelPolicy().cP1OuterTag(),
+                                       REV);
+
+        if (result != SUCCESS) {
+            log.info("Deploying process : Error in deploying pseudowire termination for CP2");
+            return Result.ADDITION_ERROR;
+        }
+
+        log.info("Deploying process : Updating relevant information for pseudowire {}", l2TunnelId);
+
+        // Populate stores
+        l2TunnelStore.put(Long.toString(l2TunnelId), pw.l2Tunnel());
+        l2PolicyStore.put(Long.toString(l2TunnelId), pw.l2TunnelPolicy());
+
+        return Result.SUCCESS;
+    }
+
+    /**
      * To deploy a number of pseudo wires.
+     * <p>
+     * Called ONLY when configuration changes, thus the check
+     * for the mastership of the device.
+     * <p>
+     * Only the master of CP1 will deploy this pseudowire.
      *
      * @param pwToAdd the set of pseudo wires to add
      */
     private void deploy(Set<DefaultL2TunnelDescription> pwToAdd) {
+
         Result result;
-        long l2TunnelId;
+
         for (DefaultL2TunnelDescription currentL2Tunnel : pwToAdd) {
-            l2TunnelId = currentL2Tunnel.l2Tunnel().tunnelId();
-            // The tunnel id cannot be 0.
-            if (l2TunnelId == 0) {
-                log.warn("Tunnel id id must be > 0");
+
+            // only the master of CP1 will program this pseudowire
+            if (!srManager.isMasterOf(currentL2Tunnel.l2TunnelPolicy().cP1())) {
                 continue;
             }
-            // We do a sanity check of the pseudo wire.
-            result = verifyPseudoWire(currentL2Tunnel);
-            if (result != SUCCESS) {
-                continue;
+
+            log.info("Deploying pseudowire {}", currentL2Tunnel.l2Tunnel().tunnelId());
+
+            result = deployPseudowire(currentL2Tunnel);
+            switch (result) {
+                case WRONG_PARAMETERS:
+                    log.warn("Could not deploy pseudowire {}, wrong parameters!",
+                             currentL2Tunnel.l2Tunnel().tunnelId());
+                    break;
+                case ADDITION_ERROR:
+                    log.warn("Could not deploy pseudowire {}, error in populating rules!",
+                             currentL2Tunnel.l2Tunnel().tunnelId());
+                    break;
+                default:
+                    log.info("Pseudowire with {} succesfully deployed!",
+                             currentL2Tunnel.l2Tunnel().tunnelId());
+                    break;
             }
-            // We establish the tunnel.
-            result = deployPseudoWireInit(
-                    currentL2Tunnel.l2Tunnel(),
-                    currentL2Tunnel.l2TunnelPolicy().cP1(),
-                    currentL2Tunnel.l2TunnelPolicy().cP2(),
-                    FWD
-            );
-            if (result != SUCCESS) {
-                continue;
-            }
-            // We create the policy.
-            result = deployPolicy(
-                    l2TunnelId,
-                    currentL2Tunnel.l2TunnelPolicy().cP1(),
-                    currentL2Tunnel.l2TunnelPolicy().cP1InnerTag(),
-                    currentL2Tunnel.l2TunnelPolicy().cP1OuterTag(),
-                    result.nextId
-            );
-            if (result != SUCCESS) {
-                continue;
-            }
-            // We terminate the tunnel
-            result = deployPseudoWireTerm(
-                    currentL2Tunnel.l2Tunnel(),
-                    currentL2Tunnel.l2TunnelPolicy().cP2(),
-                    currentL2Tunnel.l2TunnelPolicy().cP2OuterTag(),
-                    FWD
-            );
-            if (result != SUCCESS) {
-                continue;
-            }
-            // We establish the reverse tunnel.
-            result = deployPseudoWireInit(
-                    currentL2Tunnel.l2Tunnel(),
-                    currentL2Tunnel.l2TunnelPolicy().cP2(),
-                    currentL2Tunnel.l2TunnelPolicy().cP1(),
-                    REV
-            );
-            if (result != SUCCESS) {
-                continue;
-            }
-            result = deployPolicy(
-                    l2TunnelId,
-                    currentL2Tunnel.l2TunnelPolicy().cP2(),
-                    currentL2Tunnel.l2TunnelPolicy().cP2InnerTag(),
-                    currentL2Tunnel.l2TunnelPolicy().cP2OuterTag(),
-                    result.nextId
-            );
-            if (result != SUCCESS) {
-                continue;
-            }
-            deployPseudoWireTerm(
-                    currentL2Tunnel.l2Tunnel(),
-                    currentL2Tunnel.l2TunnelPolicy().cP1(),
-                    currentL2Tunnel.l2TunnelPolicy().cP1OuterTag(),
-                    REV
-            );
         }
     }
 
@@ -228,46 +426,75 @@
      * @param event network config updated event
      */
     public void processPwaasConfigUpdated(NetworkConfigEvent event) {
-        log.info("Processing Pwaas CONFIG_UPDATED");
+
+        log.info("Pseudowire configuration updated.");
+
         // We retrieve the old pseudo wires.
         PwaasConfig prevConfig = (PwaasConfig) event.prevConfig().get();
         Set<Long> prevPws = prevConfig.getPwIds();
+
         // We retrieve the new pseudo wires.
         PwaasConfig config = (PwaasConfig) event.config().get();
         Set<Long> newPws = config.getPwIds();
+
         // We compute the pseudo wires to update.
         Set<Long> updPws = newPws.stream()
-                .filter(tunnelId -> prevPws.contains(tunnelId) &&
-                !config.getPwDescription(tunnelId).equals(prevConfig.getPwDescription(tunnelId)))
+                .filter(tunnelId -> prevPws.contains(tunnelId)
+                        && !config.getPwDescription(tunnelId).equals(prevConfig.getPwDescription(tunnelId)))
                 .collect(Collectors.toSet());
+
         // The pseudo wires to remove.
-        Set<DefaultL2TunnelDescription> pwToRemove = prevPws.stream()
-                .filter(tunnelId -> !newPws.contains(tunnelId))
+        Set<Long> rmvPWs = prevPws.stream()
+                .filter(tunnelId -> !newPws.contains(tunnelId)).collect(Collectors.toSet());
+
+        Set<DefaultL2TunnelDescription> pwToRemove = rmvPWs.stream()
                 .map(prevConfig::getPwDescription)
                 .collect(Collectors.toSet());
         tearDown(pwToRemove);
+
         // The pseudo wires to add.
-        Set<DefaultL2TunnelDescription> pwToAdd = newPws.stream()
+        Set<Long> addedPWs = newPws.stream()
                 .filter(tunnelId -> !prevPws.contains(tunnelId))
+                .collect(Collectors.toSet());
+        Set<DefaultL2TunnelDescription> pwToAdd = addedPWs.stream()
                 .map(config::getPwDescription)
                 .collect(Collectors.toSet());
         deploy(pwToAdd);
+
+
         // The pseudo wires to update.
-        updPws.forEach(tunnelId -> updatePw(
-                prevConfig.getPwDescription(tunnelId),
-                config.getPwDescription(tunnelId))
-        );
+        updPws.forEach(tunnelId -> updatePw(prevConfig.getPwDescription(tunnelId),
+                                            config.getPwDescription(tunnelId)));
+
+        log.info("Pseudowires removed : {}, Pseudowires updated : {}, Pseudowires added : {}", rmvPWs,
+                 updPws, addedPWs);
     }
 
     /**
      * Helper function to update a pw.
+     * <p>
+     * Called upon configuration changes that update existing pseudowires and
+     * when links fail. Checking of mastership for CP1 is mandatory because it is
+     * called in multiple instances for both cases.
+     * <p>
+     * Meant to call asynchronously for various events, thus this call can not block and need
+     * to perform asynchronous operations.
+     * <p>
+     * For this reason error checking is omitted.
      *
      * @param oldPw the pseudo wire to remove
-     * @param newPw the pseudo wirte to add
+     * @param newPw the pseudo wire to add
      */
-    private void updatePw(DefaultL2TunnelDescription oldPw,
-                          DefaultL2TunnelDescription newPw) {
+    public void updatePw(DefaultL2TunnelDescription oldPw, DefaultL2TunnelDescription newPw) {
         long tunnelId = oldPw.l2Tunnel().tunnelId();
+
+        // only the master of CP1 will update this pseudowire
+        if (!srManager.isMasterOf(oldPw.l2TunnelPolicy().cP1())) {
+            return;
+        }
+
+        log.info("Updating pseudowire {}", oldPw.l2Tunnel().tunnelId());
+
         // The async tasks to orchestrate the next and
         // forwarding update.
         CompletableFuture<ObjectiveError> fwdInitNextFuture = new CompletableFuture<>();
@@ -277,136 +504,165 @@
         CompletableFuture<ObjectiveError> fwdPwFuture = new CompletableFuture<>();
         CompletableFuture<ObjectiveError> revPwFuture = new CompletableFuture<>();
 
-
-        Result result = verifyPseudoWire(newPw);
-        if (result != SUCCESS) {
-            return;
-        }
         // First we remove both policy.
         log.debug("Start deleting fwd policy for {}", tunnelId);
-        deletePolicy(
-                tunnelId,
-                oldPw.l2TunnelPolicy().cP1(),
-                oldPw.l2TunnelPolicy().cP1InnerTag(),
-                oldPw.l2TunnelPolicy().cP1OuterTag(),
-                fwdInitNextFuture,
-                FWD
-        );
-        log.debug("Start deleting rev policy for {}", tunnelId);
-        deletePolicy(
-                tunnelId,
-                oldPw.l2TunnelPolicy().cP2(),
-                oldPw.l2TunnelPolicy().cP2InnerTag(),
-                oldPw.l2TunnelPolicy().cP2OuterTag(),
-                revInitNextFuture,
-                REV
-        );
+
+        // first delete all information from our stores
+        // we can not do it asynchronously
+        l2PolicyStore.remove(Long.toString(tunnelId));
+        l2TunnelStore.remove(Long.toString(tunnelId));
+
+        VlanId egressVlan = determineEgressVlan(oldPw.l2TunnelPolicy().cP1OuterTag(),
+                                                 oldPw.l2TunnelPolicy().cP1InnerTag(),
+                                                 oldPw.l2TunnelPolicy().cP2OuterTag(),
+                                                 oldPw.l2TunnelPolicy().cP2InnerTag());
+        deletePolicy(tunnelId,
+                      oldPw.l2TunnelPolicy().cP1(),
+                      oldPw.l2TunnelPolicy().cP1InnerTag(),
+                      oldPw.l2TunnelPolicy().cP1OuterTag(),
+                      egressVlan,
+                      fwdInitNextFuture,
+                      FWD);
+
+        log.debug("Update process : Start deleting rev policy for {}", tunnelId);
+
+        egressVlan = determineEgressVlan(oldPw.l2TunnelPolicy().cP2OuterTag(),
+                                          oldPw.l2TunnelPolicy().cP2InnerTag(),
+                                          oldPw.l2TunnelPolicy().cP1OuterTag(),
+                                          oldPw.l2TunnelPolicy().cP1InnerTag());
+        deletePolicy(tunnelId,
+                      oldPw.l2TunnelPolicy().cP2(),
+                      oldPw.l2TunnelPolicy().cP2InnerTag(),
+                      oldPw.l2TunnelPolicy().cP2OuterTag(),
+                      egressVlan, revInitNextFuture,
+                      REV);
+
         // Finally we remove both the tunnels.
         fwdInitNextFuture.thenAcceptAsync(status -> {
             if (status == null) {
-                log.debug("Fwd policy removed. Now remove fwd {} for {}", INITIATION, tunnelId);
-                tearDownPseudoWireInit(
-                        tunnelId,
-                        oldPw.l2TunnelPolicy().cP1(),
-                        fwdTermNextFuture,
-                        FWD
-                );
+                log.debug("Update process : Fwd policy removed. " +
+                                  "Now remove fwd {} for {}", INITIATION, tunnelId);
+                tearDownPseudoWireInit(tunnelId,
+                                        oldPw.l2TunnelPolicy().cP1(),
+                                        fwdTermNextFuture,
+                                        FWD);
             }
         });
         revInitNextFuture.thenAcceptAsync(status -> {
-           if (status == null) {
-               log.debug("Rev policy removed. Now remove rev {} for {}", INITIATION, tunnelId);
-               tearDownPseudoWireInit(
-                       tunnelId,
-                       oldPw.l2TunnelPolicy().cP2(),
-                       revTermNextFuture,
-                       REV
-               );
-
-           }
+            if (status == null) {
+                log.debug("Update process : Rev policy removed. " +
+                                  "Now remove rev {} for {}", INITIATION, tunnelId);
+                tearDownPseudoWireInit(tunnelId,
+                                        oldPw.l2TunnelPolicy().cP2(),
+                                        revTermNextFuture,
+                                        REV);
+            }
         });
         fwdTermNextFuture.thenAcceptAsync(status -> {
             if (status == null) {
-                log.debug("Fwd {} removed. Now remove fwd {} for {}", INITIATION, TERMINATION, tunnelId);
-                tearDownPseudoWireTerm(
-                        oldPw.l2Tunnel(),
-                        oldPw.l2TunnelPolicy().cP2(),
-                        fwdPwFuture,
-                        FWD
-                );
+                log.debug("Update process : Fwd {} removed. " +
+                                  "Now remove fwd {} for {}", INITIATION, TERMINATION, tunnelId);
+                tearDownPseudoWireTerm(oldPw.l2Tunnel(),
+                                        oldPw.l2TunnelPolicy().cP2(),
+                                        fwdPwFuture,
+                                        FWD);
             }
         });
         revTermNextFuture.thenAcceptAsync(status -> {
             if (status == null) {
-                log.debug("Rev {} removed. Now remove rev {} for {}", INITIATION, TERMINATION, tunnelId);
-                tearDownPseudoWireTerm(
-                        oldPw.l2Tunnel(),
-                        oldPw.l2TunnelPolicy().cP1(),
-                        revPwFuture,
-                        REV
-                );
+                log.debug("Update process : Rev {} removed. " +
+                                  "Now remove rev {} for {}", INITIATION, TERMINATION, tunnelId);
+                tearDownPseudoWireTerm(oldPw.l2Tunnel(),
+                                        oldPw.l2TunnelPolicy().cP1(),
+                                        revPwFuture,
+                                        REV);
             }
         });
-        // At the end we install the new pw.
+
+        // get path here, need to use the same for fwd and rev direction
+        List<Link> path = getPath(newPw.l2TunnelPolicy().cP1(),
+                                   newPw.l2TunnelPolicy().cP2());
+        if (path == null) {
+            log.info("Deploying process : " +
+                             "No path between the connection points for pseudowire {}", newPw.l2Tunnel().tunnelId());
+            return;
+        }
+
+        newPw.l2Tunnel().setPath(path);
+        // next hops for next objectives
+        Link fwdNextHop = path.get(0);
+        Link revNextHop = reverseLink(path.get(1));
+
+        // At the end we install the updated PW.
         fwdPwFuture.thenAcceptAsync(status -> {
             if (status == null) {
-                log.debug("Deploying new fwd pw for {}", tunnelId);
-                Result lamdaResult = deployPseudoWireInit(
-                        newPw.l2Tunnel(),
-                        newPw.l2TunnelPolicy().cP1(),
-                        newPw.l2TunnelPolicy().cP2(),
-                        FWD
-                );
+
+                // Upgrade stores and book keeping information, need to move this here
+                // cause this call is asynchronous.
+                l2PolicyStore.put(Long.toString(tunnelId), newPw.l2TunnelPolicy());
+                l2TunnelStore.put(Long.toString(tunnelId), newPw.l2Tunnel());
+
+                log.debug("Update process : Deploying new fwd pw for {}", tunnelId);
+                Result lamdaResult = deployPseudoWireInit(newPw.l2Tunnel(),
+                                                           newPw.l2TunnelPolicy().cP1(),
+                                                           newPw.l2TunnelPolicy().cP2(),
+                                                           FWD,
+                                                           fwdNextHop);
                 if (lamdaResult != SUCCESS) {
                     return;
                 }
-                lamdaResult = deployPolicy(
-                        tunnelId,
-                        newPw.l2TunnelPolicy().cP1(),
-                        newPw.l2TunnelPolicy().cP1InnerTag(),
-                        newPw.l2TunnelPolicy().cP1OuterTag(),
-                        lamdaResult.nextId
-                );
+
+                VlanId egressVlanId = determineEgressVlan(newPw.l2TunnelPolicy().cP1OuterTag(),
+                                                           newPw.l2TunnelPolicy().cP1InnerTag(),
+                                                           newPw.l2TunnelPolicy().cP2OuterTag(),
+                                                           newPw.l2TunnelPolicy().cP2InnerTag());
+
+                lamdaResult = deployPolicy(tunnelId,
+                                            newPw.l2TunnelPolicy().cP1(),
+                                            newPw.l2TunnelPolicy().cP1InnerTag(),
+                                            newPw.l2TunnelPolicy().cP1OuterTag(),
+                                            egressVlanId,
+                                            lamdaResult.nextId);
                 if (lamdaResult != SUCCESS) {
                     return;
                 }
-                deployPseudoWireTerm(
-                        newPw.l2Tunnel(),
-                        newPw.l2TunnelPolicy().cP2(),
-                        newPw.l2TunnelPolicy().cP2OuterTag(),
-                        FWD
-                );
+                deployPseudoWireTerm(newPw.l2Tunnel(),
+                                      newPw.l2TunnelPolicy().cP2(),
+                                      newPw.l2TunnelPolicy().cP2OuterTag(),
+                                      FWD);
 
             }
         });
+
         revPwFuture.thenAcceptAsync(status -> {
             if (status == null) {
-                log.debug("Deploying new rev pw for {}", tunnelId);
-                Result lamdaResult = deployPseudoWireInit(
-                        newPw.l2Tunnel(),
-                        newPw.l2TunnelPolicy().cP2(),
-                        newPw.l2TunnelPolicy().cP1(),
-                        REV
-                );
+                log.debug("Update process : Deploying new rev pw for {}", tunnelId);
+                Result lamdaResult = deployPseudoWireInit(newPw.l2Tunnel(),
+                                                           newPw.l2TunnelPolicy().cP2(),
+                                                           newPw.l2TunnelPolicy().cP1(),
+                                                           REV,
+                                                           revNextHop);
                 if (lamdaResult != SUCCESS) {
                     return;
                 }
-                lamdaResult = deployPolicy(
-                        tunnelId,
-                        newPw.l2TunnelPolicy().cP2(),
-                        newPw.l2TunnelPolicy().cP2InnerTag(),
-                        newPw.l2TunnelPolicy().cP2OuterTag(),
-                        lamdaResult.nextId
-                );
+
+                VlanId egressVlanId = determineEgressVlan(newPw.l2TunnelPolicy().cP2OuterTag(),
+                                                           newPw.l2TunnelPolicy().cP2InnerTag(),
+                                                           newPw.l2TunnelPolicy().cP1OuterTag(),
+                                                           newPw.l2TunnelPolicy().cP1InnerTag());
+                lamdaResult = deployPolicy(tunnelId,
+                                            newPw.l2TunnelPolicy().cP2(),
+                                            newPw.l2TunnelPolicy().cP2InnerTag(),
+                                            newPw.l2TunnelPolicy().cP2OuterTag(),
+                                            egressVlanId,
+                                            lamdaResult.nextId);
                 if (lamdaResult != SUCCESS) {
                     return;
                 }
-                deployPseudoWireTerm(
-                        newPw.l2Tunnel(),
-                        newPw.l2TunnelPolicy().cP1(),
-                        newPw.l2TunnelPolicy().cP1OuterTag(),
-                        REV
-                );
+                deployPseudoWireTerm(newPw.l2Tunnel(),
+                                      newPw.l2TunnelPolicy().cP1(),
+                                      newPw.l2TunnelPolicy().cP1OuterTag(),
+                                      REV);
             }
         });
     }
@@ -417,110 +673,165 @@
      * @param event network config removed event
      */
     public void processPwaasConfigRemoved(NetworkConfigEvent event) {
-        log.info("Processing Pwaas CONFIG_REMOVED");
+
+        log.info("Network event : Pseudowire configuration removed!");
         PwaasConfig config = (PwaasConfig) event.prevConfig().get();
-        Set<DefaultL2TunnelDescription> pwToRemove = config.getPwIds()
+
+        Set<DefaultL2TunnelDescription> pwToRemove = config
+                .getPwIds()
                 .stream()
                 .map(config::getPwDescription)
                 .collect(Collectors.toSet());
+
         // We teardown all the pseudo wire deployed
         tearDown(pwToRemove);
     }
 
     /**
-     * Helper function to handle the pw removal.
+     * Helper function for removing a single pseudowire.
+     * <p>
+     * No mastership of CP1 is checked, because it can be called from
+     * the CLI for removal of pseudowires.
      *
-     * @param pwToRemove the pseudo wires to remove
+     * @param l2TunnelId the id of the pseudowire to tear down
+     * @return Returns SUCCESS if no error is obeserved or an appropriate
+     * error on a failure
      */
-    private void tearDown(Set<DefaultL2TunnelDescription> pwToRemove) {
-        Result result;
-        long l2TunnelId;
-        // We remove all the pw in the configuration file.
-        for (DefaultL2TunnelDescription currentL2Tunnel : pwToRemove) {
-            l2TunnelId = currentL2Tunnel.l2Tunnel().tunnelId();
-            if (l2TunnelId == 0) {
-                log.warn("Tunnel id cannot be 0");
-                continue;
-            }
-            result = verifyPseudoWire(currentL2Tunnel);
-            if (result != SUCCESS) {
-                continue;
-            }
-            // First all we have to delete the policy.
-            deletePolicy(
-                    l2TunnelId,
-                    currentL2Tunnel.l2TunnelPolicy().cP1(),
-                    currentL2Tunnel.l2TunnelPolicy().cP1InnerTag(),
-                    currentL2Tunnel.l2TunnelPolicy().cP1OuterTag(),
-                    null,
-                    FWD
-            );
-            // Finally we will tear down the pseudo wire.
-            tearDownPseudoWireInit(
-                    l2TunnelId,
-                    currentL2Tunnel.l2TunnelPolicy().cP1(),
-                    null,
-                    FWD
-            );
-            tearDownPseudoWireTerm(
-                    currentL2Tunnel.l2Tunnel(),
-                    currentL2Tunnel.l2TunnelPolicy().cP2(),
-                    null,
-                    FWD
-            );
-            // We do the same operations on the reverse side.
-            deletePolicy(
-                    l2TunnelId,
-                    currentL2Tunnel.l2TunnelPolicy().cP2(),
-                    currentL2Tunnel.l2TunnelPolicy().cP2InnerTag(),
-                    currentL2Tunnel.l2TunnelPolicy().cP2OuterTag(),
-                    null,
-                    REV
-            );
-            tearDownPseudoWireInit(
-                    l2TunnelId,
-                    currentL2Tunnel.l2TunnelPolicy().cP2(),
-                    null,
-                    REV
-            );
-            tearDownPseudoWireTerm(
-                    currentL2Tunnel.l2Tunnel(),
-                    currentL2Tunnel.l2TunnelPolicy().cP1(),
-                    null,
-                    REV
-            );
+    public Result tearDownPseudowire(long l2TunnelId) {
+
+        CompletableFuture<ObjectiveError> fwdInitNextFuture = new CompletableFuture<>();
+        CompletableFuture<ObjectiveError> fwdTermNextFuture = new CompletableFuture<>();
+
+        CompletableFuture<ObjectiveError> revInitNextFuture = new CompletableFuture<>();
+        CompletableFuture<ObjectiveError> revTermNextFuture = new CompletableFuture<>();
+
+        if (l2TunnelId == 0) {
+            log.warn("Removal process : Tunnel id cannot be 0");
+            return Result.WRONG_PARAMETERS;
         }
 
+        // check existence of tunnels/policy in the store, if one is missing abort!
+        Versioned<DefaultL2Tunnel> l2TunnelVersioned = l2TunnelStore.get(Long.toString(l2TunnelId));
+        Versioned<DefaultL2TunnelPolicy> l2TunnelPolicyVersioned = l2PolicyStore.get(Long.toString(l2TunnelId));
+        if ((l2TunnelVersioned == null) || (l2TunnelPolicyVersioned == null)) {
+            log.warn("Removal process : Policy and/or tunnel missing for tunnel id {}", l2TunnelId);
+            return Result.REMOVAL_ERROR;
+        }
+
+        DefaultL2TunnelDescription pwToRemove = new DefaultL2TunnelDescription(l2TunnelVersioned.value(),
+                                                                               l2TunnelPolicyVersioned.value());
+
+        // remove the tunnels and the policies from the store
+        l2PolicyStore.remove(Long.toString(l2TunnelId));
+        l2TunnelStore.remove(Long.toString(l2TunnelId));
+
+        log.info("Removal process : Tearing down forward direction of pseudowire {}", l2TunnelId);
+
+        VlanId egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP1OuterTag(),
+                                                 pwToRemove.l2TunnelPolicy().cP1InnerTag(),
+                                                 pwToRemove.l2TunnelPolicy().cP2OuterTag(),
+                                                 pwToRemove.l2TunnelPolicy().cP2InnerTag());
+        deletePolicy(l2TunnelId,
+                      pwToRemove.l2TunnelPolicy().cP1(),
+                      pwToRemove.l2TunnelPolicy().cP1InnerTag(),
+                      pwToRemove.l2TunnelPolicy().cP1OuterTag(),
+                      egressVlan,
+                      fwdInitNextFuture,
+                      FWD);
+
+        fwdInitNextFuture.thenAcceptAsync(status -> {
+            if (status == null) {
+                // Finally we will tear down the pseudo wire.
+                tearDownPseudoWireInit(l2TunnelId,
+                                        pwToRemove.l2TunnelPolicy().cP1(),
+                                        fwdTermNextFuture,
+                                        FWD);
+            }
+        });
+
+        fwdTermNextFuture.thenAcceptAsync(status -> {
+            if (status == null) {
+                tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
+                                        pwToRemove.l2TunnelPolicy().cP2(),
+                                        null,
+                                        FWD);
+            }
+        });
+
+        log.info("Removal process : Tearing down reverse direction of pseudowire {}", l2TunnelId);
+
+        egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP2OuterTag(),
+                                          pwToRemove.l2TunnelPolicy().cP2InnerTag(),
+                                          pwToRemove.l2TunnelPolicy().cP1OuterTag(),
+                                          pwToRemove.l2TunnelPolicy().cP1InnerTag());
+
+        // We do the same operations on the reverse side.
+        deletePolicy(l2TunnelId,
+                      pwToRemove.l2TunnelPolicy().cP2(),
+                      pwToRemove.l2TunnelPolicy().cP2InnerTag(),
+                      pwToRemove.l2TunnelPolicy().cP2OuterTag(),
+                      egressVlan,
+                      revInitNextFuture,
+                      REV);
+
+        revInitNextFuture.thenAcceptAsync(status -> {
+            if (status == null) {
+                tearDownPseudoWireInit(l2TunnelId,
+                                        pwToRemove.l2TunnelPolicy().cP2(),
+                                        revTermNextFuture,
+                                        REV);
+            }
+        });
+
+        revTermNextFuture.thenAcceptAsync(status -> {
+            if (status == null) {
+                tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
+                                        pwToRemove.l2TunnelPolicy().cP1(),
+                                        null,
+                                        REV);
+            }
+        });
+
+        return Result.SUCCESS;
     }
 
     /**
-     * Helper method to verify the integrity of the pseudo wire.
+     * Helper function to handle the pw removal.
+     * <p>
+     * This method checks for the mastership of the device because it is
+     * used only from network configuration updates, thus we only want
+     * one instance only to program each pseudowire.
      *
-     * @param l2TunnelDescription the pseudo wire description
-     * @return the result of the check
+     * @param pwToRemove the pseudo wires to remove
      */
-    private Result verifyPseudoWire(DefaultL2TunnelDescription l2TunnelDescription) {
-        Result result;
-        DefaultL2Tunnel l2Tunnel = l2TunnelDescription.l2Tunnel();
-        DefaultL2TunnelPolicy l2TunnelPolicy = l2TunnelDescription.l2TunnelPolicy();
-        result = verifyTunnel(l2Tunnel);
-        if (result != SUCCESS) {
-            log.warn("Tunnel {}: did not pass the validation", l2Tunnel.tunnelId());
-            return result;
-        }
-        result = verifyPolicy(
-                l2TunnelPolicy.isAllVlan(),
-                l2TunnelPolicy.cP1InnerTag(),
-                l2TunnelPolicy.cP1OuterTag(),
-                l2TunnelPolicy.cP2InnerTag(),
-                l2TunnelPolicy.cP2OuterTag()
-        );
-        if (result != SUCCESS) {
-            log.warn("Policy for tunnel {}: did not pass the validation", l2Tunnel.tunnelId());
-            return result;
-        }
+    public void tearDown(Set<DefaultL2TunnelDescription> pwToRemove) {
 
-        return SUCCESS;
+        Result result;
+
+        // We remove all the pw in the configuration file.
+        for (DefaultL2TunnelDescription currentL2Tunnel : pwToRemove) {
+
+            // only the master of CP1 will program this pseudowire
+            if (!srManager.isMasterOf(currentL2Tunnel.l2TunnelPolicy().cP1())) {
+                continue;
+            }
+
+            log.info("Removing pseudowire {}", currentL2Tunnel.l2Tunnel().tunnelId());
+
+            result = tearDownPseudowire(currentL2Tunnel.l2Tunnel().tunnelId());
+            switch (result) {
+                case WRONG_PARAMETERS:
+                    log.warn("Error in supplied parameters for the pseudowire removal with tunnel id {}!",
+                             currentL2Tunnel.l2Tunnel().tunnelId());
+                    break;
+                case REMOVAL_ERROR:
+                    log.warn("Error in pseudowire removal with tunnel id {}!", currentL2Tunnel.l2Tunnel().tunnelId());
+                    break;
+                default:
+                    log.warn("Pseudowire with tunnel id {} was removed successfully",
+                             currentL2Tunnel.l2Tunnel().tunnelId());
+            }
+        }
     }
 
     /**
@@ -528,54 +839,48 @@
      * create the filtering and forwarding objectives related
      * to the initiation and termination.
      *
-     * @param tunnelId the tunnel id
-     * @param ingress the ingress point
+     * @param tunnelId     the tunnel id
+     * @param ingress      the ingress point
      * @param ingressInner the ingress inner tag
      * @param ingressOuter the ingress outer tag
-     * @param nextId the next objective id
+     * @param nextId       the next objective id
+     * @param egressVlan   Vlan-id to set, depends on ingress vlan
+     *                     combinations. For example, if pw is double tagged
+     *                     then this is the value of the outer vlan, if single
+     *                     tagged then it is the new value of the single tag.
+     *                     Should be None for untagged traffic.
      * @return the result of the operation
      */
-    private Result deployPolicy(long tunnelId,
-                                ConnectPoint ingress,
-                                VlanId ingressInner,
-                                VlanId ingressOuter,
-                                int nextId) {
-        if (!srManager.mastershipService.isLocalMaster(ingress.deviceId())) {
-            log.info("Abort creation of policy for tunnel {}: I am not the master", tunnelId);
-            return SUCCESS;
-        }
+    private Result deployPolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner,
+                                VlanId ingressOuter, VlanId egressVlan, int nextId) {
+
         List<Objective> objectives = Lists.newArrayList();
         // We create the forwarding objective for supporting
         // the l2 tunnel.
-        ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(
-                tunnelId,
-                ingress.port(),
-                nextId
-        );
+        ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
         // We create and add objective context.
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective)
-                        -> log.debug("FwdObj for tunnel {} populated", tunnelId),
-                (objective, error)
-                        -> log.warn("Failed to populate fwdrObj for tunnel {}", tunnelId, error));
+        ObjectiveContext context = new DefaultObjectiveContext((objective) ->
+                                                                log.debug("FwdObj for tunnel {} populated", tunnelId),
+                                                               (objective, error) ->
+                                                                log.warn("Failed to populate fwdrObj " +
+                                                                                 "for tunnel {}", tunnelId, error));
         objectives.add(fwdBuilder.add(context));
+
         // We create the filtering objective to define the
         // permit traffic in the switch
-        FilteringObjective.Builder filtBuilder = createFiltObjective(
-                ingress.port(),
-                ingressInner,
-                ingressOuter
-        );
+        FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
+
         // We add the metadata.
-        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
-                .setTunnelId(tunnelId);
+        TrafficTreatment.Builder treatment = DefaultTrafficTreatment
+                .builder()
+                .setTunnelId(tunnelId)
+                .setVlanId(egressVlan);
         filtBuilder.withMeta(treatment.build());
+
         // We create and add objective context.
-        context = new DefaultObjectiveContext(
-                (objective)
-                        -> log.debug("FilterObj for tunnel {} populated", tunnelId),
-                (objective, error)
-                        -> log.warn("Failed to populate filterObj for tunnel {}", tunnelId, error));
+        context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for tunnel {} populated", tunnelId),
+                                              (objective, error) -> log.warn("Failed to populate filterObj for " +
+                                                                                     "tunnel {}", tunnelId, error));
         objectives.add(filtBuilder.add(context));
 
         for (Objective objective : objectives) {
@@ -591,73 +896,32 @@
     }
 
     /**
-     * Helper method to verify if the policy is whether or not
-     * supported.
-     *
-     * @param isAllVlan all vlan mode
-     * @param ingressInner the ingress inner tag
-     * @param ingressOuter the ingress outer tag
-     * @param egressInner the egress inner tag
-     * @param egressOuter the egress outer tag
-     * @return the result of verification
-     */
-    private Result verifyPolicy(boolean isAllVlan,
-                                VlanId ingressInner,
-                                VlanId ingressOuter,
-                                VlanId egressInner,
-                                VlanId egressOuter) {
-        // AllVlan mode is not supported yet.
-        if (isAllVlan) {
-            log.warn("AllVlan not supported yet");
-            return UNSUPPORTED;
-        }
-        // The vlan tags for cP1 and cP2 have to be different from
-        // vlan none.
-        if (ingressInner.equals(VlanId.NONE) ||
-                ingressOuter.equals(VlanId.NONE) ||
-                egressInner.equals(VlanId.NONE) ||
-                egressOuter.equals(VlanId.NONE)) {
-            log.warn("The vlan tags for the connect point have to be" +
-                             "different from vlan none");
-            return WRONG_PARAMETERS;
-        }
-        return SUCCESS;
-    }
-
-    /**
      * Handles the tunnel establishment which consists in
      * create the next objectives related to the initiation.
      *
-     * @param l2Tunnel the tunnel to deploy
-     * @param ingress the ingress connect point
-     * @param egress the egress connect point
+     * @param l2Tunnel  the tunnel to deploy
+     * @param ingress   the ingress connect point
+     * @param egress    the egress connect point
      * @param direction the direction of the pw
      * @return the result of the operation
      */
-    private Result deployPseudoWireInit(DefaultL2Tunnel l2Tunnel,
-                                        ConnectPoint ingress,
-                                        ConnectPoint egress,
-                                        Direction direction) {
-        if (!srManager.mastershipService.isLocalMaster(ingress.deviceId())) {
-            log.info("Abort initiation of tunnel {}: I am not the master", l2Tunnel.tunnelId());
-            return SUCCESS;
-        }
-        // We need at least a path between ingress and egress.
-        Link nextHop = getNextHop(ingress, egress);
+    private Result deployPseudoWireInit(DefaultL2Tunnel l2Tunnel, ConnectPoint ingress,
+                                        ConnectPoint egress, Direction direction, Link nextHop) {
+
         if (nextHop == null) {
-            log.warn("No path between ingress and egress");
+            log.warn("No path between ingress and egress cps for tunnel {}", l2Tunnel.tunnelId());
             return WRONG_PARAMETERS;
         }
+
         // We create the next objective without the metadata
         // context and id. We check if it already exists in the
         // store. If not we store as it is in the store.
-        NextObjective.Builder nextObjectiveBuilder = createNextObjective(
-                INITIATION,
-                nextHop.src(),
-                nextHop.dst(),
-                l2Tunnel,
-                egress.deviceId()
-        );
+        NextObjective.Builder nextObjectiveBuilder = createNextObjective(INITIATION,
+                                                                         nextHop.src(),
+                                                                         nextHop.dst(),
+                                                                         l2Tunnel,
+                                                                         egress.deviceId());
+
         if (nextObjectiveBuilder == null) {
             return INTERNAL_ERROR;
         }
@@ -676,19 +940,18 @@
         nextObjectiveBuilder.withId(nextId);
         String key = generateKey(l2Tunnel.tunnelId(), direction);
         l2InitiationNextObjStore.put(key, nextObjectiveBuilder.add());
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective)
-                        -> log.debug("Initiation l2 tunnel rule for {} populated",
-                                     l2Tunnel.tunnelId()),
-                (objective, error)
-                        -> log.warn("Failed to populate Initiation l2 tunnel rule for {}: {}",
-                                    l2Tunnel.tunnelId(), error));
+        ObjectiveContext context = new DefaultObjectiveContext((objective) ->
+                                                                 log.debug("Initiation l2 tunnel rule " +
+                                                                                   "for {} populated",
+                                                                           l2Tunnel.tunnelId()),
+                                                               (objective, error) ->
+                                                                       log.warn("Failed to populate Initiation " +
+                                                                                        "l2 tunnel rule for {}: {}",
+                                                                                l2Tunnel.tunnelId(), error));
         NextObjective nextObjective = nextObjectiveBuilder.add(context);
         srManager.flowObjectiveService.next(ingress.deviceId(), nextObjective);
         log.debug("Initiation next objective for {} not found. Creating new NextObj with id={}",
-                  l2Tunnel.tunnelId(),
-                  nextObjective.id()
-        );
+                  l2Tunnel.tunnelId(), nextObjective.id());
         Result result = SUCCESS;
         result.nextId = nextObjective.id();
         return result;
@@ -698,30 +961,18 @@
      * Handles the tunnel termination, which consists in the creation
      * of a forwarding objective and a next objective.
      *
-     * @param l2Tunnel the tunnel to terminate
-     * @param egress the egress point
+     * @param l2Tunnel   the tunnel to terminate
+     * @param egress     the egress point
      * @param egressVlan the expected vlan at egress
-     * @param direction the direction
+     * @param direction  the direction
      * @return the result of the operation
      */
-    private Result deployPseudoWireTerm(DefaultL2Tunnel l2Tunnel,
-                                        ConnectPoint egress,
-                                        VlanId egressVlan,
-                                        Direction direction) {
+    private Result deployPseudoWireTerm(DefaultL2Tunnel l2Tunnel, ConnectPoint egress,
+                                        VlanId egressVlan, Direction direction) {
+
         // We create the group relative to the termination.
-        // It's fine to abort the termination if we are
-        // not the master.
-        if (!srManager.mastershipService.isLocalMaster(egress.deviceId())) {
-            log.info("Abort termination of tunnel {}: I am not the master", l2Tunnel.tunnelId());
-            return SUCCESS;
-        }
-        NextObjective.Builder nextObjectiveBuilder = createNextObjective(
-                TERMINATION,
-                egress,
-                null,
-                null,
-                egress.deviceId()
-        );
+        NextObjective.Builder nextObjectiveBuilder = createNextObjective(TERMINATION, egress, null,
+                                                                         null, egress.deviceId());
         if (nextObjectiveBuilder == null) {
             return INTERNAL_ERROR;
         }
@@ -738,74 +989,47 @@
         nextObjectiveBuilder.withId(nextId);
         String key = generateKey(l2Tunnel.tunnelId(), direction);
         l2TerminationNextObjStore.put(key, nextObjectiveBuilder.add());
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective)
-                        -> log.debug("Termination l2 tunnel rule for {} populated",
-                                     l2Tunnel.tunnelId()),
-                (objective, error)
-                        -> log.warn("Failed to populate termination l2 tunnel rule for {}: {}",
-                                    l2Tunnel.tunnelId(), error));
+        ObjectiveContext context = new DefaultObjectiveContext((objective) -> log.debug("Termination l2 tunnel rule " +
+                                                                                        "for {} populated",
+                                                                                        l2Tunnel.tunnelId()),
+                                                               (objective, error) -> log.warn("Failed to populate " +
+                                                                                              "termination l2 tunnel " +
+                                                                                              "rule for {}: {}",
+                                                                                              l2Tunnel.tunnelId(),
+                                                                                              error));
         NextObjective nextObjective = nextObjectiveBuilder.add(context);
         srManager.flowObjectiveService.next(egress.deviceId(), nextObjective);
         log.debug("Termination next objective for {} not found. Creating new NextObj with id={}",
-                  l2Tunnel.tunnelId(),
-                  nextObjective.id()
-        );
+                  l2Tunnel.tunnelId(), nextObjective.id());
+
         // We create the flow relative to the termination.
-        ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(
-                l2Tunnel.pwLabel(),
-                l2Tunnel.tunnelId(),
-                egress.port(),
-                nextObjective.id()
-        );
-        context = new DefaultObjectiveContext(
-                (objective)
-                        -> log.debug("FwdObj for tunnel termination {} populated",
-                                     l2Tunnel.tunnelId()),
-                (objective, error)
-                        -> log.warn("Failed to populate fwdrObj for tunnel termination {}",
-                                    l2Tunnel.tunnelId(), error));
+        ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(), l2Tunnel.tunnelId(),
+                                                                        egress.port(), nextObjective.id());
+        context = new DefaultObjectiveContext((objective) -> log.debug("FwdObj for tunnel termination {} populated",
+                                                                       l2Tunnel.tunnelId()),
+                                              (objective, error) -> log.warn("Failed to populate fwdrObj" +
+                                                                             " for tunnel termination {}",
+                                                                             l2Tunnel.tunnelId(), error));
         srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.add(context));
-        log.debug("Creating new FwdObj for termination NextObj with id={} for tunnel {}", nextId, l2Tunnel.tunnelId());
+        log.debug("Creating new FwdObj for termination NextObj with id={} for tunnel {}",
+                  nextId, l2Tunnel.tunnelId());
         return SUCCESS;
 
     }
 
     /**
-     * Helper method to verify if the tunnel is whether or not
-     * supported.
-     *
-     * @param l2Tunnel the tunnel to verify
-     * @return the result of the verification
-     */
-    private Result verifyTunnel(DefaultL2Tunnel l2Tunnel) {
-        // Service delimiting tag not supported yet.
-        if (!l2Tunnel.sdTag().equals(VlanId.NONE)) {
-            log.warn("Service delimiting tag not supported yet");
-            return UNSUPPORTED;
-        }
-        // Tag mode not supported yet.
-        if (l2Tunnel.pwMode() == TAGGED) {
-            log.warn("Tagged mode not supported yet");
-            return UNSUPPORTED;
-        }
-        // Raw mode without service delimiting tag
-        // is the only mode supported for now.
-        return SUCCESS;
-    }
-
-    /**
      * Creates the filtering objective according to a given policy.
      *
-     * @param inPort the in port
+     * @param inPort   the in port
      * @param innerTag the inner vlan tag
      * @param outerTag the outer vlan tag
      * @return the filtering objective
      */
-    private FilteringObjective.Builder createFiltObjective(PortNumber inPort,
-                                                           VlanId innerTag,
-                                                           VlanId outerTag) {
-        return DefaultFilteringObjective.builder()
+    private FilteringObjective.Builder createFiltObjective(PortNumber inPort, VlanId innerTag, VlanId outerTag) {
+
+        log.info("Creating filtering objective for vlans {} / {}", outerTag, innerTag);
+        return DefaultFilteringObjective
+                .builder()
                 .withKey(Criteria.matchInPort(inPort))
                 .addCondition(Criteria.matchInnerVlanId(innerTag))
                 .addCondition(Criteria.matchVlanId(outerTag))
@@ -817,20 +1041,17 @@
     /**
      * Creates the forwarding objective for the termination.
      *
-     * @param pwLabel the pseudo wire label
-     * @param tunnelId the tunnel id
+     * @param pwLabel    the pseudo wire label
+     * @param tunnelId   the tunnel id
      * @param egressPort the egress port
-     * @param nextId the next step
+     * @param nextId     the next step
      * @return the forwarding objective to support the termination
      */
-    private ForwardingObjective.Builder createTermFwdObjective(MplsLabel pwLabel,
-                                                               long tunnelId,
-                                                               PortNumber egressPort,
-                                                               int nextId) {
-        TrafficSelector.Builder trafficSelector = DefaultTrafficSelector
-                .builder();
-        TrafficTreatment.Builder trafficTreatment = DefaultTrafficTreatment
-                .builder();
+    private ForwardingObjective.Builder createTermFwdObjective(MplsLabel pwLabel, long tunnelId,
+                                                               PortNumber egressPort, int nextId) {
+
+        TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
+        TrafficTreatment.Builder trafficTreatment = DefaultTrafficTreatment.builder();
         // The flow has to match on the pw label and bos
         trafficSelector.matchEthType(Ethernet.MPLS_UNICAST);
         trafficSelector.matchMplsLabel(pwLabel);
@@ -843,7 +1064,8 @@
         trafficTreatment.setTunnelId(tunnelId);
         trafficTreatment.setOutput(egressPort);
 
-        return DefaultForwardingObjective.builder()
+        return DefaultForwardingObjective
+                .builder()
                 .fromApp(srManager.appId())
                 .makePermanent()
                 .nextStep(nextId)
@@ -857,21 +1079,21 @@
      * Creates the forwarding objective for the initiation.
      *
      * @param tunnelId the tunnel id
-     * @param inPort the input port
-     * @param nextId the next step
+     * @param inPort   the input port
+     * @param nextId   the next step
      * @return the forwarding objective to support the initiation.
      */
-    private ForwardingObjective.Builder createInitFwdObjective(long tunnelId,
-                                                               PortNumber inPort,
-                                                               int nextId) {
-        TrafficSelector.Builder trafficSelector = DefaultTrafficSelector
-                .builder();
+    private ForwardingObjective.Builder createInitFwdObjective(long tunnelId, PortNumber inPort, int nextId) {
+
+        TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
+
         // The flow has to match on the mpls logical
         // port and the tunnel id.
         trafficSelector.matchTunnelId(tunnelId);
         trafficSelector.matchInPort(inPort);
 
-        return DefaultForwardingObjective.builder()
+        return DefaultForwardingObjective
+                .builder()
                 .fromApp(srManager.appId())
                 .makePermanent()
                 .nextStep(nextId)
@@ -888,16 +1110,14 @@
      * the same next objective for different tunnels.
      *
      * @param pipeline the pipeline to support
-     * @param srcCp the source port
-     * @param dstCp the destination port
+     * @param srcCp    the source port
+     * @param dstCp    the destination port
      * @param l2Tunnel the tunnel to support
      * @param egressId the egress device id
      * @return the next objective to support the pipeline
      */
-    private NextObjective.Builder createNextObjective(Pipeline pipeline,
-                                                      ConnectPoint srcCp,
-                                                      ConnectPoint dstCp,
-                                                      DefaultL2Tunnel l2Tunnel,
+    private NextObjective.Builder createNextObjective(Pipeline pipeline, ConnectPoint srcCp,
+                                                      ConnectPoint dstCp,  DefaultL2Tunnel l2Tunnel,
                                                       DeviceId egressId) {
         NextObjective.Builder nextObjBuilder;
         TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
@@ -924,16 +1144,17 @@
                 treatmentBuilder.copyTtlOut();
             }
             // We retrieve the sr label from the config
+            // specific for pseudowire traffic
             // using the egress leaf device id.
             MplsLabel srLabel;
             try {
-                 srLabel = MplsLabel.mplsLabel(
-                         srManager.deviceConfiguration().getIPv4SegmentId(egressId)
-                 );
+                srLabel = MplsLabel.mplsLabel(srManager.deviceConfiguration().getPWRoutingLabel(egressId));
+
             } catch (DeviceConfigNotFoundException e) {
-                log.warn("Sr label not configured");
+                log.warn("Sr label for pw traffic not configured");
                 return null;
             }
+
             treatmentBuilder.pushMpls();
             treatmentBuilder.setMpls(srLabel);
             treatmentBuilder.setMplsBos(false);
@@ -941,9 +1162,7 @@
             // We have to rewrite the src and dst mac address.
             MacAddress ingressMac;
             try {
-                ingressMac = srManager
-                        .deviceConfiguration()
-                        .getDeviceMac(srcCp.deviceId());
+                ingressMac = srManager.deviceConfiguration().getDeviceMac(srcCp.deviceId());
             } catch (DeviceConfigNotFoundException e) {
                 log.warn("Was not able to find the ingress mac");
                 return null;
@@ -951,9 +1170,7 @@
             treatmentBuilder.setEthSrc(ingressMac);
             MacAddress neighborMac;
             try {
-                neighborMac = srManager
-                        .deviceConfiguration()
-                        .getDeviceMac(dstCp.deviceId());
+                neighborMac = srManager.deviceConfiguration().getDeviceMac(dstCp.deviceId());
             } catch (DeviceConfigNotFoundException e) {
                 log.warn("Was not able to find the neighbor mac");
                 return null;
@@ -973,54 +1190,64 @@
     }
 
     /**
-     * Returns the next hop.
+     * Reverses a link.
      *
-     * @param srcCp the ingress connect point
-     * @param dstCp the egress connect point
-     * @return the next hop
+     * @param link
+     * @return The reversed link
      */
-    private Link getNextHop(ConnectPoint srcCp, ConnectPoint dstCp) {
-        // We retrieve a set of disjoint paths.
-        Set<DisjointPath> paths = srManager.pathService.getDisjointPaths(
-                srcCp.elementId(),
-                dstCp.elementId()
-        );
-        // We randmly pick a path.
+    private Link reverseLink(Link link) {
+
+        DefaultLink.Builder linkBuilder = DefaultLink.builder();
+
+        linkBuilder.src(link.dst());
+        linkBuilder.dst(link.src());
+        linkBuilder.type(link.type());
+        linkBuilder.providerId(link.providerId());
+
+        return linkBuilder.build();
+    }
+
+    /**
+     * Returns the path betwwen two connect points.
+     *
+     * @param srcCp
+     * @param dstCp
+     * @return The path
+     */
+    private List<Link> getPath(ConnectPoint srcCp, ConnectPoint dstCp) {
+
+        /* We retrieve a set of disjoint paths.
+        * We perform that in case of a link failure, what happens
+        * if the PathService gets the link notification AFTER us and
+        * has not updated the paths?
+        */
+        Set<DisjointPath> paths = srManager
+                .pathService
+                .getDisjointPaths(srcCp.elementId(), dstCp.elementId());
+
+        // We randomly pick a path.
         if (paths.isEmpty()) {
             return null;
         }
         int size = paths.size();
         int index = RandomUtils.nextInt(0, size);
-        // We verify if the path is ok and there is not
-        // a misconfiguration.
-        List<Link> links = Iterables.get(paths, index).links();
-        checkState(links.size() == 2, WRONG_TOPOLOGY, links);
-        return links.get(0);
+
+        return Iterables.get(paths, index).links();
     }
 
     /**
      * Deletes a given policy using the parameter supplied.
      *
-     * @param tunnelId the tunnel id
-     * @param ingress the ingress point
+     * @param tunnelId     the tunnel id
+     * @param ingress      the ingress point
      * @param ingressInner the ingress inner vlan id
      * @param ingressOuter the ingress outer vlan id
-     * @param future to perform the async operation
-     * @param direction the direction: forward or reverse
+     * @param future       to perform the async operation
+     * @param direction    the direction: forward or reverse
      */
-    private void deletePolicy(long tunnelId,
-                              ConnectPoint ingress,
-                              VlanId ingressInner,
-                              VlanId ingressOuter,
-                              CompletableFuture<ObjectiveError> future,
-                              Direction direction) {
-        if (!srManager.mastershipService.isLocalMaster(ingress.deviceId())) {
-            log.info("Abort delete of policy for tunnel {}: I am not the master", tunnelId);
-            if (future != null) {
-                future.complete(null);
-            }
-            return;
-        }
+    private void deletePolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner, VlanId ingressOuter,
+                              VlanId egressVlan, CompletableFuture<ObjectiveError> future, Direction direction) {
+
         String key = generateKey(tunnelId, direction);
         if (!l2InitiationNextObjStore.containsKey(key)) {
             log.warn("Abort delete of policy for tunnel {}: next does not exist in the store", tunnelId);
@@ -1033,11 +1260,7 @@
         int nextId = nextObjective.id();
         List<Objective> objectives = Lists.newArrayList();
         // We create the forwarding objective.
-        ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(
-                tunnelId,
-                ingress.port(),
-                nextId
-        );
+        ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
         ObjectiveContext context = new ObjectiveContext() {
             @Override
             public void onSuccess(Objective objective) {
@@ -1058,19 +1281,16 @@
         objectives.add(fwdBuilder.remove(context));
         // We create the filtering objective to define the
         // permit traffic in the switch
-        FilteringObjective.Builder filtBuilder = createFiltObjective(
-                ingress.port(),
-                ingressInner,
-                ingressOuter
-        );
-        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
-                .setTunnelId(tunnelId);
+        FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
+        TrafficTreatment.Builder treatment = DefaultTrafficTreatment
+                .builder()
+                .setTunnelId(tunnelId)
+                .setVlanId(egressVlan);
         filtBuilder.withMeta(treatment.build());
-        context = new DefaultObjectiveContext(
-                (objective)
-                        -> log.debug("FilterObj for policy {} revoked", tunnelId),
-                (objective, error)
-                        -> log.warn("Failed to revoke filterObj for policy {}", tunnelId, error));
+        context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for policy {} revoked", tunnelId),
+                                              (objective, error) ->
+                                                      log.warn("Failed to revoke filterObj for policy {}",
+                                                               tunnelId, error));
         objectives.add(filtBuilder.remove(context));
 
         for (Objective objective : objectives) {
@@ -1086,22 +1306,14 @@
      * Deletes the pseudo wire initiation.
      *
      * @param l2TunnelId the tunnel id
-     * @param ingress the ingress connect point
-     * @param future to perform an async operation
-     * @param direction the direction: reverse of forward
+     * @param ingress    the ingress connect point
+     * @param future     to perform an async operation
+     * @param direction  the direction: reverse of forward
      */
-    private void tearDownPseudoWireInit(long l2TunnelId,
-                                        ConnectPoint ingress,
-                                        CompletableFuture<ObjectiveError> future,
-                                        Direction direction) {
+    private void tearDownPseudoWireInit(long l2TunnelId, ConnectPoint ingress,
+                                        CompletableFuture<ObjectiveError> future, Direction direction) {
+
         String key = generateKey(l2TunnelId, direction);
-        if (!srManager.mastershipService.isLocalMaster(ingress.deviceId())) {
-            log.info("Abort delete of {} for {}: I am not the master", INITIATION, key);
-            if (future != null) {
-                future.complete(null);
-            }
-            return;
-        }
         if (!l2InitiationNextObjStore.containsKey(key)) {
             log.info("Abort delete of {} for {}: next does not exist in the store", INITIATION, key);
             if (future != null) {
@@ -1110,6 +1322,10 @@
             return;
         }
         NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
+        // un-comment in case you want to delete groups used by the pw
+        // however, this will break the update of pseudowires cause the L2 interface group can
+        // not be deleted (it is referenced by other groups)
+        /*
         ObjectiveContext context = new ObjectiveContext() {
             @Override
             public void onSuccess(Objective objective) {
@@ -1127,34 +1343,27 @@
                 }
             }
         };
-        srManager.flowObjectiveService
-                .next(ingress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
+        srManager.flowObjectiveService.next(ingress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
+        */
+
+        future.complete(null);
         l2InitiationNextObjStore.remove(key);
     }
 
     /**
      * Deletes the pseudo wire termination.
      *
-     * @param l2Tunnel the tunnel
-     * @param egress the egress connect point
-     * @param future the async task
+     * @param l2Tunnel  the tunnel
+     * @param egress    the egress connect point
+     * @param future    the async task
      * @param direction the direction of the tunnel
      */
     private void tearDownPseudoWireTerm(DefaultL2Tunnel l2Tunnel,
                                         ConnectPoint egress,
                                         CompletableFuture<ObjectiveError> future,
                                         Direction direction) {
-        /*
-         * We verify the mastership for the termination.
-         */
+
         String key = generateKey(l2Tunnel.tunnelId(), direction);
-        if (!srManager.mastershipService.isLocalMaster(egress.deviceId())) {
-            log.info("Abort delete of {} for {}: I am not the master", TERMINATION, key);
-            if (future != null) {
-                future.complete(null);
-            }
-            return;
-        }
         if (!l2TerminationNextObjStore.containsKey(key)) {
             log.info("Abort delete of {} for {}: next does not exist in the store", TERMINATION, key);
             if (future != null) {
@@ -1163,20 +1372,24 @@
             return;
         }
         NextObjective nextObjective = l2TerminationNextObjStore.get(key).value();
-        ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(
-                l2Tunnel.pwLabel(),
-                l2Tunnel.tunnelId(),
-                egress.port(),
-                nextObjective.id()
-        );
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective)
-                        -> log.debug("FwdObj for {} {} removed", TERMINATION, l2Tunnel.tunnelId()),
-                (objective, error)
-                        -> log.warn("Failed to remove fwdObj for {} {}", TERMINATION, l2Tunnel.tunnelId(),
-                                    error));
+        ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(),
+                                                                        l2Tunnel.tunnelId(),
+                                                                        egress.port(),
+                                                                        nextObjective.id());
+        ObjectiveContext context = new DefaultObjectiveContext((objective) -> log.debug("FwdObj for {} {} removed",
+                                                                                        TERMINATION,
+                                                                                        l2Tunnel.tunnelId()),
+                                                               (objective, error) ->
+                                                                       log.warn("Failed to remove fwdObj for {} {}",
+                                                                                TERMINATION,
+                                                                                l2Tunnel.tunnelId(),
+                                                                                error));
         srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.remove(context));
 
+        // un-comment in case you want to delete groups used by the pw
+        // however, this will break the update of pseudowires cause the L2 interface group can
+        // not be deleted (it is referenced by other groups)
+        /*
         context = new ObjectiveContext() {
             @Override
             public void onSuccess(Objective objective) {
@@ -1194,15 +1407,17 @@
                 }
             }
         };
-        srManager.flowObjectiveService
-                .next(egress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
+        srManager.flowObjectiveService.next(egress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
+        */
+
+        future.complete(null);
         l2TerminationNextObjStore.remove(key);
     }
 
     /**
      * Utilities to generate pw key.
      *
-     * @param tunnelId the tunnel id
+     * @param tunnelId  the tunnel id
      * @param direction the direction of the pw
      * @return the key of the store
      */
@@ -1217,38 +1432,46 @@
         /**
          * The initiation pipeline.
          */
-        INITIATION,
-        /**
+        INITIATION, /**
          * The termination pipeline.
          */
         TERMINATION
     }
 
     /**
-     * Enum helper to carry the outcomes of an operation.
+     * Enum helper to carry results of various operations.
      */
     public enum Result {
         /**
-         * Happy ending scenario it has been created.
+         * Happy ending scenario.
          */
-        SUCCESS(0, "It has been Created"),
+        SUCCESS(0, "No error occurred"),
+
         /**
          * We have problems with the supplied parameters.
          */
         WRONG_PARAMETERS(1, "Wrong parameters"),
-        /**
-         * It already exists.
-         */
-        ID_EXISTS(2, "The id already exists"),
+
         /**
          * We have an internal error during the deployment
-         * phase.
+         * or removal phase.
          */
         INTERNAL_ERROR(3, "Internal error"),
+
         /**
-         * The operation is not supported.
+         *
          */
-        UNSUPPORTED(4, "Unsupported");
+        REMOVAL_ERROR(5, "Can not remove pseudowire from network configuration"),
+
+        /**
+         *
+         */
+        ADDITION_ERROR(6, "Can not add pseudowire in network configuration"),
+
+        /**
+         *
+         */
+        CONFIG_NOT_FOUND(7, "Can not find configuration class for pseudowires");
 
         private final int code;
         private final String description;
@@ -1276,8 +1499,7 @@
         /**
          * The forward direction of the pseudo wire.
          */
-        FWD,
-        /**
+        FWD, /**
          * The reverse direction of the pseudo wire.
          */
         REV
diff --git a/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index da4ebe3..4e00481 100644
--- a/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -54,9 +54,22 @@
                 <ref component-id="deviceIdCompleter"/>
             </completers>
         </command>
+        <command>
+            <action class="org.onosproject.segmentrouting.cli.PseudowireListCommand"/>
+        </command>
+        <command>
+            <action class="org.onosproject.segmentrouting.cli.PseudowireRemoveCommand"/>
+            <completers>
+                <ref component-id="pseudowireIdCompleter"/>
+            </completers>
+        </command>
+        <command>
+            <action class="org.onosproject.segmentrouting.cli.PseudowireAddCommand"/>
+        </command>
     </command-bundle>
 
     <bean id="deviceIdCompleter" class="org.onosproject.cli.net.DeviceIdCompleter"/>
+    <bean id="pseudowireIdCompleter" class="org.onosproject.segmentrouting.cli.PseudowireIdCompleter"/>
 
 </blueprint>
 
diff --git a/src/test/java/org/onosproject/segmentrouting/MockDevice.java b/src/test/java/org/onosproject/segmentrouting/MockDevice.java
new file mode 100644
index 0000000..0abbf2a
--- /dev/null
+++ b/src/test/java/org/onosproject/segmentrouting/MockDevice.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.segmentrouting;
+
+import org.onosproject.net.Annotations;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.DeviceId;
+/**
+ * Test fixture for the device service.
+ */
+public class MockDevice extends DefaultDevice {
+
+        public MockDevice(DeviceId id, Annotations annotations) {
+            super(null, id, null, null, null, null, null, null, annotations);
+        }
+ }
+
diff --git a/src/test/java/org/onosproject/segmentrouting/MockDeviceService.java b/src/test/java/org/onosproject/segmentrouting/MockDeviceService.java
new file mode 100644
index 0000000..1d173f6
--- /dev/null
+++ b/src/test/java/org/onosproject/segmentrouting/MockDeviceService.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.segmentrouting;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultPort;
+import org.onosproject.net.Port;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceServiceAdapter;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+
+/**
+ * Test fixture for the device service.
+ */
+public class MockDeviceService extends DeviceServiceAdapter {
+    private List<Device> devices = new LinkedList<>();
+    private DeviceListener listener;
+
+    public void addDevice(Device dev) {
+        devices.add(dev);
+    }
+
+    public void addMultipleDevices(Set<Device> devicesToAdd) {
+        devicesToAdd.forEach(dev -> devices.add(dev));
+    }
+
+    @Override
+    public Device getDevice(DeviceId deviceId) {
+        for (Device dev : devices) {
+            if (dev.id().equals(deviceId)) {
+                return dev;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Port getPort(ConnectPoint cp) {
+        return new DefaultPort(null, null, false);
+    }
+
+    @Override
+    public Iterable<Device> getAvailableDevices() {
+        return devices;
+    }
+
+    @Override
+    public void addListener(DeviceListener listener) {
+        this.listener = listener;
+    }
+
+    /**
+     * Get the listener.
+     */
+    public DeviceListener getListener() {
+        return listener;
+    }
+
+}
diff --git a/src/test/java/org/onosproject/segmentrouting/PwaasConfigTest.java b/src/test/java/org/onosproject/segmentrouting/PwaasConfigTest.java
new file mode 100644
index 0000000..7686242
--- /dev/null
+++ b/src/test/java/org/onosproject/segmentrouting/PwaasConfigTest.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.segmentrouting;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.MplsLabel;
+import org.onlab.packet.VlanId;
+import org.onosproject.TestApplicationId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultHost;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+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.Config;
+import org.onosproject.net.config.ConfigApplyDelegate;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.host.InterfaceIpAddress;
+import org.onosproject.net.intf.Interface;
+import org.onosproject.net.intf.InterfaceService;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.segmentrouting.config.PwaasConfig;
+import org.onosproject.segmentrouting.pwaas.DefaultL2Tunnel;
+import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelDescription;
+import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelPolicy;
+import org.onosproject.segmentrouting.pwaas.L2Mode;
+
+import java.io.InputStream;
+import java.util.Set;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.*;
+
+/**
+ * Unit tests for class {@link PwaasConfig}.
+ */
+public class PwaasConfigTest {
+
+    private static final String TUNNEL_ID_1 = "1";
+    private static final String TUNNEL_ID_2 = "20";
+    private static final String NOT_PRESENT_TUNNEL_ID = "2";
+    private static final ConnectPoint INGRESS_1 = ConnectPoint.deviceConnectPoint("of:0000000000000001/1");
+    private static final ConnectPoint INGRESS_2 = ConnectPoint.deviceConnectPoint("of:0000000000000001/1");
+    private static final ConnectPoint EGRESS_1 = ConnectPoint.deviceConnectPoint("of:0000000000000002/1");
+    private static final ConnectPoint EGRESS_2 = ConnectPoint.deviceConnectPoint("of:0000000000000002/1");
+    private static final VlanId INGRESS_INNER_TAG_1 = VlanId.vlanId("10");
+    private static final VlanId INGRESS_INNER_TAG_2 = VlanId.vlanId("100");
+    private static final VlanId INGRESS_OUTER_TAG_1 = VlanId.vlanId("20");
+    private static final VlanId INGRESS_OUTER_TAG_2 = VlanId.vlanId("200");
+    private static final VlanId EGRESS_INNER_TAG_1 = VlanId.vlanId("10");
+    private static final VlanId EGRESS_INNER_TAG_2 = VlanId.vlanId("100");
+    private static final VlanId EGRESS_OUTER_TAG_1 = VlanId.vlanId("21");
+    private static final VlanId EGRESS_OUTER_TAG_2 = VlanId.vlanId("210");
+    private static final String MODE_1 = "RAW";
+    private static final String MODE_2 = "RAW";
+    private static final VlanId SD_TAG_1 = VlanId.NONE;
+    private static final VlanId SD_TAG_2 = VlanId.NONE;
+    private static final MplsLabel PW_LABEL_1 = MplsLabel.mplsLabel("255");
+    private static final MplsLabel PW_LABEL_2 = MplsLabel.mplsLabel("1255");
+
+    /*
+     * Configuration below copied from host handler test.
+     */
+
+    // Host Mac, VLAN
+    private static final ProviderId PROVIDER_ID = ProviderId.NONE;
+    private static final MacAddress HOST_MAC = MacAddress.valueOf("00:00:00:00:00:01");
+    private static final VlanId HOST_VLAN_UNTAGGED = VlanId.NONE;
+    private static final HostId HOST_ID_UNTAGGED = HostId.hostId(HOST_MAC, HOST_VLAN_UNTAGGED);
+    private static final VlanId HOST_VLAN_TAGGED = VlanId.vlanId((short) 20);
+    private static final HostId HOST_ID_TAGGED = HostId.hostId(HOST_MAC, HOST_VLAN_TAGGED);
+    // Host IP
+    private static final IpAddress HOST_IP11 = IpAddress.valueOf("10.0.1.1");
+    private static final IpAddress HOST_IP21 = IpAddress.valueOf("10.0.2.1");
+    private static final IpAddress HOST_IP12 = IpAddress.valueOf("10.0.1.2");
+    private static final IpAddress HOST_IP13 = IpAddress.valueOf("10.0.1.3");
+    private static final IpAddress HOST_IP14 = IpAddress.valueOf("10.0.1.4");
+    private static final IpAddress HOST_IP32 = IpAddress.valueOf("10.0.3.2");
+    // Device
+    private static final DeviceId DEV1 = DeviceId.deviceId("of:0000000000000001");
+    private static final DeviceId DEV2 = DeviceId.deviceId("of:0000000000000002");
+    private static final DeviceId DEV3 = DeviceId.deviceId("of:0000000000000003");
+    private static final DeviceId DEV4 = DeviceId.deviceId("of:0000000000000004");
+    private static final DeviceId DEV5 = DeviceId.deviceId("of:0000000000000005");
+    private static final DeviceId DEV6 = DeviceId.deviceId("of:0000000000000006");
+    // Port
+    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 PortNumber P9 = PortNumber.portNumber(9);
+    // Connect Point
+    private static final ConnectPoint CP11 = new ConnectPoint(DEV1, P1);
+    private static final HostLocation HOST_LOC11 = new HostLocation(CP11, 0);
+    private static final ConnectPoint CP12 = new ConnectPoint(DEV1, P2);
+    private static final HostLocation HOST_LOC12 = new HostLocation(CP12, 0);
+    private static final ConnectPoint CP13 = new ConnectPoint(DEV1, P3);
+    private static final HostLocation HOST_LOC13 = new HostLocation(CP13, 0);
+    private static final ConnectPoint CP21 = new ConnectPoint(DEV2, P1);
+    private static final HostLocation HOST_LOC21 = new HostLocation(CP21, 0);
+    private static final ConnectPoint CP22 = new ConnectPoint(DEV2, P2);
+    private static final HostLocation HOST_LOC22 = new HostLocation(CP22, 0);
+    // Connect Point for dual-homed host failover
+    private static final ConnectPoint CP31 = new ConnectPoint(DEV3, P1);
+    private static final HostLocation HOST_LOC31 = new HostLocation(CP31, 0);
+    private static final ConnectPoint CP32 = new ConnectPoint(DEV3, P2);
+    private static final HostLocation HOST_LOC32 = new HostLocation(CP32, 0);
+    private static final ConnectPoint CP41 = new ConnectPoint(DEV4, P1);
+    private static final HostLocation HOST_LOC41 = new HostLocation(CP41, 0);
+    private static final ConnectPoint CP39 = new ConnectPoint(DEV3, P9);
+    private static final ConnectPoint CP49 = new ConnectPoint(DEV4, P9);
+    // Conenct Point for mastership test
+    private static final ConnectPoint CP51 = new ConnectPoint(DEV5, P1);
+    private static final HostLocation HOST_LOC51 = new HostLocation(CP51, 0);
+    private static final ConnectPoint CP61 = new ConnectPoint(DEV6, P1);
+    private static final HostLocation HOST_LOC61 = new HostLocation(CP61, 0);
+    // Interface VLAN
+    private static final VlanId INTF_VLAN_UNTAGGED = VlanId.vlanId((short) 10);
+    private static final Set<VlanId> INTF_VLAN_TAGGED = Sets.newHashSet(VlanId.vlanId((short) 20));
+    private static final VlanId INTF_VLAN_NATIVE = VlanId.vlanId((short) 30);
+    private static final Set<VlanId> INTF_VLAN_PAIR = Sets.newHashSet(VlanId.vlanId((short) 10),
+                                     VlanId.vlanId((short) 20), VlanId.vlanId((short) 30));
+    private static final VlanId INTF_VLAN_OTHER = VlanId.vlanId((short) 40);
+    // Interface subnet
+    private static final IpPrefix INTF_PREFIX1 = IpPrefix.valueOf("10.0.1.254/24");
+    private static final IpPrefix INTF_PREFIX2 = IpPrefix.valueOf("10.0.2.254/24");
+    private static final IpPrefix INTF_PREFIX3 = IpPrefix.valueOf("10.0.3.254/24");
+    private static final InterfaceIpAddress INTF_IP1 =
+            new InterfaceIpAddress(INTF_PREFIX1.address(), INTF_PREFIX1);
+    private static final InterfaceIpAddress INTF_IP2 =
+            new InterfaceIpAddress(INTF_PREFIX2.address(), INTF_PREFIX2);
+    private static final InterfaceIpAddress INTF_IP3 =
+            new InterfaceIpAddress(INTF_PREFIX3.address(), INTF_PREFIX3);
+    // Interfaces
+    private static final Interface INTF11 =
+            new Interface(null, CP11, Lists.newArrayList(INTF_IP1), MacAddress.NONE, null,
+                          INTF_VLAN_UNTAGGED, null, null);
+    private static final Interface INTF12 =
+            new Interface(null, CP12, Lists.newArrayList(INTF_IP1), MacAddress.NONE, null,
+                          INTF_VLAN_UNTAGGED, null, null);
+    private static final Interface INTF13 =
+            new Interface(null, CP13, Lists.newArrayList(INTF_IP2), MacAddress.NONE, null,
+                          null, INTF_VLAN_TAGGED, INTF_VLAN_NATIVE);
+    private static final Interface INTF21 =
+            new Interface(null, CP21, Lists.newArrayList(INTF_IP1), MacAddress.NONE, null,
+                          INTF_VLAN_UNTAGGED, null, null);
+    private static final Interface INTF22 =
+            new Interface(null, CP22, Lists.newArrayList(INTF_IP1), MacAddress.NONE, null,
+                          INTF_VLAN_UNTAGGED, null, null);
+    private static final Interface INTF31 =
+            new Interface(null, CP31, Lists.newArrayList(INTF_IP1), MacAddress.NONE, null,
+                          INTF_VLAN_UNTAGGED, null, null);
+    private static final Interface INTF32 =
+            new Interface(null, CP32, Lists.newArrayList(INTF_IP3), MacAddress.NONE, null,
+                          INTF_VLAN_OTHER, null, null);
+    private static final Interface INTF39 =
+            new Interface(null, CP39, Lists.newArrayList(INTF_IP1), MacAddress.NONE, null,
+                          null, INTF_VLAN_PAIR, null);
+    private static final Interface INTF41 =
+            new Interface(null, CP41, Lists.newArrayList(INTF_IP1), MacAddress.NONE, null,
+                          INTF_VLAN_UNTAGGED, null, null);
+    private static final Interface INTF49 =
+            new Interface(null, CP49, Lists.newArrayList(INTF_IP1), MacAddress.NONE, null,
+                          null, INTF_VLAN_PAIR, null);
+    // Host
+    private static final Host HOST1 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC,
+                                                      HOST_VLAN_UNTAGGED,
+                                                      Sets.newHashSet(HOST_LOC11, HOST_LOC21),
+                                                      Sets.newHashSet(HOST_IP11),
+                                                      false);
+
+    // A set of hosts
+    private static final Set<Host> HOSTS = Sets.newHashSet(HOST1);
+    // A set of devices of which we have mastership
+    private static final Set<DeviceId> LOCAL_DEVICES = Sets.newHashSet(DEV1, DEV2, DEV3, DEV4);
+    // A set of interfaces
+    private static final Set<Interface> INTERFACES = Sets.newHashSet(INTF11, INTF12, INTF13, INTF21,
+                                                                     INTF22, INTF31, INTF32, INTF39, INTF41, INTF49);
+
+    private PwaasConfig config;
+    private PwaasConfig invalidConfigVlan;
+    private PwaasConfig invalidConfigMode;
+    private PwaasConfig invalidConfigLabel;
+    private PwaasConfig invalidConfigConflictingVlan;
+
+    @Before
+    public void setUp() throws Exception {
+        InputStream jsonStream = PwaasConfig.class
+                .getResourceAsStream("/pwaas.json");
+        InputStream jsonStreamInvalid1 = PwaasConfig.class
+                .getResourceAsStream("/pwaas-invalid-mode.json");
+        InputStream jsonStreamInvalid2 = PwaasConfig.class
+                .getResourceAsStream("/pwaas-invalid-pwlabel.json");
+        InputStream jsonStreamInvalid3 = PwaasConfig.class
+                .getResourceAsStream("/pwaas-invalid-vlan.json");
+        InputStream jsonStreamInvalid4 = PwaasConfig.class
+                .getResourceAsStream("/pwaas-conflicting-vlan.json");
+
+        String key = SegmentRoutingManager.APP_NAME;
+        ApplicationId subject = new TestApplicationId(key);
+        ObjectMapper mapper = new ObjectMapper();
+
+        JsonNode jsonNode = mapper.readTree(jsonStream);
+        JsonNode jsonNodeInvalid1 = mapper.readTree(jsonStreamInvalid1);
+        JsonNode jsonNodeInvalid2 = mapper.readTree(jsonStreamInvalid2);
+        JsonNode jsonNodeInvalid3 = mapper.readTree(jsonStreamInvalid3);
+        JsonNode jsonNodeInvalid4 = mapper.readTree(jsonStreamInvalid4);
+
+        ConfigApplyDelegate delegate = new MockDelegate();
+
+        DeviceService devService = new MockDeviceService();
+        InterfaceService infService = new MockInterfaceService(INTERFACES);
+
+        // create two devices and add them
+        DefaultAnnotations.Builder builderDev1 = DefaultAnnotations.builder();
+        DefaultAnnotations.Builder builderDev2 = DefaultAnnotations.builder();
+
+        Device dev1 = new MockDevice(DEV1, builderDev1.build());
+        Device dev2 = new MockDevice(DEV2, builderDev2.build());
+        ((MockDeviceService) devService).addDevice(dev1);
+        ((MockDeviceService) devService).addDevice(dev2);
+
+        config = new PwaasConfig(devService, infService);
+        invalidConfigVlan = new PwaasConfig(devService, infService);
+        invalidConfigMode = new PwaasConfig(devService, infService);
+        invalidConfigLabel = new PwaasConfig(devService, infService);
+        invalidConfigConflictingVlan = new PwaasConfig(devService, infService);
+
+        config.init(subject, key, jsonNode, mapper, delegate);
+        invalidConfigVlan.init(subject, key, jsonNodeInvalid1, mapper, delegate);
+        invalidConfigMode.init(subject, key, jsonNodeInvalid2, mapper, delegate);
+        invalidConfigLabel.init(subject, key, jsonNodeInvalid3, mapper, delegate);
+        invalidConfigConflictingVlan.init(subject, key, jsonNodeInvalid4, mapper, delegate);
+
+        config.deviceService = devService;
+        config.intfService = infService;
+
+        invalidConfigVlan.deviceService = devService;
+        invalidConfigVlan.intfService = infService;
+
+        invalidConfigLabel.deviceService = devService;
+        invalidConfigLabel.intfService = infService;
+
+        invalidConfigMode.deviceService = devService;
+        invalidConfigMode.intfService = infService;
+
+        invalidConfigConflictingVlan.deviceService = devService;
+        invalidConfigConflictingVlan.intfService = infService;
+    }
+
+    /**
+     * Tests config validity.
+     */
+    @Test
+    public void testIsValid() {
+        try {
+            assertTrue(config.isValid());
+        } catch (IllegalArgumentException e) {
+            assertTrue(false);
+        }
+    }
+
+    /**
+     * Tests config in-validity.
+     */
+    @Test
+    public void testValid1() {
+        assertFalse(invalidConfigVlan.isValid());
+    }
+
+    @Test
+    public void testValid2() {
+        assertFalse(invalidConfigMode.isValid());
+    }
+
+    @Test
+    public void testValid3() {
+        assertFalse(invalidConfigLabel.isValid());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testValid4() {
+        invalidConfigConflictingVlan.isValid();
+    }
+
+    /**
+     * Tests getPwIds.
+     */
+    @Test
+    public void testGetPwIds() {
+        Set<Long> pwIds = config.getPwIds();
+
+        assertThat(pwIds.size(), is(2));
+        assertTrue(pwIds.contains(Long.parseLong(TUNNEL_ID_1)));
+        assertTrue(pwIds.contains(Long.parseLong(TUNNEL_ID_2)));
+        assertFalse(pwIds.contains(Long.parseLong(NOT_PRESENT_TUNNEL_ID)));
+    }
+
+    /**
+     * Tests getPwDescription.
+     */
+    @Test
+    public void testGetPwDescription() {
+        DefaultL2TunnelDescription l2TunnelDescription = null;
+
+        DefaultL2Tunnel l2Tunnel = new DefaultL2Tunnel(
+            L2Mode.valueOf(MODE_1),
+            SD_TAG_1,
+            Long.parseLong(TUNNEL_ID_1),
+            PW_LABEL_1
+        );
+        DefaultL2TunnelPolicy l2TunnelPolicy = new DefaultL2TunnelPolicy(
+                Long.parseLong(TUNNEL_ID_1),
+                INGRESS_1,
+                INGRESS_INNER_TAG_1,
+                INGRESS_OUTER_TAG_1,
+                EGRESS_1,
+                EGRESS_INNER_TAG_1,
+                EGRESS_OUTER_TAG_1
+        );
+
+        l2TunnelDescription = config.getPwDescription(Long.parseLong(TUNNEL_ID_1));
+        assertThat(l2TunnelDescription.l2Tunnel().pwMode(), is(l2Tunnel.pwMode()));
+        assertThat(l2TunnelDescription.l2Tunnel().sdTag(), is(l2Tunnel.sdTag()));
+        assertThat(l2TunnelDescription.l2Tunnel().tunnelId(), is(l2Tunnel.tunnelId()));
+        assertThat(l2TunnelDescription.l2Tunnel().pwLabel(), is(l2Tunnel.pwLabel()));
+        assertThat(l2TunnelDescription.l2TunnelPolicy().tunnelId(), is(l2TunnelPolicy.tunnelId()));
+        assertThat(l2TunnelDescription.l2TunnelPolicy().cP1InnerTag(), is(l2TunnelPolicy.cP1InnerTag()));
+        assertThat(l2TunnelDescription.l2TunnelPolicy().cP1OuterTag(), is(l2TunnelPolicy.cP1OuterTag()));
+        assertThat(l2TunnelDescription.l2TunnelPolicy().cP2InnerTag(), is(l2TunnelPolicy.cP2InnerTag()));
+        assertThat(l2TunnelDescription.l2TunnelPolicy().cP2OuterTag(), is(l2TunnelPolicy.cP2OuterTag()));
+        assertThat(l2TunnelDescription.l2TunnelPolicy().cP1(), is(l2TunnelPolicy.cP1()));
+        assertThat(l2TunnelDescription.l2TunnelPolicy().cP2(), is(l2TunnelPolicy.cP2()));
+
+        l2Tunnel = new DefaultL2Tunnel(
+                L2Mode.valueOf(MODE_2),
+                SD_TAG_2,
+                Long.parseLong(TUNNEL_ID_2),
+                PW_LABEL_2
+        );
+        l2TunnelPolicy = new DefaultL2TunnelPolicy(
+                Long.parseLong(TUNNEL_ID_2),
+                INGRESS_2,
+                INGRESS_INNER_TAG_2,
+                INGRESS_OUTER_TAG_2,
+                EGRESS_2,
+                EGRESS_INNER_TAG_2,
+                EGRESS_OUTER_TAG_2
+        );
+
+        l2TunnelDescription = config.getPwDescription(Long.parseLong(TUNNEL_ID_2));
+        assertThat(l2TunnelDescription.l2Tunnel().pwMode(), is(l2Tunnel.pwMode()));
+        assertThat(l2TunnelDescription.l2Tunnel().sdTag(), is(l2Tunnel.sdTag()));
+        assertThat(l2TunnelDescription.l2Tunnel().tunnelId(), is(l2Tunnel.tunnelId()));
+        assertThat(l2TunnelDescription.l2Tunnel().pwLabel(), is(l2Tunnel.pwLabel()));
+        assertThat(l2TunnelDescription.l2TunnelPolicy().tunnelId(), is(l2TunnelPolicy.tunnelId()));
+        assertThat(l2TunnelDescription.l2TunnelPolicy().cP1InnerTag(), is(l2TunnelPolicy.cP1InnerTag()));
+        assertThat(l2TunnelDescription.l2TunnelPolicy().cP1OuterTag(), is(l2TunnelPolicy.cP1OuterTag()));
+        assertThat(l2TunnelDescription.l2TunnelPolicy().cP2OuterTag(), is(l2TunnelPolicy.cP2OuterTag()));
+        assertThat(l2TunnelDescription.l2TunnelPolicy().cP2OuterTag(), is(l2TunnelPolicy.cP2OuterTag()));
+        assertThat(l2TunnelDescription.l2TunnelPolicy().cP1(), is(l2TunnelPolicy.cP1()));
+        assertThat(l2TunnelDescription.l2TunnelPolicy().cP2(), is(l2TunnelPolicy.cP2()));
+    }
+
+    private class MockDelegate implements ConfigApplyDelegate {
+        @Override
+        public void onApply(Config config) {
+        }
+    }
+
+}
diff --git a/src/test/java/org/onosproject/segmentrouting/config/PwaasConfigTest.java b/src/test/java/org/onosproject/segmentrouting/config/PwaasConfigTest.java
deleted file mode 100644
index 3c86a6e..0000000
--- a/src/test/java/org/onosproject/segmentrouting/config/PwaasConfigTest.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting.config;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.junit.Before;
-import org.junit.Test;
-import org.onlab.packet.MplsLabel;
-import org.onlab.packet.VlanId;
-import org.onosproject.TestApplicationId;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.config.Config;
-import org.onosproject.net.config.ConfigApplyDelegate;
-import org.onosproject.segmentrouting.SegmentRoutingManager;
-import org.onosproject.segmentrouting.pwaas.DefaultL2Tunnel;
-import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelDescription;
-import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelPolicy;
-import org.onosproject.segmentrouting.pwaas.L2Mode;
-
-import java.io.InputStream;
-import java.util.Set;
-
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.*;
-
-/**
- * Unit tests for class {@link PwaasConfig}.
- */
-public class PwaasConfigTest {
-
-    private static final String TUNNEL_ID_1 = "1";
-    private static final String TUNNEL_ID_2 = "20";
-    private static final String NOT_PRESENT_TUNNEL_ID = "2";
-    private static final ConnectPoint INGRESS_1 = ConnectPoint.deviceConnectPoint("of:0000000000000001/1");
-    private static final ConnectPoint INGRESS_2 = ConnectPoint.deviceConnectPoint("of:0000000000000002/1");
-    private static final ConnectPoint EGRESS_1 = ConnectPoint.deviceConnectPoint("of:0000000000000011/1");
-    private static final ConnectPoint EGRESS_2 = ConnectPoint.deviceConnectPoint("of:0000000000000012/1");
-    private static final VlanId INGRESS_INNER_TAG_1 = VlanId.vlanId("10");
-    private static final VlanId INGRESS_INNER_TAG_2 = VlanId.vlanId("100");
-    private static final VlanId INGRESS_OUTER_TAG_1 = VlanId.vlanId("20");
-    private static final VlanId INGRESS_OUTER_TAG_2 = VlanId.vlanId("200");
-    private static final VlanId EGRESS_INNER_TAG_1 = VlanId.vlanId("11");
-    private static final VlanId EGRESS_INNER_TAG_2 = VlanId.vlanId("110");
-    private static final VlanId EGRESS_OUTER_TAG_1 = VlanId.vlanId("21");
-    private static final VlanId EGRESS_OUTER_TAG_2 = VlanId.vlanId("210");
-    private static final String MODE_1 = "RAW";
-    private static final String MODE_2 = "TAGGED";
-    private static final boolean ALL_VLAN_1 = true;
-    private static final boolean ALL_VLAN_2 = false;
-    private static final VlanId SD_TAG_1 = VlanId.vlanId("40");
-    private static final VlanId SD_TAG_2 = VlanId.NONE;
-    private static final MplsLabel PW_LABEL_1 = MplsLabel.mplsLabel("255");
-    private static final MplsLabel PW_LABEL_2 = MplsLabel.mplsLabel("4095");
-
-    private PwaasConfig config;
-    private PwaasConfig invalidConfig;
-
-    @Before
-    public void setUp() throws Exception {
-        InputStream jsonStream = PwaasConfig.class
-                .getResourceAsStream("/pwaas.json");
-        InputStream invalidJsonStream = PwaasConfig.class
-                .getResourceAsStream("/pwaas-invalid.json");
-
-        String key = SegmentRoutingManager.APP_NAME;
-        ApplicationId subject = new TestApplicationId(key);
-        ObjectMapper mapper = new ObjectMapper();
-        JsonNode jsonNode = mapper.readTree(jsonStream);
-        JsonNode invalidJsonNode = mapper.readTree(invalidJsonStream);
-        ConfigApplyDelegate delegate = new MockDelegate();
-
-        config = new PwaasConfig();
-        config.init(subject, key, jsonNode, mapper, delegate);
-        invalidConfig = new PwaasConfig();
-        invalidConfig.init(subject, key, invalidJsonNode, mapper, delegate);
-    }
-
-    /**
-     * Tests config validity.
-     */
-    @Test
-    public void testIsValid() {
-        assertTrue(config.isValid());
-        assertFalse(invalidConfig.isValid());
-    }
-
-    /**
-     * Tests getPwIds.
-     */
-    @Test
-    public void testGetPwIds() {
-        Set<Long> pwIds = config.getPwIds();
-        assertThat(pwIds.size(), is(2));
-        assertTrue(pwIds.contains(Long.parseLong(TUNNEL_ID_1)));
-        assertTrue(pwIds.contains(Long.parseLong(TUNNEL_ID_2)));
-        assertFalse(pwIds.contains(Long.parseLong(NOT_PRESENT_TUNNEL_ID)));
-    }
-
-    /**
-     * Tests getPwDescription.
-     */
-    @Test
-    public void testGetPwDescription() {
-        DefaultL2TunnelDescription l2TunnelDescription = null;
-
-        DefaultL2Tunnel l2Tunnel = new DefaultL2Tunnel(
-            L2Mode.valueOf(MODE_1),
-            SD_TAG_1,
-            Long.parseLong(TUNNEL_ID_1),
-            PW_LABEL_1
-        );
-        DefaultL2TunnelPolicy l2TunnelPolicy = new DefaultL2TunnelPolicy(
-                Long.parseLong(TUNNEL_ID_1),
-                INGRESS_1,
-                INGRESS_INNER_TAG_1,
-                INGRESS_OUTER_TAG_1,
-                EGRESS_1,
-                EGRESS_INNER_TAG_1,
-                EGRESS_OUTER_TAG_1,
-                ALL_VLAN_1
-        );
-        l2TunnelDescription = config.getPwDescription(Long.parseLong(TUNNEL_ID_1));
-        assertThat(l2TunnelDescription.l2Tunnel().pwMode(), is(l2Tunnel.pwMode()));
-        assertThat(l2TunnelDescription.l2Tunnel().sdTag(), is(l2Tunnel.sdTag()));
-        assertThat(l2TunnelDescription.l2Tunnel().tunnelId(), is(l2Tunnel.tunnelId()));
-        assertThat(l2TunnelDescription.l2Tunnel().pwLabel(), is(l2Tunnel.pwLabel()));
-        assertThat(l2TunnelDescription.l2TunnelPolicy().tunnelId(), is(l2TunnelPolicy.tunnelId()));
-        assertThat(l2TunnelDescription.l2TunnelPolicy().cP1InnerTag(), is(l2TunnelPolicy.cP1InnerTag()));
-        assertThat(l2TunnelDescription.l2TunnelPolicy().cP1OuterTag(), is(l2TunnelPolicy.cP1OuterTag()));
-        assertThat(l2TunnelDescription.l2TunnelPolicy().cP2InnerTag(), is(l2TunnelPolicy.cP2InnerTag()));
-        assertThat(l2TunnelDescription.l2TunnelPolicy().cP2OuterTag(), is(l2TunnelPolicy.cP2OuterTag()));
-        assertThat(l2TunnelDescription.l2TunnelPolicy().cP1(), is(l2TunnelPolicy.cP1()));
-        assertThat(l2TunnelDescription.l2TunnelPolicy().cP2(), is(l2TunnelPolicy.cP2()));
-        assertThat(l2TunnelDescription.l2TunnelPolicy().isAllVlan(), is(l2TunnelPolicy.isAllVlan()));
-
-        l2Tunnel = new DefaultL2Tunnel(
-                L2Mode.valueOf(MODE_2),
-                SD_TAG_2,
-                Long.parseLong(TUNNEL_ID_2),
-                PW_LABEL_2
-        );
-        l2TunnelPolicy = new DefaultL2TunnelPolicy(
-                Long.parseLong(TUNNEL_ID_2),
-                INGRESS_2,
-                INGRESS_INNER_TAG_2,
-                INGRESS_OUTER_TAG_2,
-                EGRESS_2,
-                EGRESS_INNER_TAG_2,
-                EGRESS_OUTER_TAG_2,
-                ALL_VLAN_2
-        );
-        l2TunnelDescription = config.getPwDescription(Long.parseLong(TUNNEL_ID_2));
-        assertThat(l2TunnelDescription.l2Tunnel().pwMode(), is(l2Tunnel.pwMode()));
-        assertThat(l2TunnelDescription.l2Tunnel().sdTag(), is(l2Tunnel.sdTag()));
-        assertThat(l2TunnelDescription.l2Tunnel().tunnelId(), is(l2Tunnel.tunnelId()));
-        assertThat(l2TunnelDescription.l2Tunnel().pwLabel(), is(l2Tunnel.pwLabel()));
-        assertThat(l2TunnelDescription.l2TunnelPolicy().tunnelId(), is(l2TunnelPolicy.tunnelId()));
-        assertThat(l2TunnelDescription.l2TunnelPolicy().cP1InnerTag(), is(l2TunnelPolicy.cP1InnerTag()));
-        assertThat(l2TunnelDescription.l2TunnelPolicy().cP1OuterTag(), is(l2TunnelPolicy.cP1OuterTag()));
-        assertThat(l2TunnelDescription.l2TunnelPolicy().cP2OuterTag(), is(l2TunnelPolicy.cP2OuterTag()));
-        assertThat(l2TunnelDescription.l2TunnelPolicy().cP2OuterTag(), is(l2TunnelPolicy.cP2OuterTag()));
-        assertThat(l2TunnelDescription.l2TunnelPolicy().cP1(), is(l2TunnelPolicy.cP1()));
-        assertThat(l2TunnelDescription.l2TunnelPolicy().cP2(), is(l2TunnelPolicy.cP2()));
-        assertThat(l2TunnelDescription.l2TunnelPolicy().isAllVlan(), is(l2TunnelPolicy.isAllVlan()));
-
-    }
-
-    private class MockDelegate implements ConfigApplyDelegate {
-        @Override
-        public void onApply(Config config) {
-        }
-    }
-
-}
diff --git a/src/test/resources/pwaas-conflicting-vlan.json b/src/test/resources/pwaas-conflicting-vlan.json
new file mode 100644
index 0000000..fac162d
--- /dev/null
+++ b/src/test/resources/pwaas-conflicting-vlan.json
@@ -0,0 +1,24 @@
+{
+  "1": {
+    "cP1": "of:0000000000000001/1",
+    "cP2": "of:0000000000000002/2",
+    "cP1InnerTag": "10",
+    "cP1OuterTag": "20",
+    "cP2InnerTag": "11",
+    "cP2OuterTag": "21",
+    "mode": "RAW",
+    "sdTag": "",
+    "pwLabel": "255"
+  },
+  "20": {
+    "cP1": "of:0000000000000001/1",
+    "cP2": "of:0000000000000002/2",
+    "cP1InnerTag": "100",
+    "cP1OuterTag": "",
+    "cP2InnerTag": "21",
+    "cP2OuterTag": "",
+    "mode": "RAW",
+    "sdTag": "",
+    "pwLabel": "1255"
+  }
+}
diff --git a/src/test/resources/pwaas-invalid-mode.json b/src/test/resources/pwaas-invalid-mode.json
new file mode 100644
index 0000000..af0a9d1
--- /dev/null
+++ b/src/test/resources/pwaas-invalid-mode.json
@@ -0,0 +1,13 @@
+{
+  "1": {
+    "cP1": "of:0000000000000001/1",
+    "cP2": "of:0000000000000011/1",
+    "cP1InnerTag": "10",
+    "cP1OuterTag": "20",
+    "cP2InnerTag": "11",
+    "cP2OuterTag": "",
+    "mode": "UNDEFINED_MODED",
+    "sdTag": "40",
+    "pwLabel": "255"
+  }
+}
diff --git a/src/test/resources/pwaas-invalid-pwlabel.json b/src/test/resources/pwaas-invalid-pwlabel.json
new file mode 100644
index 0000000..66697f5
--- /dev/null
+++ b/src/test/resources/pwaas-invalid-pwlabel.json
@@ -0,0 +1,13 @@
+{
+  "1": {
+    "cP1": "of:0000000000000001/1",
+    "cP2": "of:0000000000000011/1",
+    "cP1InnerTag": "10",
+    "cP1OuterTag": "20",
+    "cP2InnerTag": "11",
+    "cP2OuterTag": "",
+    "mode": "1",
+    "sdTag": "40",
+    "pwLabel": "255.555551"
+  }
+}
diff --git a/src/test/resources/pwaas-invalid-vlan.json b/src/test/resources/pwaas-invalid-vlan.json
new file mode 100644
index 0000000..b556e06
--- /dev/null
+++ b/src/test/resources/pwaas-invalid-vlan.json
@@ -0,0 +1,24 @@
+{
+  "1": {
+    "cP1": "of:0000000000000001/1",
+    "cP2": "of:0000000000000002/2",
+    "cP1InnerTag": "10",
+    "cP1OuterTag": "20",
+    "cP2InnerTag": "11",
+    "cP2OuterTag": "21",
+    "mode": "RAW",
+    "sdTag": "",
+    "pwLabel": "255"
+  },
+  "20": {
+    "cP1": "of:0000000000000001/1",
+    "cP2": "of:0000000000000002/2",
+    "cP1InnerTag": "100",
+    "cP1OuterTag": "200.55",
+    "cP2InnerTag": "1100",
+    "cP2OuterTag": "210",
+    "mode": "RAW",
+    "sdTag": "",
+    "pwLabel": "1255"
+  }
+}
diff --git a/src/test/resources/pwaas-invalid.json b/src/test/resources/pwaas-invalid.json
deleted file mode 100644
index f282abe..0000000
--- a/src/test/resources/pwaas-invalid.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-  "1": {
-    "cP1": "of:0000000000000001/1",
-    "cP2": "of:0000000000000011/1",
-    "cP1InnerTag": "10",
-    "cP1OuterTag": "20",
-    "cP2InnerTag": "11",
-    "cP2OuterTag": "",
-    "mode": "1",
-    "allVlan": true,
-    "sdTag": "40",
-    "pwLabel": "255"
-  },
-  "20": {
-    "cP1": "of:0000000000000002/1",
-    "cP2": "of:0000000000000012/1",
-    "cP1InnerTag": "100",
-    "cP1OuterTag": "200",
-    "cP2InnerTag": "110",
-    "cP2OuterTag": "210",
-    "mode": "TAGGED",
-    "allVlan": false,
-    "sdTag": "-1",
-    "pwLabel": "4095"
-  }
-}
diff --git a/src/test/resources/pwaas.json b/src/test/resources/pwaas.json
index 902611e..2a58b4e 100644
--- a/src/test/resources/pwaas.json
+++ b/src/test/resources/pwaas.json
@@ -1,26 +1,24 @@
 {
   "1": {
     "cP1": "of:0000000000000001/1",
-    "cP2": "of:0000000000000011/1",
+    "cP2": "of:0000000000000002/1",
     "cP1InnerTag": "10",
     "cP1OuterTag": "20",
-    "cP2InnerTag": "11",
+    "cP2InnerTag": "10",
     "cP2OuterTag": "21",
     "mode": "RAW",
-    "allVlan": true,
-    "sdTag": "40",
+    "sdTag": "",
     "pwLabel": "255"
   },
   "20": {
-    "cP1": "of:0000000000000002/1",
-    "cP2": "of:0000000000000012/1",
+    "cP1": "of:0000000000000001/1",
+    "cP2": "of:0000000000000002/1",
     "cP1InnerTag": "100",
     "cP1OuterTag": "200",
-    "cP2InnerTag": "110",
+    "cP2InnerTag": "100",
     "cP2OuterTag": "210",
-    "mode": "TAGGED",
-    "allVlan": false,
-    "sdTag": "-1",
-    "pwLabel": "4095"
+    "mode": "RAW",
+    "sdTag": "",
+    "pwLabel": "1255"
   }
 }